Update maximo

feature/reliability_stat
Cizz22 5 months ago
parent 8ae93be047
commit 536c8fc889

@ -63,10 +63,18 @@ DATABASE_ENGINE_POOL_SIZE = config("DATABASE_ENGINE_POOL_SIZE", cast=int, defaul
DATABASE_ENGINE_MAX_OVERFLOW = config(
"DATABASE_ENGINE_MAX_OVERFLOW", cast=int, default=0
)
# Deal with DB disconnects
# https://docs.sqlalchemy.org/en/20/core/pooling.html#pool-disconnects
DATABASE_ENGINE_POOL_PING = config("DATABASE_ENGINE_POOL_PING", default=False)
COLLECTOR_HOSTNAME = config("COLLECTOR_HOSTNAME")
COLLECTOR_PORT = config("COLLECTOR_PORT", default="5432")
COLLECTOR_CREDENTIAL_USER = config("COLLECTOR_CREDENTIAL_USER")
COLLECTOR_CREDENTIAL_PASSWORD = config("COLLECTOR_CREDENTIAL_PASSWORD")
QUOTED_COLLECTOR_CREDENTIAL_PASSWORD = parse.quote(str(COLLECTOR_CREDENTIAL_PASSWORD))
COLLECTOR_NAME = config("COLLECTOR_NAME")
# Deal w
SQLALCHEMY_DATABASE_URI = f"postgresql+asyncpg://{_DATABASE_CREDENTIAL_USER}:{_QUOTED_DATABASE_PASSWORD}@{DATABASE_HOSTNAME}:{DATABASE_PORT}/{DATABASE_NAME}"
SQLALCHEMY_COLLECTOR_URI = f"postgresql+asyncpg://{COLLECTOR_CREDENTIAL_USER}:{QUOTED_COLLECTOR_CREDENTIAL_PASSWORD}@{COLLECTOR_HOSTNAME}:{COLLECTOR_PORT}/{COLLECTOR_NAME}"
TIMEZONE = "Asia/Jakarta"

@ -23,7 +23,7 @@ from starlette.routing import compile_path
from starlette.staticfiles import StaticFiles
from src.api import api_router
from src.database.core import async_session, engine
from src.database.core import async_session, engine, async_collector_session
from src.enums import ResponseStatus
from src.exceptions import handle_exception
from src.logging import configure_logging
@ -72,11 +72,16 @@ async def db_session_middleware(request: Request, call_next):
try:
session = async_scoped_session(async_session, scopefunc=get_request_id)
request.state.db = session()
collector_session = async_scoped_session(async_collector_session, scopefunc=get_request_id)
request.state.collector_db = collector_session()
response = await call_next(request)
except Exception as e:
raise e from None
finally:
await request.state.db.close()
await request.state.collector_db.close()
_request_id_ctx_var.reset(ctx_token)
return response

@ -0,0 +1,42 @@
from sqlalchemy import Column, BigInteger, Integer, Float, String, Text, DateTime
from src.database.core import CollectorBase
class WorkOrderData(CollectorBase):
__tablename__ = "wo_staging_3"
id = Column(BigInteger, primary_key=True, autoincrement=True)
assetnum = Column(Text, nullable=True)
description1 = Column(Text, nullable=True)
unit = Column(Integer, nullable=True)
location = Column(String(25), nullable=True)
system_tag = Column(String(25), nullable=True)
wonum = Column(String(10), nullable=True)
description2 = Column(Text, nullable=True)
wo_complt_comment = Column(Text, nullable=True)
worktype = Column(String(10), nullable=True)
jpnum = Column(String(10), nullable=True)
workgroup = Column(String(30), nullable=True)
mat_cost_max = Column(Float, nullable=True)
serv_cost_max = Column(Float, nullable=True)
total_cost_max = Column(Float, nullable=True)
wo_start = Column(DateTime, nullable=True)
wo_finish = Column(DateTime, nullable=True)
wo_start_olah = Column(DateTime, nullable=True)
wo_finish_olah = Column(DateTime, nullable=True)
reportdate = Column(DateTime, nullable=True)
reportdate_olah = Column(DateTime, nullable=True)
time_to_event = Column(Float, nullable=True)
actstart = Column(DateTime, nullable=True)
actfinish = Column(DateTime, nullable=True)
actstart_olah = Column(DateTime, nullable=True)
actfinish_olah = Column(DateTime, nullable=True)
act_repair_time = Column(Float, nullable=True)
jumlah_labor = Column(Integer, nullable=True)
need_downtime = Column(String(100), nullable=True)
validation_downtime = Column(Integer, nullable=True)
down_0_and_not_oh = Column(Integer, nullable=True)
downtime = Column(Integer, nullable=True)
failure_code = Column(String(10), nullable=True)
problem_code = Column(String(10), nullable=True)
act_finish_wo_start = Column(Float, nullable=True)

@ -1,154 +1,43 @@
from datetime import datetime, timedelta
from typing import Any, Dict
import httpx
from fastapi import HTTPException
from starlette.config import Config
from src.config import MAXIMO_API_KEY, MAXIMO_BASE_URL
class MaximoDataMapper:
"""
Helper class to map MAXIMO API response to our data structure.
Update these mappings according to actual MAXIMO API documentation.
"""
def __init__(self, maximo_data: Dict[Any, Any]):
self.data = maximo_data
def get_start_date(self) -> datetime:
"""
Extract start date from MAXIMO data.
TODO: Update this based on actual MAXIMO API response structure
Example: might be data['startDate'] or data['SCHEDSTART'] etc.
"""
# This is a placeholder - update with actual MAXIMO field name
start_date_str = self.data.get("scheduleStart")
if not start_date_str:
raise ValueError("Start date not found in MAXIMO data")
return datetime.fromisoformat(start_date_str)
def get_end_date(self) -> datetime:
"""
Extract end date from MAXIMO data.
TODO: Update this based on actual MAXIMO API response structure
"""
# This is a placeholder - update with actual MAXIMO field name
end_date_str = self.data.get("scheduleEnd")
if not end_date_str:
raise ValueError("End date not found in MAXIMO data")
return datetime.fromisoformat(end_date_str)
def get_maximo_id(self) -> str:
"""
Extract MAXIMO ID from response.
TODO: Update this based on actual MAXIMO API response structure
"""
# This is a placeholder - update with actual MAXIMO field name
maximo_id = self.data.get("workOrderId")
if not maximo_id:
raise ValueError("MAXIMO ID not found in response")
return str(maximo_id)
def get_status(self) -> str:
"""
Extract status from MAXIMO data.
TODO: Update this based on actual MAXIMO API response structure
"""
# This is a placeholder - update with actual MAXIMO status field and values
status = self.data.get("status", "").upper()
return status
def get_total_cost(self) -> float:
"""
Extract total cost from MAXIMO data.
TODO: Update this based on actual MAXIMO API response structure
"""
# This is a placeholder - update with actual MAXIMO field name
cost = self.data.get("totalCost", 0)
return float(cost)
def get_scope_name(self) -> str:
scope_name = self.data.get("location", "A")
return scope_name
class MaximoService:
def __init__(self):
# TODO: Update these settings based on actual MAXIMO API configuration
self.base_url = MAXIMO_BASE_URL
self.api_key = MAXIMO_API_KEY
async def get_recent_overhaul(self) -> dict:
"""
Fetch most recent overhaul from MAXIMO.
TODO: Update this method based on actual MAXIMO API endpoints and parameters
"""
current_date = datetime.now()
schedule_start = current_date + timedelta(days=30) # Starting in 30 days
schedule_end = schedule_start + timedelta(days=90) # 90 day overhaul period
return {
"scheduleStart": schedule_start.isoformat(),
"scheduleEnd": schedule_end.isoformat(),
"workOrderId": "WO-2024-12345",
"status": "PLAN", # Common Maximo statuses: SCHEDULED, INPRG, COMP, CLOSE
"totalCost": 10000000.00,
"description": "Annual Turbine Overhaul",
"priority": 1,
"location": "A",
"assetDetails": [
{
"assetnum": "ASSET001",
"description": "Gas Turbine",
"status": "OPERATING",
},
{
"assetnum": "ASSET002",
"description": "Steam Turbine",
"status": "OPERATING",
},
],
"workType": "OH", # OH for Overhaul
"createdBy": "MAXADMIN",
"createdDate": (current_date - timedelta(days=10)).isoformat(),
"lastModifiedBy": "MAXADMIN",
"lastModifiedDate": current_date.isoformat(),
}
async with httpx.AsyncClient() as client:
try:
# TODO: Update endpoint and parameters based on actual MAXIMO API
response = await client.get(
f"{self.base_url}/your-endpoint-here",
headers={
"Authorization": f"Bearer {self.api_key}",
# Add any other required headers
},
params={
# Update these parameters based on actual MAXIMO API
"orderBy": "-scheduleEnd", # Example parameter
"limit": 1,
},
)
if response.status_code != 200:
raise HTTPException(
status_code=response.status_code,
detail=f"MAXIMO API error: {response.text}",
)
data = response.json()
if not data:
raise HTTPException(
status_code=404, detail="No recent overhaul found"
)
# TODO: Update this based on actual MAXIMO response structure
return data[0] if isinstance(data, list) else data
except httpx.RequestError as e:
raise HTTPException(
status_code=503, detail=f"Failed to connect to MAXIMO: {str(e)}"
)
from datetime import datetime
from sqlalchemy import select, func, cast, Numeric
from sqlalchemy.orm import Session
from sqlalchemy import and_
from sqlalchemy.sql import not_
from src.maximo.model import WorkOrderData # Assuming this is where your model is
from src.database.core import CollectorDbSession
async def get_cm_cost_summary(collector_db: CollectorDbSession, last_oh_date:datetime, upcoming_oh_date:datetime):
query = select(
WorkOrderData.location,
(func.sum(WorkOrderData.total_cost_max).cast(Numeric) / func.count(WorkOrderData.wonum)).label('avg_cost')
).where(
and_(
# WorkOrderData.wo_start >= last_oh_date,
# WorkOrderData.wo_start <= upcoming_oh_date,
WorkOrderData.worktype.in_(['CM', 'EM', 'PROACTIVE']),
WorkOrderData.system_tag.in_(['HPB', 'AH', 'APC', 'SCR', 'CL', 'DM', 'CRH', 'ASH', 'BAD', 'DS', 'WTP',
'MT', 'SUP', 'DCS', 'FF', 'EG', 'AI', 'SPS', 'EVM', 'SCW', 'KLH', 'CH',
'TUR', 'LOT', 'HRH', 'ESP', 'CAE', 'GMC', 'BFT', 'LSH', 'CHB', 'BSS',
'LOS', 'LPB', 'SAC', 'CP', 'EHS', 'RO', 'GG', 'MS', 'CW', 'SO', 'ATT',
'AFG', 'EHB', 'RP', 'FO', 'PC', 'APE', 'AF', 'DMW', 'BRS', 'GEN', 'ABS',
'CHA', 'TR', 'H2', 'BDW', 'LOM', 'ACR', 'AL', 'FW', 'COND', 'CCCW', 'IA',
'GSS', 'BOL', 'SSB', 'CO', 'OA', 'CTH-UPD', 'AS', 'DP']),
WorkOrderData.reportdate.is_not(None),
WorkOrderData.actstart.is_not(None),
WorkOrderData.actfinish.is_not(None),
WorkOrderData.unit.in_([3, 0]),
WorkOrderData.reportdate >= datetime.strptime('2015-01-01', '%Y-%m-%d'),
not_(WorkOrderData.wonum.like('T%'))
)
).group_by(
WorkOrderData.location
).order_by(
func.count(WorkOrderData.wonum).desc()
)
result = await collector_db.execute(query)
data = result.all()
return {
data.location: data.avg_cost for data in data
}

@ -38,6 +38,31 @@ async def get_overhaul_schedules(*, db_session: DbSession):
def get_overhaul_system_components():
"""Get all overhaul system components with dummy data."""
powerplant_reliability = {
"Plant Control": 98,
"SPS": 98,
"Turbine": 98,
"Generator": 98,
"Condensate Water": 98,
"Feedwater System": 98,
"Cooling Water": 98,
"SCR": 98,
"Ash Handling": 98,
"Air Flue Gas": 98,
"Boiler": 98,
"SAC-IAC": 98,
"KLH": 98,
"CL": 98,
"Desalination": 98,
"FGD": 98,
"CHS": 98,
"SSB": 98,
"WTP": 98,
}
return powerplant_reliability
return {
"HPT": {
"efficiency": "92%",

@ -3,6 +3,7 @@ from uuid import UUID
from fastapi import APIRouter, HTTPException, Query, status
from src.database.core import CollectorDbSession
from src.database.service import (CommonParameters, DbSession,
search_filter_sort_paginate)
from src.models import StandardResponse
@ -20,6 +21,7 @@ router = APIRouter()
async def get_scope_equipments(
common: CommonParameters,
overhaul_session: str,
collector_db: CollectorDbSession,
location_tag: Optional[str] = Query(None),
scope_name: Optional[str] = Query(None),
):
@ -30,6 +32,7 @@ async def get_scope_equipments(
location_tag=location_tag,
scope_name=scope_name,
overhaul_session_id=overhaul_session,
collector_db=collector_db,
)

@ -12,7 +12,7 @@ from src.database.core import DbSession
from src.database.service import CommonParameters, search_filter_sort_paginate
from src.overhaul_activity.utils import get_material_cost, get_service_cost
from src.overhaul_scope.model import OverhaulScope
from src.overhaul_scope.service import get as get_session
from src.overhaul_scope.service import get as get_session, get_prev_oh
from src.standard_scope.model import MasterEquipment, StandardScope
from src.standard_scope.service import get_by_oh_session_id
from src.workscope_group.model import MasterActivity
@ -26,6 +26,8 @@ from .schema import (OverhaulActivityCreate, OverhaulActivityRead,
OverhaulActivityUpdate)
import json
from src.database.core import CollectorDbSession
from src.maximo.service import get_cm_cost_summary
async def get(
*, db_session: DbSession, assetnum: str, overhaul_session_id: Optional[UUID] = None
@ -55,7 +57,8 @@ async def get_all(
overhaul_session_id: UUID,
location_tag: Optional[str] = None,
scope_name: Optional[str] = None,
all: bool = False
all: bool = False,
collector_db: CollectorDbSession
):
# query = (
# Select(OverhaulActivity)
@ -84,6 +87,7 @@ async def get_all(
# )
overhaul = await get_overhaul(db_session=common['db_session'], overhaul_session_id=overhaul_session_id)
prev_oh_scope = await get_prev_oh(db_session=common['db_session'], overhaul_session=overhaul)
query = (
Select(StandardScope)
@ -102,25 +106,25 @@ async def get_all(
num_equipments = len((await common['db_session'].execute(query)).scalars().all())
data_cost = get_cost_per_failute()
material_cost = await get_cm_cost_summary(collector_db=collector_db, last_oh_date=prev_oh_scope.end_date, upcoming_oh_date=overhaul.start_date)
service_cost = get_service_cost(scope=overhaul.maintenance_type.name, total_equipment=num_equipments)
equipments = await search_filter_sort_paginate(model=query, **common)
data = equipments['items']
results = []
for equipment in data:
if not equipment.master_equipment:
continue
cost = next((item for item in data_cost if item['location'] == equipment.location_tag), None)
cost = material_cost.get(equipment.location_tag, 0)
res = OverhaulActivityRead(
id=equipment.id,
material_cost=cost.get('avg_cost', 0) if cost else 0,
service_cost=200000000,
material_cost=float(cost),
service_cost=float(service_cost),
location_tag=equipment.location_tag,
equipment_name=equipment.master_equipment.name if equipment.master_equipment else None,
oh_scope=overhaul.maintenance_type.name,
@ -132,8 +136,9 @@ async def get_all(
return equipments
async def get_standard_scope_by_session_id(*, db_session: DbSession, overhaul_session_id: UUID):
overhaul = await get_overhaul(db_session=db_session, overhaul_session_id=overhaul_session_id)
async def get_standard_scope_by_session_id(*, db_session: DbSession, overhaul_session_id: UUID, collector_db: CollectorDbSession):
overhaul = await get_session(db_session=db_session, overhaul_session_id=calculation.overhaul_session_id)
prev_oh_scope = await get_prev_oh(db_session=db_session, overhaul_session=overhaul)
query = (
Select(StandardScope)
@ -153,16 +158,17 @@ async def get_standard_scope_by_session_id(*, db_session: DbSession, overhaul_se
data = await db_session.execute(query)
eqs = data.scalars().all()
material_cost = get_material_cost("B", len(eqs))
results = []
data_cost = get_cost_per_failute()
material_cost = await get_cm_cost_summary(collector_db=collector_db, last_oh_date=prev_oh_scope.end_date, upcoming_oh_date=overhaul.start_date)
service_cost = get_service_cost(scope=overhaul.maintenance_type.name, total_equipment=len(eqs))
for equipment in eqs:
cost = next((item for item in data_cost if item['location'] == equipment.location_tag), None)
cost = material_cost.get(equipment.location_tag,0)
res = OverhaulActivityRead(
id=equipment.id,
material_cost=cost.get('avg_cost', 0) if cost else 0,
service_cost=200000000,
material_cost=float(cost),
service_cost=float(service_cost),
location_tag=equipment.location_tag,
equipment_name=equipment.master_equipment.name if equipment.master_equipment else None,
oh_scope=overhaul.maintenance_type.name,

Loading…
Cancel
Save