You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
be-optimumoh/patch_service.py

264 lines
13 KiB
Python

import re
with open('src/calculation_time_constrains/service.py', 'r') as f:
content = f.read()
# Replacement 1: run_simulation_with_spareparts
replacement_1 = """ finally:
await optimum_oh_model._close_session()
# Re-fetch calculation_data with equipment_results to ensure they are loaded
from sqlalchemy import select
from sqlalchemy.orm import selectinload
calculation_query = await db_session.execute(
select(CalculationData)
.options(selectinload(CalculationData.equipment_results), selectinload(CalculationData.parameter))
.where(CalculationData.id == calculation.id)
)
scope_calculation = calculation_query.scalar_one_or_none()
data_num = scope_calculation.max_interval
all_equipment = scope_calculation.equipment_results
included_equipment = [eq for eq in all_equipment if eq.is_included]
calculation_results = []
fleet_statistics = {
'total_equipment': len(all_equipment),
'included_equipment': len(included_equipment),
'excluded_equipment': len(all_equipment) - len(included_equipment),
'equipment_with_sparepart_constraints': 0,
'total_procurement_items': 0,
'critical_procurement_items': 0,
'total_spareparts': 745
}
avg_failure_cost = sum((eq.material_cost or 0) + (3 * 111000 * 3) for eq in all_equipment) / len(all_equipment) if all_equipment else 0
rbd_marginal_fails = [0] * data_num
try:
if scope_calculation.rbd_simulation_id:
from src.config import RBD_SERVICE_API
import httpx
plant_result_url = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{scope_calculation.rbd_simulation_id}/plant"
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.get(
plant_result_url,
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
)
if response.status_code == 200:
plant_data = response.json().get("data", {})
timestamp_outs = plant_data.get("timestamp_outs", [])
if timestamp_outs:
from src.calculation_time_constrains.utils import create_time_series_data, calculate_failures_per_month
hourly_data = create_time_series_data(timestamp_outs, max_hours=data_num * 720)
cumulative_rbd_fails = calculate_failures_per_month(hourly_data)
rbd_fails_map = {m['month']: m['failures'] for m in cumulative_rbd_fails}
prev_fail = 0
for m in range(1, data_num + 1):
curr_fail = rbd_fails_map.get(m, prev_fail)
rbd_marginal_fails[m-1] = curr_fail - prev_fail
prev_fail = curr_fail
except Exception as e:
import logging
logger = logging.getLogger(__name__)
logger.warning(f"Failed to fetch plant simulation: {e}")
cumulative_plant_failures = 0
import numpy as np
from .schema import CalculationResultsRead
for month_index in range(data_num):
historical_marginal_fail = 0
for eq in all_equipment:
if eq.is_actual and month_index < len(eq.is_actual) and eq.is_actual[month_index]:
curr_fail = eq.daily_failures[month_index] if month_index < len(eq.daily_failures) else 0
prev_fail = eq.daily_failures[month_index-1] if month_index > 0 and (month_index - 1) < len(eq.daily_failures) else 0
historical_marginal_fail += max(0, curr_fail - prev_fail)
marginal_fail = rbd_marginal_fails[month_index] + historical_marginal_fail
cumulative_plant_failures += marginal_fail
month_result = {
"overhaul_cost": 0.0,
"corrective_cost": 0.0,
"procurement_cost": 0.0,
"num_failures": cumulative_plant_failures,
"day": month_index + 1,
"month": month_index + 1,
"procurement_details": {},
"sparepart_summary": {
"total_procurement_cost": 0.0,
"equipment_requiring_procurement": 0,
"critical_shortages": 0,
"existing_orders_value": 0.0,
"new_orders_required": 0,
"urgent_procurements": 0
}
}
equipment_requiring_procurement = 0
total_existing_orders_value = 0.0
total_new_orders_value = 0.0
critical_shortages = 0
urgent_procurements = 0
for eq in all_equipment:
if month_index >= len(eq.procurement_details):
continue
procurement_detail = eq.procurement_details[month_index]
if (procurement_detail and isinstance(procurement_detail, dict) and procurement_detail.get("procurement_needed")):
equipment_requiring_procurement += 1
pr_po_summary = procurement_detail.get("pr_po_summary", {})
existing_orders_value = pr_po_summary.get("total_existing_value", 0)
total_existing_orders_value += existing_orders_value
new_orders_value = pr_po_summary.get("total_new_orders_value", 0)
total_new_orders_value += new_orders_value
critical_missing = procurement_detail.get("critical_missing_parts", 0)
if critical_missing > 0:
critical_shortages += 1
recommendations = procurement_detail.get("recommendations", [])
urgent_items = [r for r in recommendations if r.get("priority") == "CRITICAL"]
if urgent_items:
urgent_procurements += 1
is_included_eq = False if eq.is_initial else eq.is_included
month_result["procurement_details"][eq.location_tag] = {
"is_included": is_included_eq,
"location_tag": eq.location_tag,
"details": procurement_detail.get("procurement_needed", []),
"detailed_message": procurement_detail.get("detailed_message", ""),
"pr_po_summary": pr_po_summary,
"recommendations": recommendations,
"sparepart_available": procurement_detail.get("sparepart_available", True),
"can_proceed": procurement_detail.get("can_proceed_with_delays", True),
"critical_missing_parts": critical_missing,
"existing_orders_value": existing_orders_value,
"new_orders_value": new_orders_value
}
if eq.is_included:
if (month_index < len(eq.overhaul_costs) and month_index < len(eq.procurement_costs)):
month_result["overhaul_cost"] += float(eq.overhaul_costs[month_index])
month_result["procurement_cost"] += float(eq.procurement_costs[month_index])
month_result["corrective_cost"] = (cumulative_plant_failures * avg_failure_cost) / (month_index + 1)
month_result["sparepart_summary"].update({
"total_procurement_cost": month_result["procurement_cost"],
"equipment_requiring_procurement": equipment_requiring_procurement,
"critical_shortages": critical_shortages,
"existing_orders_value": total_existing_orders_value,
"new_orders_required": len([eq for eq in all_equipment if month_index < len(eq.procurement_details) and eq.procurement_details[month_index] and eq.procurement_details[month_index].get("procurement_needed")]),
"urgent_procurements": urgent_procurements
})
month_result["total_cost"] = month_result["corrective_cost"] + month_result["overhaul_cost"] + month_result["procurement_cost"]
calculation_results.append(month_result)
optimum_day = np.argmin([month["total_cost"] for month in calculation_results])
scope_calculation.optimum_oh_day = int(optimum_day)
fleet_statistics['equipment_with_sparepart_constraints'] = len([
eq for eq in all_equipment if any(detail and detail.get("procurement_needed") for detail in eq.procurement_details if detail)
])
fleet_statistics['total_procurement_items'] = sum([
len(detail.get("procurement_needed", [])) for eq in all_equipment for detail in eq.procurement_details if detail and isinstance(detail, dict)
])
analysis_metadata = {
"planned_month": (scope.start_date.year - prev_oh_scope.end_date.year) * 12 + (scope.start_date.month - prev_oh_scope.end_date.month) if prev_oh_scope and scope else 0,
"total_fleet_procurement_cost": sum([eq.procurement_costs[int(scope_calculation.optimum_oh_day)] for eq in all_equipment if eq.procurement_costs]),
"last_oh_date": prev_oh_scope.end_date.isoformat() if prev_oh_scope else None,
"next_oh_date": scope.start_date.isoformat() if scope else None,
"optimal_stat": None
}
calc_results_read = [CalculationResultsRead(**r) for r in calculation_results]
optimal_analysis = _analyze_optimal_timing(
calc_results_read, scope_calculation.optimum_oh_day, prev_oh_scope, scope
)
scope_calculation.plant_results = calculation_results
scope_calculation.fleet_statistics = fleet_statistics
scope_calculation.analysis_metadata = analysis_metadata
scope_calculation.optimum_analysis = optimal_analysis
await db_session.commit()
return {
"id": calculation.id,
"optimum": optimal_analysis
}"""
pattern_1 = re.compile(r" finally:\n await optimum_oh_model\._close_session\(\).*?return \{\n \"id\": calculation_data\.id,\n \"optimum\": stats\n \}", re.DOTALL)
if pattern_1.search(content):
content = pattern_1.sub(replacement_1, content)
else:
print("Could not find Replacement 1 target")
# Replacement 2: get_calculation_result
replacement_2 = """async def get_calculation_result(db_session: DbSession, calculation_id: str, token, include_risk_cost):
\"\"\"
Get calculation results from DB, returning pre-calculated plant and equipment results.
\"\"\"
try:
# Get calculation data with equipment results
calculation_query = await db_session.execute(
select(CalculationData)
.options(selectinload(CalculationData.equipment_results))
.where(CalculationData.id == calculation_id)
)
scope_calculation = calculation_query.scalar_one_or_none()
if not scope_calculation:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Calculation with id {calculation_id} does not exist.",
)
scope_overhaul = await get_scope(
db_session=db_session,
overhaul_session_id=scope_calculation.overhaul_session_id
)
if not scope_overhaul:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Overhaul scope for session {scope_calculation.overhaul_session_id} does not exist.",
)
# Parse pre-calculated plant results
plant_results_raw = scope_calculation.plant_results or []
calculation_results = [CalculationResultsRead(**r) for r in plant_results_raw]
# Return comprehensive result
return CalculationTimeConstrainsRead(
id=scope_calculation.id,
reference=scope_calculation.overhaul_session_id,
scope=scope_overhaul.maintenance_type.name,
results=calculation_results,
optimum_oh=scope_calculation.optimum_oh_day,
optimum_oh_month=scope_calculation.optimum_oh_day + 1 if scope_calculation.optimum_oh_day is not None else None,
equipment_results=scope_calculation.equipment_results,
fleet_statistics=scope_calculation.fleet_statistics or {},
optimal_analysis=scope_calculation.optimum_analysis or {},
analysis_metadata=scope_calculation.analysis_metadata or {}
)
except HTTPException:
raise
except Exception as e:
import logging
logger = logging.getLogger(__name__)
logger.error(f"Error in get_calculation_result for calculation_id {calculation_id}: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Internal error processing calculation results: {str(e)}",
)"""
pattern_2 = re.compile(r"async def get_calculation_result\(db_session: DbSession, calculation_id: str, token, include_risk_cost\):.*?raise HTTPException\(\n status_code=status\.HTTP_500_INTERNAL_SERVER_ERROR,\n detail=f\"Internal error processing calculation results: \{str\(e\)\}\",\n \)", re.DOTALL)
if pattern_2.search(content):
content = pattern_2.sub(replacement_2, content)
else:
print("Could not find Replacement 2 target")
with open('src/calculation_time_constrains/service.py', 'w') as f:
f.write(content)
print("Patch applied.")