chore: clear all unused imports and variables

oh_security
Cizz22 4 weeks ago
parent 95ae35c0fe
commit 0ecae96664

@ -0,0 +1 @@
tests/

1
.gitignore vendored

@ -1,3 +1,4 @@
env/
__pycache__/
venv/
tests/

@ -12,32 +12,18 @@ from src.calculation_target_reliability.router import \
router as calculation_target_reliability
from src.calculation_time_constrains.router import \
router as calculation_time_constrains_router, get_calculation
# from src.job.router import router as job_router
from src.overhaul.router import router as overhaul_router
from src.standard_scope.router import router as standard_scope_router
from src.overhaul_activity.router import router as overhaul_activity_router
from src.workscope_group.router import router as workscope_group_router
from src.equipment_workscope_group.router import router as equipment_workscope_group_router
# from src.overhaul_job.router import router as job_overhaul_router
# from src.overhaul_scope.router import router as scope_router
# from src.scope_equipment.router import router as scope_equipment_router
# from src.scope_equipment_job.router import router as scope_equipment_job_router
# from src.overhaul_schedule.router import router as overhaul_schedule_router
from src.overhaul_gantt.router import router as gantt_router
from src.sparepart.router import router as sparepart_router
from src.equipment_sparepart.router import router as equipment_sparepart_router
# from src.overhaul_scope.router import router as scope_router
# from src.scope_equipment.router import router as scope_equipment_router
# from src.overhaul.router import router as overhaul_router
# from src.overhaul_history.router import router as overhaul_history_router
# from src.overhaul_activity.router import router as scope_equipment_activity_router
from src.overhaul_scope.router import router as ovehaul_schedule_router
# from src.scope_equipment_part.router import router as scope_equipment_part_router
# from src.calculation_target_reliability.router import router as calculation_target_reliability
#
# from src.master_activity.router import router as activity_router
class ErrorMessage(BaseModel):
@ -74,12 +60,10 @@ authenticated_api_router.include_router(
)
# authenticated_api_router.include_router(job_router, prefix="/jobs", tags=["job"])
# # # Overhaul session data
# authenticated_api_router.include_router(
# scope_router, prefix="/overhaul-session", tags=["overhaul-session"]
# )
authenticated_api_router.include_router(
standard_scope_router, prefix="/scope-equipments", tags=["scope_equipment"]
@ -110,19 +94,12 @@ authenticated_api_router.include_router(
)
# authenticated_api_router.include_router(
# scope_equipment_job_router,
# prefix="/scope-equipment-jobs",
# tags=["scope_equipment", "job"],
# )
# authenticated_api_router.include_router(
# overhaul_schedule_router,
# prefix="/overhaul-schedules",
# tags=["overhaul_schedule"],
# )
# authenticated_api_router.include_router(
# job_overhaul_router, prefix="/overhaul-jobs", tags=["job", "overhaul"]
# )
authenticated_api_router.include_router(
gantt_router, prefix="/overhaul-gantt", tags=["gantt"]
@ -130,19 +107,15 @@ authenticated_api_router.include_router(
# authenticated_api_router.include_router(
# overhaul_history_router, prefix="/overhaul-history", tags=["overhaul_history"]
# )
# authenticated_api_router.include_router(
# scope_equipment_activity_router, prefix="/equipment-activities", tags=["scope_equipment_activities"]
# )
# authenticated_api_router.include_router(
# activity_router, prefix="/activities", tags=["activities"]
# )
# authenticated_api_router.include_router(
# scope_equipment_part_router, prefix="/equipment-parts", tags=["scope_equipment_parts"]
# )
authenticated_api_router.include_router(
ovehaul_schedule_router, prefix="/overhaul-schedules", tags=["overhaul_schedules"]

@ -1,12 +1,11 @@
# app/auth/auth_bearer.py
import json
from typing import Annotated, Optional
from typing import Annotated
import requests
from fastapi import Depends, HTTPException, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy.sql.expression import false
import src.config as config

@ -1,6 +1,6 @@
from typing import Annotated, Dict, List, Optional
from typing import Annotated, Dict
from fastapi import APIRouter, HTTPException, status
from fastapi import APIRouter
from fastapi.params import Query
from src.auth.service import Token

@ -1,10 +1,8 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from uuid import UUID
from typing import Any, Dict, List
from pydantic import BaseModel, Field
from src.models import DefultBase, Pagination
from src.models import DefultBase
class OverhaulBase(BaseModel):
@ -35,40 +33,19 @@ class OverhaulRead(OverhaulBase):
class BudgetContraintQuery(DefultBase):
cost_threshold: float = Field(100, ge=0, le=1000000000000000)
# {
# "overview": {
# "totalEquipment": 30,
# "nextSchedule": {
# "date": "2025-01-12",
# "Overhaul": "B",
# "equipmentCount": 30
# }
# },
# "criticalParts": [
# "Boiler feed pump",
# "Boiler reheater system",
# "Drum Level (Right) Root Valve A",
# "BCP A Discharge Valve",
# "BFPT A EXH Press HI Root VLV"
# ],
# "schedules": [
# {
# "date": "2025-01-12",
# "Overhaul": "B",
# "status": "upcoming"
# }
# // ... other scheduled overhauls
# ],
# "systemComponents": {
# "boiler": {
# "status": "operational",
# "lastOverhaul": "2024-06-15"
# },
# "turbine": {
# "hpt": { "status": "operational" },
# "ipt": { "status": "operational" },
# "lpt": { "status": "operational" }
# }
# // ... other major components
# }
# }

@ -1,17 +1,11 @@
from collections import defaultdict
import math
import random
from typing import Optional, List, Dict, Tuple
from uuid import UUID
from sqlalchemy import Delete, Select
from src.auth.service import CurrentUser
from src.contribution_util import calculate_contribution_accurate
from src.database.core import CollectorDbSession, DbSession
# from src.scope_equipment.model import ScopeEquipment
# from src.scope_equipment.service import get_by_scope_name
from src.overhaul_activity.service import get_all_by_session_id, get_standard_scope_by_session_id
from src.overhaul_activity.service import get_standard_scope_by_session_id
# async def get_all_budget_constrains(
# *, db_session: DbSession, session_id: str, cost_threshold: float = 100000000
@ -24,7 +18,6 @@ from typing import List, Dict, Tuple
from src.database.core import CollectorDbSession, DbSession
from src.overhaul_activity.service import get_standard_scope_by_session_id
from src.contribution_util import calculate_contribution_accurate
async def get_all_budget_constrains(
@ -84,7 +77,7 @@ async def get_all_budget_constrains(
# Calculate efficiency and composite score
for item in result:
cost = item["total_cost"] or 1.0
efficiency = item["contribution_norm"] / cost
item["contribution_norm"] / cost
item["priority_score"] = item["contribution_norm"]
# Choose method

@ -1,17 +1,14 @@
import asyncio
from typing import Dict, List, Optional
from typing_extensions import Annotated
from temporalio.client import Client
from fastapi import APIRouter, HTTPException, status
from fastapi.params import Query
from src.calculation_target_reliability.utils import wait_for_workflow
from src.config import TEMPORAL_URL, TR_RBD_ID
from src.database.core import DbSession, CollectorDbSession
from src.auth.service import Token
from src.models import StandardResponse
from .service import run_rbd_simulation, get_simulation_results, identify_worst_eaf_contributors
from .service import get_simulation_results, identify_worst_eaf_contributors
from .schema import OptimizationResult, TargetReliabiltiyQuery
from src.auth.access_control import required_permission, OHPermission
from src.overhaul_scope.model import OverhaulScope
@ -22,18 +19,10 @@ router = APIRouter()
# @router.get("", response_model=StandardResponse[List[Dict]])
# async def get_target_reliability(
# db_session: DbSession,
# scope_name: Optional[str] = Query(None),
# eaf_threshold: float = Query(100),
# ):
# """Get all scope pagination."""
# results = await get_all_target_reliability(
# db_session=db_session, scope_name=scope_name, eaf_threshold=eaf_threshold
# )
# return StandardResponse(
# data=results,
# message="Data retrieved successfully",
# )
@router.get("", response_model=StandardResponse[OptimizationResult], dependencies=[Depends(required_permission(OHPermission.READ, OverhaulScope))])
@ -42,11 +31,6 @@ async def get_target_reliability(
token: Token,
collector_db: CollectorDbSession,
params: Annotated[TargetReliabiltiyQuery, Query()],
# oh_session_id: Optional[str] = Query(None),
# eaf_input: float = Query(99.8),
# duration: int = Query(17520),
# simulation_id: Optional[str] = Query(None),
# cut_hours = Query(0)
):
"""Get all scope pagination."""
oh_session_id = params.oh_session_id
@ -61,12 +45,6 @@ async def get_target_reliability(
detail="oh_session_id is required",
)
# results = await get_eaf_timeline(
# db_session=db_session,
# oh_session_id=oh_session_id,
# eaf_input=eaf_input,
# oh_duration=duration
# )
if duration != 17520:
if not simulation_id:

@ -1,10 +1,8 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from uuid import UUID
from pydantic import BaseModel, Field
from src.models import DefultBase, Pagination
from src.models import DefultBase
class OverhaulBase(BaseModel):
@ -71,40 +69,19 @@ class TargetReliabiltiyQuery(DefultBase):
simulation_id: Optional[str] = Field(None)
cut_hours:int = Field(0)
# {
# "overview": {
# "totalEquipment": 30,
# "nextSchedule": {
# "date": "2025-01-12",
# "Overhaul": "B",
# "equipmentCount": 30
# }
# },
# "criticalParts": [
# "Boiler feed pump",
# "Boiler reheater system",
# "Drum Level (Right) Root Valve A",
# "BCP A Discharge Valve",
# "BFPT A EXH Press HI Root VLV"
# ],
# "schedules": [
# {
# "date": "2025-01-12",
# "Overhaul": "B",
# "status": "upcoming"
# }
# // ... other scheduled overhauls
# ],
# "systemComponents": {
# "boiler": {
# "status": "operational",
# "lastOverhaul": "2024-06-15"
# },
# "turbine": {
# "hpt": { "status": "operational" },
# "ipt": { "status": "operational" },
# "lpt": { "status": "operational" }
# }
# // ... other major components
# }
# }

@ -1,20 +1,9 @@
import math
from typing import Optional, List
from dataclasses import dataclass
from sqlalchemy import Delete, Select
import httpx
from src.auth.service import CurrentUser
from src.config import RBD_SERVICE_API
from src.contribution_util import calculate_contribution, calculate_contribution_accurate
from src.database.core import DbSession, CollectorDbSession
from datetime import datetime, timedelta
import random
from .utils import generate_down_periods
from src.overhaul_scope.service import get as get_overhaul
from bisect import bisect_left
from collections import defaultdict
import asyncio
from .schema import AssetWeight,MaintenanceScenario,OptimizationResult
from .schema import AssetWeight,OptimizationResult
from src.overhaul_activity.service import get_standard_scope_by_session_id
@ -53,28 +42,23 @@ async def get_simulation_results(*, simulation_id: str, token: str):
}
calc_result_url = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}?nodetype=RegularNode"
# plot_result_url = f"{RBD_SERVICE_API}/aeros/simulation/result/plot/{simulation_id}?nodetype=RegularNode"
calc_plant_result = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}/plant"
async with httpx.AsyncClient(timeout=300.0) as client:
calc_task = client.get(calc_result_url, headers=headers)
# plot_task = client.get(plot_result_url, headers=headers)
plant_task = client.get(calc_plant_result, headers=headers)
# Run all three requests concurrently
calc_response, plant_response = await asyncio.gather(calc_task, plant_task)
calc_response.raise_for_status()
# plot_response.raise_for_status()
plant_response.raise_for_status()
calc_data = calc_response.json()["data"]
# plot_data = plot_response.json()["data"]
plant_data = plant_response.json()["data"]
return {
"calc_result": calc_data,
# "plot_result": plot_data,
"plant_result": plant_data
}
@ -95,7 +79,6 @@ def calculate_asset_eaf_contributions(plant_result, eq_results, standard_scope,
MIN_IMPROVEMENT_PERCENT = 0.0001
min_improvement_fraction = MIN_IMPROVEMENT_PERCENT / 100.0
EPSILON = 0.001 # 1 ms or a fraction of an hour for comparison tolerance
results = []
weighted_assets = []
@ -223,7 +206,7 @@ async def identify_worst_eaf_contributors(
# Target feasibility check
warning_message = None
if target_eaf > max_eaf_possible:
impossible_gap = target_eaf - max_eaf_possible
target_eaf - max_eaf_possible
required_scheduled_hours = total_hours * (1 - target_eaf / 100)
required_reduction = reduced_outage - required_scheduled_hours

@ -1,10 +1,10 @@
import asyncio
from datetime import datetime, timedelta
import random
from typing import List, Optional
from typing import Optional
from temporalio.client import Client
from src.config import TEMPORAL_URL, TR_RBD_ID
from src.config import TEMPORAL_URL
def generate_down_periods(start_date: datetime, end_date: datetime,
num_periods: Optional[int] = None, min_duration: int = 3,

@ -106,8 +106,6 @@ class OptimumCostModel:
failure_cost = (failure_prob * failure_replacement_cost) / expected_operating_time
preventive_cost = (reliability * preventive_cost) / expected_operating_time
else:
# failure_cost = failure_prob * self.IDRu
# preventive_cost = reliability * self.IDRp
continue
@ -167,13 +165,13 @@ class OptimumCostModel:
Calculate optimization for entire fleet of equipment
"""
max_interval = self._get_months_between(start_date, end_date)
preventive_cost_per_equipment = preventive_cost / len(equipments)
preventive_cost / len(equipments)
fleet_results = []
total_corrective_costs = np.zeros(max_interval)
total_preventive_costs = np.zeros(max_interval)
total_procurement_costs = np.zeros(max_interval)
total_failures = np.zeros(max_interval)
np.zeros(max_interval)
for equipment in equipments:
# Get reliability data

@ -1,12 +1,8 @@
from typing import Optional
from uuid import UUID
import numpy as np
from fastapi import HTTPException, status
from sqlalchemy import Select, func, select
from sqlalchemy.orm import joinedload
from sqlalchemy import func, select
from src.auth.service import Token
from src.config import TC_RBD_ID
from src.database.core import DbSession
from src.overhaul_scope.service import get_all
@ -15,13 +11,9 @@ from src.workorder.model import MasterWorkOrder
from .schema import (CalculationTimeConstrainsParametersCreate,
CalculationTimeConstrainsParametersRead,
CalculationTimeConstrainsParametersRetrive,
CalculationTimeConstrainsRead)
from .service import (create_calculation_result_service, create_param_and_data,
get_avg_cost_by_asset,
get_calculation_by_reference_and_parameter,
get_calculation_data_by_id, get_calculation_result,
run_simulation_with_spareparts)
CalculationTimeConstrainsParametersRetrive)
from .service import (create_param_and_data,
get_calculation_data_by_id, run_simulation_with_spareparts)
from src.database.core import CollectorDbSession
@ -69,12 +61,6 @@ async def get_create_calculation_parameters(
costPerFailure=costFailurePerScope,
availableScopes=avaiableScopes.values(),
recommendedScope="A",
# historicalData={
# "averageOverhaulCost": 10000000,
# "lastCalculation": {
# "id": "calc_122",
# "date": "2024-10-15",
# "scope": "B",
# },
# },
)
@ -97,9 +83,6 @@ async def create_calculation(
rbd_simulation_id = simulation_id or TC_RBD_ID
# results = await create_calculation_result_service(
# db_session=db_session, calculation=calculation_data, token=token
# )
results = await run_simulation_with_spareparts(
db_session=db_session, calculation=calculation_data, token=token, collector_db_session=collector_db_session, simulation_id=rbd_simulation_id
)
@ -127,9 +110,3 @@ async def get_or_create_scope_equipment_calculation(
# Check if calculation already exist
# return CalculationTimeConstrainsRead(
# id=scope_calculation.id,
# reference=scope_calculation.overhaul_session_id,
# results=scope_calculation.results,
# optimum_oh=scope_calculation.optimum_oh_day,
# equipment_results=scope_calculation.equipment_results,
# )

@ -1,12 +1,12 @@
from enum import Enum
from typing import List, Optional, Union
from typing import Optional
from sqlalchemy import (JSON, UUID, Boolean, Column, Float, ForeignKey,
Integer, Numeric, String)
Integer, String)
from sqlalchemy.orm import relationship
from src.database.core import Base, DbSession
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin, UUIDMixin
from src.models import DefaultMixin, IdentityMixin
class OverhaulReferenceType(str, Enum):
@ -32,29 +32,12 @@ class CalculationParam(Base, DefaultMixin, IdentityMixin):
# overhaul_cost: float,
# created_by: str,
# # list of {"reference_type": OverhaulReferenceType, "reference_id": str}
# references: List[dict]
# ):
# # Create parameter
# param = cls(
# avg_failure_cost=avg_failure_cost,
# overhaul_cost=overhaul_cost,
# created_by=created_by
# )
# db.add(param)
# await db.flush() # Flush to get the param.id
# # Create reference links
# for ref in references:
# reference_link = ReferenceLink(
# parameter_id=param.id,
# overhaul_reference_type=ref["reference_type"],
# reference_id=ref["reference_id"]
# )
# db.add(reference_link)
# await db.commit()
# await db.refresh(param)
# return param
class CalculationData(Base, DefaultMixin, IdentityMixin):

@ -2,9 +2,7 @@ from typing import Annotated, List, Optional, Union
from fastapi import APIRouter
from fastapi.params import Query
import requests
from src import config
from src.auth.service import CurrentUser, InternalKey, Token
from src.config import DEFAULT_TC_ID
from src.database.core import DbSession
@ -42,13 +40,9 @@ async def create_calculation_time_constrains(
current_user: CurrentUser,
calculation_time_constrains_in: CalculationTimeConstrainsParametersCreate,
params: Annotated[CreateCalculationQuery, Query()],
# scope_calculation_id: Optional[str] = Query(None),
# with_results: Optional[int] = Query(0),
# simulation_id = Query(None)
):
"""Save calculation time constrains Here"""
scope_calculation_id = params.scope_calculation_id
with_results = params.with_results
simulation_id = params.simulation_id
if scope_calculation_id:
@ -132,7 +126,6 @@ async def get_calculation_results(db_session: DbSession, calculation_id, token:I
# requests.post(f"{config.AUTH_SERVICE_API}/sign-out", headers={
# "Authorization": f"Bearer {token}"
# })
return StandardResponse(
data=results,

@ -1,7 +1,6 @@
from src.overhaul_scope.schema import ScopeRead
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, List, Optional, Union
from typing import Dict, List, Optional, Union
from uuid import UUID
from pydantic import Field
@ -24,7 +23,6 @@ class CalculationTimeConstrainsParametersRetrive(CalculationTimeConstrainsBase):
costPerFailure: Union[dict, float] = Field(..., description="Cost per failure")
availableScopes: List[str] = Field(..., description="Available scopes")
recommendedScope: str = Field(..., description="Recommended scope")
# historicalData: Dict[str, Any] = Field(..., description="Historical data")
class CalculationTimeConstrainsParametersRead(CalculationTimeConstrainsBase):
@ -40,10 +38,6 @@ class CalculationTimeConstrainsParametersCreate(CalculationTimeConstrainsBase):
# class CalculationTimeConstrainsCreate(CalculationTimeConstrainsBase):
# overhaulCost: float = Field(..., description="Overhaul cost")
# scopeOH: str = Field(..., description="Scope OH")
# costPerFailure: float = Field(..., description="Cost per failure")
# metadata: Dict[str, Any] = Field(..., description="Metadata")
class CalculationResultsRead(CalculationTimeConstrainsBase):
@ -112,10 +106,6 @@ class CalculationTimeConstrainsReadNoResult(CalculationTimeConstrainsBase):
max_interval: Optional[int]
optimum_analysis: Optional[dict]
session: ScopeRead
# optimum_oh_day: int
# max_interval: int
# optimal_analysis: dict
# analysis_metadata: dict
class CalculationTimeConstrainsRead(CalculationTimeConstrainsBase):
id: UUID

@ -1,5 +1,5 @@
import datetime
from typing import Coroutine, List, Optional, Tuple,Dict
from typing import List, Optional, Tuple,Dict
from uuid import UUID
import calendar
@ -14,11 +14,8 @@ from sqlalchemy.orm import joinedload, selectinload
from src.database.core import DbSession
from src.overhaul_activity.service import get_all as get_all_by_session_id
from src.overhaul_scope.service import get as get_scope, get_prev_oh
from src.sparepart.service import get_spareparts_paginated, load_sparepart_data_from_db
from src.utils import get_latest_numOfFail
from src.sparepart.service import load_sparepart_data_from_db
from src.workorder.model import MasterWorkOrder
from src.database.core import CollectorDbSession
from src.overhaul_activity.model import OverhaulActivity
from .model import (CalculationData, CalculationEquipmentResult,
CalculationResult)
from .schema import (CalculationResultsRead,
@ -26,21 +23,15 @@ from .schema import (CalculationResultsRead,
CalculationTimeConstrainsParametersCreate,
CalculationTimeConstrainsRead, OptimumResult)
from .utils import analyze_monthly_metrics, calculate_failures_per_month, calculate_risk_cost_per_failure, create_time_series_data, get_monthly_risk_analysis, get_months_between, plant_simulation_metrics
from src.equipment_sparepart.model import ScopeEquipmentPart
import copy
import random
from .utils import analyze_monthly_metrics, create_time_series_data, get_months_between, plant_simulation_metrics
import math
from src.overhaul_activity.service import get_standard_scope_by_session_id
from collections import defaultdict
from datetime import timedelta
import pandas as pd
import logging
import aiohttp
from datetime import datetime, date
import asyncio
import json
# from src.utils import save_to_pastebin
from src.config import BASE_URL, REALIBILITY_SERVICE_API
client = httpx.AsyncClient(timeout=300.0)
@ -184,7 +175,6 @@ class OptimumCostModelWithSpareparts:
failure_counts = []
cumulative_risk = 0
monthly_risk_cost_per_failure = 0
if ecs:
@ -272,24 +262,18 @@ class OptimumCostModelWithSpareparts:
feasible_results = [r for r in cost_results if r['can_proceed']]
# if not feasible_results:
# self.logger.warning(f"No feasible overhaul months for {location_tag} due to sparepart constraints")
# # Return the earliest month with the least critical parts missing
# min_critical_missing = min(r['missing_critical_parts'] for r in cost_results)
# for result in cost_results:
# if result['missing_critical_parts'] == min_critical_missing:
# return self._create_optimal_result(result, location_tag, "INFEASIBLE")
# return None
# Find month with minimum total cost among feasible options
min_cost = float('inf')
optimal_result = None
optimal_index = -1
for i, result in enumerate(cost_results):
if result in feasible_results and result['total_cost'] < min_cost:
min_cost = result['total_cost']
optimal_result = result
optimal_index = i
if optimal_result is None:
return None
@ -325,7 +309,6 @@ class OptimumCostModelWithSpareparts:
'sparepart_available': optimal_result['sparepart_available'],
'sparepart_status': optimal_result['sparepart_status'],
'procurement_details': optimal_result['procurement_details'],
# 'missing_critical_parts': optimal_result['missing_critical_parts'],
'optimization_status': status,
'all_monthly_costs': [] # Will be filled by calling function
}
@ -364,13 +347,9 @@ class OptimumCostModelWithSpareparts:
eq["Location"]: eq
for eq in data
}
plant_monthly_metrics = await self.get_failures_prediction(simulation_id=simulation_id, location_tag="plant", use_location_tag=0, birnbaum_importance=0)
await self.get_failures_prediction(simulation_id=simulation_id, location_tag="plant", use_location_tag=0, birnbaum_importance=0)
REFERENCE_CAPACITY = 630 # or 550
COST_PER_MWH = 1_000_000 # rupiah
# plant_capacity_loss_money = [metrics['derated_mwh'] * COST_PER_MWH for metrics in plant_monthly_metrics.values()]
# cumulative_loss_money = np.cumsum(plant_capacity_loss_money)
total_simulation_period = int((importance_results["plant_result"]['total_downtime'] + importance_results["plant_result"]['total_uptime'])/720)
@ -385,7 +364,6 @@ class OptimumCostModelWithSpareparts:
contribution_factor = equipment_birnbaum.get(location_tag, 0.0)
ecs = ecs_tags.get(location_tag, None)
# try:
# # Get failure predictions
monthly_data = await self.get_failures_prediction(simulation_id, location_tag, contribution_factor)
@ -419,15 +397,12 @@ class OptimumCostModelWithSpareparts:
self.logger.info(f"Individual optimal for {location_tag}: Month {optimal_timing['optimal_month']}")
# except Exception as e:
# self.logger.error(f"Failed to calculate individual timing for {location_tag}: {e}")
# raise Exception(e)
# Phase 2: Optimize considering sparepart interactions
self.logger.info("Phase 2: Optimizing with sparepart interactions...")
# Start with individual optimal timings
current_plan = [(tag, result['optimal_month']) for tag, result in individual_results.items()]
[(tag, result['optimal_month']) for tag, result in individual_results.items()]
# Iteratively improve the plan considering sparepart constraints
improved_plan = self._optimize_fleet_with_sparepart_constraints(
@ -515,7 +490,7 @@ class OptimumCostModelWithSpareparts:
fleet_optimal_cost = total_costs[fleet_optimal_index]
# Generate procurement optimization report
procurement_plan = self.sparepart_manager.optimize_procurement_timing(improved_plan)
self.sparepart_manager.optimize_procurement_timing(improved_plan)
# Update calculation with results
calculation.optimum_oh_day = fleet_optimal_index
@ -825,7 +800,6 @@ async def get_calculation_result(db_session: DbSession, calculation_id: str, tok
# Filter included equipment for performance
included_equipment = [eq for eq in scope_calculation.equipment_results if eq.is_included]
all_equipment = scope_calculation.equipment_results
# all_spareparts = await get_spareparts_paginated(db_session=db_session, collector_db_session=collector_db_session )
# Pre-calculate aggregated statistics
calculation_results = []
@ -850,13 +824,8 @@ async def get_calculation_result(db_session: DbSession, calculation_id: str, tok
k = 5
loss_exp = (plant_monthly_metrics['total_downtime'] * 660 * 500_000 * include_risk_cost) * (np.exp(k * (loss_production_per_month / total_simulation_period)) - 1) / (np.exp(k) - 1)
# REFERENCE_CAPACITY = 630 # or 550
# COST_PER_MWH = 1_000_000 # rupiah
# plant_capacity_loss_money = [metrics['derated_mwh'] * COST_PER_MWH for metrics in plant_monthly_metrics.values()]
# cumulative_loss_money = np.cumsum(plant_capacity_loss_money)
# # raise Exception(plant_monthly_metrics)
# Process each monthself
for month_index in range(data_num):
@ -1050,8 +1019,7 @@ def _analyze_optimal_timing(calculation_results: List, optimum_oh_day: int,
(scope_overhaul.start_date.month - prev_oh_scope.end_date.month)
# Find minimum cost point
min_cost_result = min(calculation_results, key=lambda x: x.total_cost)
min_cost_month = min_cost_result.month
min(calculation_results, key=lambda x: x.total_cost)
# Calculate timing recommendation
timing_recommendation = "OPTIMAL"

@ -1,5 +1,4 @@
import datetime
import json
import pandas as pd
import requests
@ -230,7 +229,6 @@ def calculate_risk_cost_per_failure(monthly_results, birnbaum_importance, energy
failures_count = data['failures_count']
# 1. Calculate equipment capacity contribution to system
# Capacity = avg_flowrate * birnbaum_importance * availability
equipment_capacity = avg_flow_rate * birnbaum_importance * availability
# 2. Calculate capacity lost to downtime per month
@ -294,9 +292,4 @@ def get_monthly_risk_analysis(timestamp_outs, birnbaum_importance, energy_price)
}
# Usage example:
# birnbaum_importance = 0.85 # Example value
# energy_price = 100 # Example: $100 per unit
#
# results = get_monthly_risk_analysis(timestamp_outs, birnbaum_importance, energy_price)
# risk_cost_array = results['risk_cost_array']
# print("Risk cost per failure each month:", risk_cost_array)

@ -1,4 +1,3 @@
import base64
import logging
import os
from typing import List
@ -6,7 +5,6 @@ from urllib import parse
from pydantic import BaseModel
from starlette.config import Config
from starlette.datastructures import CommaSeparatedStrings
log = logging.getLogger(__name__)

@ -1,8 +1,7 @@
import json
import logging
from typing import Dict, Union, Tuple
from typing import Dict, Union
from decimal import Decimal, getcontext
import math
# Set high precision for decimal calculations
getcontext().prec = 50
@ -34,7 +33,6 @@ def system_availability(structure: Structure, availabilities: Dict[str, float])
if not components: # Handle empty series
return 1.0
# Series: A_system = A1 * A2 * ... * An
product = Decimal('1.0')
for s in components:
availability = system_availability(s, availabilities)
@ -46,7 +44,6 @@ def system_availability(structure: Structure, availabilities: Dict[str, float])
if not components: # Handle empty parallel
return 0.0
# Parallel: A_system = 1 - (1-A1) * (1-A2) * ... * (1-An)
product = Decimal('1.0')
for s in components:
availability = system_availability(s, availabilities)
@ -256,12 +253,6 @@ def calculate_contribution_accurate(availabilities: Dict[str, float], structure_
key=lambda x: x[1]['birnbaum_importance'],
reverse=True)
# print("\n=== COMPONENT IMPORTANCE ANALYSIS ===")
# print(f"System Availability: {system_info['system_availability']:.6f} ({system_info['system_availability']*100:.4f}%)")
# print(f"System Unavailability: {system_info['system_unavailability']:.6f}")
# print("\nComponent Rankings (by Birnbaum Importance):")
# print(f"{'Component':<20} {'Availability':<12} {'Birnbaum':<12} {'Criticality':<12} {'F-V':<12} {'Contribution%':<12}")
# print("-" * 92)
for component, measures in sorted_components:
print(f"{component:<20} {measures['component_availability']:<12.6f} "
@ -269,7 +260,6 @@ def calculate_contribution_accurate(availabilities: Dict[str, float], structure_
f"{measures['fussell_vesely_importance']:<12.6f} {measures['contribution_percentage']*100:<12.4f}")
# Return results with system info included
# results['_system_info'] = system_info
return results

@ -6,7 +6,7 @@ from typing import Annotated, Any
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import DeclarativeBase
from starlette.requests import Request

@ -4,8 +4,6 @@ from typing import Annotated, List, Type, TypeVar
from fastapi import Depends, Query
from pydantic.types import Json, constr
from sqlalchemy import Select, desc, func, or_
from sqlalchemy.exc import ProgrammingError
from sqlalchemy_filters import apply_pagination
from src.database.schema import CommonParams
@ -28,7 +26,6 @@ def common_parameters(
descending: List[bool] = Query([], alias="descending[]"),
exclude: List[str] = Query([], alias="exclude[]"),
all: int = Query(0, ge=0, le=1000000),
# role: QueryStr = Depends(get_current_role),
):
return {
"db_session": db_session,
@ -40,7 +37,6 @@ def common_parameters(
"descending": descending,
"current_user": current_user,
"all": bool(all),
# "role": role,
}
@ -108,7 +104,6 @@ async def search_filter_sort_paginate(
all: bool = False,
):
"""Common functionality for searching, filtering, sorting, and pagination."""
# try:
# Check if model is Select
if not isinstance(model, Select):
query = Select(model)

@ -1,10 +1,8 @@
from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy import UUID, Column, Float, ForeignKey, String
from sqlalchemy.orm import relationship
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.workorder.model import MasterWorkOrder
from src.models import DefaultMixin
class ScopeEquipmentPart(Base, DefaultMixin):

@ -1,16 +1,11 @@
from typing import Dict, List
from fastapi import APIRouter, Depends, HTTPException, Query, status
from fastapi import APIRouter, Depends
from src.database.core import CollectorDbSession
from src.database.service import (CommonParameters, DbSession,
search_filter_sort_paginate)
from src.models import StandardResponse
from src.auth.access_control import require_any_role, ALLOWED_ROLES
from .schema import (ScopeEquipmentActivityCreate,
ScopeEquipmentActivityPagination,
ScopeEquipmentActivityRead, ScopeEquipmentActivityUpdate)
from .service import get_all
router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))])
@ -19,7 +14,6 @@ router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))])
@router.get("/{location_tag}", response_model=StandardResponse[List[Dict]])
async def get_scope_equipment_parts(collector_db_session: CollectorDbSession, location_tag):
"""Get all scope activity pagination."""
# return
data = await get_all(db_session=collector_db_session, location_tag=location_tag)
return StandardResponse(

@ -1,8 +1,6 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from uuid import UUID
from typing import List, Optional
from pydantic import BaseModel, Field
from pydantic import Field
from src.models import DefultBase, Pagination
@ -30,40 +28,19 @@ class ScopeEquipmentActivityPagination(Pagination):
items: List[ScopeEquipmentActivityRead] = []
# {
# "overview": {
# "totalEquipment": 30,
# "nextSchedule": {
# "date": "2025-01-12",
# "Overhaul": "B",
# "equipmentCount": 30
# }
# },
# "criticalParts": [
# "Boiler feed pump",
# "Boiler reheater system",
# "Drum Level (Right) Root Valve A",
# "BCP A Discharge Valve",
# "BFPT A EXH Press HI Root VLV"
# ],
# "schedules": [
# {
# "date": "2025-01-12",
# "Overhaul": "B",
# "status": "upcoming"
# }
# // ... other scheduled overhauls
# ],
# "systemComponents": {
# "boiler": {
# "status": "operational",
# "lastOverhaul": "2024-06-15"
# },
# "turbine": {
# "hpt": { "status": "operational" },
# "ipt": { "status": "operational" },
# "lpt": { "status": "operational" }
# }
# // ... other major components
# }
# }

@ -1,23 +1,13 @@
import random
from typing import Optional
from sqlalchemy import Delete, Select, and_, text
from sqlalchemy.orm import selectinload
from sqlalchemy import text
from src.auth.service import CurrentUser
from src.database.core import CollectorDbSession, DbSession
from src.database.service import CommonParameters, search_filter_sort_paginate
from .model import ScopeEquipmentPart
from .schema import ScopeEquipmentActivityCreate, ScopeEquipmentActivityUpdate
# async def get(*, db_session: DbSession, scope_equipment_activity_id: str) -> Optional[ScopeEquipmentActivity]:
# """Returns a document based on the given document id."""
# result = await db_session.get(ScopeEquipmentActivity, scope_equipment_activity_id)
# return result
from typing import Optional, List, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession as DbSession
from sqlalchemy.sql import text
import logging
@ -25,10 +15,6 @@ logger = logging.getLogger(__name__)
# async def get_all(
# db_session: CollectorDbSession,
# location_tag: Optional[str] = None,
# start_year: int = 2023,
# end_year: Optional[int] = None,
# parent_wonum: Optional[str] = None
# ) -> List[Dict[str, Any]]:
# """
# Retrieve overhaul spare parts consumption data.
@ -47,14 +33,10 @@ logger = logging.getLogger(__name__)
# # --- 1. Basic validation ---
# if not parent_wonum:
# logger.warning("Parent WO number not provided. Returning empty result.")
# return []
# if start_year < 1900 or (end_year and end_year < start_year):
# raise ValueError("Invalid year range provided.")
# if end_year is None:
# end_year = start_year + 1
# # --- 2. Build SQL safely ---
# base_query = """
@ -65,13 +47,8 @@ logger = logging.getLogger(__name__)
# AND xx_parent = :parent_wonum
# """
# params = {
# "parent_wonum": parent_wonum,
# }
# if location_tag:
# base_query += " AND location_tag = :location_tag"
# params["location_tag"] = location_tag
# base_query += """
# ),
@ -79,7 +56,6 @@ logger = logging.getLogger(__name__)
# SELECT wonum, itemnum, itemqty, inv_curbaltotal, inv_avgcost
# FROM public.wo_max_material
# WHERE wonum IN (SELECT wonum FROM filtered_wo)
# )
# SELECT
# fwo.location_tag AS location_tag,
# fm.itemnum,
@ -95,36 +71,16 @@ logger = logging.getLogger(__name__)
# """
# # --- 3. Execute query ---
# try:
# result = await db_session.execute(text(base_query), params)
# rows = result.fetchall()
# # Handle "no data found"
# if not rows:
# logger.info(f"No spare part data found for parent WO {parent_wonum}.")
# return []
# # --- 4. Map results cleanly ---
# equipment_parts = []
# for row in rows:
# try:
# equipment_parts.append({
# "location_tag": row.location_tag,
# "itemnum": row.itemnum,
# "sparepart_name": row.sparepart_name or "-",
# "parts_consumed_in_oh": float(row.parts_consumed_in_oh or 0),
# "avgcost": float(row.avgcost or 0),
# "inv_curbaltotal": float(row.inv_curbaltotal or 0)
# })
# except Exception as parse_err:
# logger.error(f"Failed to parse row {row}: {parse_err}")
# continue # Skip malformed rows
# return equipment_parts
# except Exception as e:
# logger.exception(f"Database query failed: {e}")
# raise RuntimeError("Failed to fetch overhaul spare parts data.") from e
from typing import List, Dict, Any, Optional
from sqlalchemy.ext.asyncio import AsyncSession

@ -1,10 +1,8 @@
from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy import UUID, Column, ForeignKey, String
from sqlalchemy.orm import relationship
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.workorder.model import MasterWorkOrder
from src.models import DefaultMixin
class EquipmentWorkscopeGroup(Base, DefaultMixin):

@ -1,9 +1,8 @@
from typing import Dict, List, Optional
from typing import Optional
from fastapi import APIRouter, HTTPException, Query, status
from fastapi import APIRouter, Query
from src.database.service import (CommonParameters, DbSession,
search_filter_sort_paginate)
from src.database.service import (CommonParameters, DbSession)
from src.models import StandardResponse
from .schema import ScopeEquipmentJobCreate, ScopeEquipmentJobPagination
@ -17,7 +16,6 @@ router = APIRouter()
)
async def get_jobs(common: CommonParameters, location_tag: str, scope: Optional[str] = Query(None)):
"""Get all scope pagination."""
# return
results = await get_all(common=common, location_tag=location_tag, scope=scope)
return StandardResponse(
@ -31,7 +29,6 @@ async def create_scope_equipment_jobs(
db_session: DbSession, assetnum, scope_job_in: ScopeEquipmentJobCreate
):
"""Get all scope activity pagination."""
# return
await create(db_session=db_session, assetnum=assetnum, scope_job_in=scope_job_in)
return StandardResponse(

@ -1,8 +1,7 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from typing import List, Optional
from uuid import UUID
from pydantic import BaseModel, Field
from pydantic import Field
from src.workscope_group.schema import ActivityMasterRead
from src.models import DefultBase, Pagination
@ -41,40 +40,19 @@ class ScopeEquipmentJobPagination(Pagination):
items: List[ScopeEquipmentJobRead] = []
# {
# "overview": {
# "totalEquipment": 30,
# "nextSchedule": {
# "date": "2025-01-12",
# "Overhaul": "B",
# "equipmentCount": 30
# }
# },
# "criticalParts": [
# "Boiler feed pump",
# "Boiler reheater system",
# "Drum Level (Right) Root Valve A",
# "BCP A Discharge Valve",
# "BFPT A EXH Press HI Root VLV"
# ],
# "schedules": [
# {
# "date": "2025-01-12",
# "Overhaul": "B",
# "status": "upcoming"
# }
# // ... other scheduled overhauls
# ],
# "systemComponents": {
# "boiler": {
# "status": "operational",
# "lastOverhaul": "2024-06-15"
# },
# "turbine": {
# "hpt": { "status": "operational" },
# "ipt": { "status": "operational" },
# "lpt": { "status": "operational" }
# }
# // ... other major components
# }
# }

@ -1,16 +1,11 @@
import random
from typing import Optional
from fastapi import HTTPException, status
from sqlalchemy import Delete, Select, and_
from sqlalchemy.orm import selectinload
from sqlalchemy import Select
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import CommonParameters, search_filter_sort_paginate
from src.overhaul_activity.model import OverhaulActivity
from src.database.service import search_filter_sort_paginate
from src.standard_scope.model import MasterEquipment
from src.standard_scope.service import get_equipment_level_by_no
from src.workscope_group.model import MasterActivity
from src.workscope_group_maintenance_type.model import WorkscopeOHType
from src.overhaul_scope.model import MaintenanceType
@ -21,37 +16,23 @@ from src.soft_delete import apply_not_deleted_filter
# async def get(*, db_session: DbSession, scope_equipment_activity_id: str) -> Optional[ScopeEquipmentActivity]:
# """Returns a document based on the given document id."""
# result = await db_session.get(ScopeEquipmentActivity, scope_equipment_activity_id)
# return result
# async def get_all(db_session: DbSession, assetnum: Optional[str], common):
# # Example usage
# if not assetnum:
# raise ValueError("assetnum parameter is required")
# # First get the parent equipment
# equipment_stmt = Select(MasterEquipment).where(MasterEquipment.assetnum == assetnum)
# equipment: MasterEquipment = await db_session.scalar(equipment_stmt)
# if not equipment:
# raise ValueError(f"No equipment found with assetnum: {assetnum}")
# # Build query for parts
# stmt = (
# Select(ScopeEquipmentJob)
# .where(ScopeEquipmentJob.assetnum == assetnum)
# .options(
# selectinload(ScopeEquipmentJob.job),
# selectinload(ScopeEquipmentJob.overhaul_jobs)
# .selectinload(OverhaulJob.overhaul_activity)
# .selectinload(OverhaulActivity.overhaul_scope),
# )
# )
# results = await search_filter_sort_paginate(model=stmt, **common)
# return results
async def get_all(*, common, location_tag: str, scope: Optional[str] = None):
"""Returns all documents."""
@ -100,17 +81,12 @@ async def create(
# async def update(*, db_session: DbSession, activity: ScopeEquipmentActivity, scope_equipment_activty_in: ScopeEquipmentActivityUpdate):
# """Updates a document."""
# data = scope_equipment_activty_in.model_dump()
# update_data = scope_equipment_activty_in.model_dump(exclude_defaults=True)
# for field in data:
# if field in update_data:
# setattr(activity, field, update_data[field])
# await db_session.commit()
# return activity
async def delete(
@ -148,6 +124,6 @@ async def delete(
return True
except Exception as e:
except Exception:
await db_session.rollback()
raise

@ -2,39 +2,26 @@ import logging
import asyncio
import time
from src.context import set_request_id, reset_request_id, get_request_id
from contextvars import ContextVar
from os import path
from typing import Final, Optional
from uuid import uuid1
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi import Depends, FastAPI, HTTPException
from src.auth.service import JWTBearer
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from pydantic import ValidationError
from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
from sqlalchemy import inspect
from sqlalchemy.ext.asyncio import async_scoped_session
from sqlalchemy.orm import scoped_session
from starlette.middleware.base import (BaseHTTPMiddleware,
RequestResponseEndpoint)
from starlette.middleware.base import (BaseHTTPMiddleware)
from starlette.middleware.gzip import GZipMiddleware
from starlette.requests import Request
from starlette.responses import FileResponse, Response, StreamingResponse
from starlette.routing import compile_path
from starlette.staticfiles import StaticFiles
from starlette.responses import Response
import uuid as _uuid_module
from src.api import api_router
from src.database.core import async_session, engine, async_collector_session
from src.database.core import async_session, async_collector_session
from src.enums import ResponseStatus
from src.exceptions import handle_exception
from src.app_logging import configure_logging
from src.middleware import RequestValidationMiddleware, RequestTimeoutMiddleware
# from src.rate_limiter import limiter
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
from src.csrf_protect import csrf_protect
@ -74,9 +61,7 @@ app.add_exception_handler(StarletteHTTPException, handle_exception)
app.add_exception_handler(RequestValidationError, handle_exception)
app.add_exception_handler(RateLimitExceeded, _sync_rate_limit_handler)
# app.state.limiter = limiter
app.add_middleware(GZipMiddleware, minimum_size=1000)
# app.add_middleware(SlowAPIMiddleware)
app.add_middleware(RequestTimeoutMiddleware, timeout=REQUEST_TIMEOUT_SECONDS)
# credentials: "include",
@ -160,8 +145,6 @@ security_headers_middleware(app)
app.add_middleware(RequestValidationMiddleware)
# app.add_middleware(
# CSRFProtectMiddleware,
# excluded_paths=["/healthcheck"]
# )
@ -301,24 +284,9 @@ async def add_security_headers(request: Request, call_next):
# class MetricsMiddleware(BaseHTTPMiddleware):
# async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
# method = request.method
# endpoint = request.url.path
# tags = {"method": method, "endpoint": endpoint}
# try:
# start = time.perf_counter()
# response = await call_next(request)
# elapsed_time = time.perf_counter() - start
# tags.update({"status_code": response.status_code})
# metric_provider.counter("server.call.counter", tags=tags)
# metric_provider.timer("server.call.elapsed", value=elapsed_time, tags=tags)
# log.debug(f"server.call.elapsed.{endpoint}: {elapsed_time}")
# except Exception as e:
# metric_provider.counter("server.call.exception.counter", tags=tags)
# raise e from None
# return response
# app.add_middleware(ExceptionMiddleware)
app.include_router(api_router)

@ -1,10 +1,6 @@
from datetime import datetime
from typing import Optional, Union
from sqlalchemy import select, func, cast, Numeric, text
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 sqlalchemy import select, text
from src.database.core import CollectorDbSession, DbSession
from src.overhaul_scope.model import OverhaulScope
@ -107,7 +103,6 @@ ORDER BY avg_cost DESC;
# wm.wonum,
# SUM(
# wm.itemqty *
# COALESCE(wm.inv_avgcost, po.unit_cost, 0)
# ) AS parts_total_cost
# FROM public.wo_maxim_material wm
# LEFT JOIN (
@ -129,7 +124,6 @@ ORDER BY avg_cost DESC;
# COALESCE(w.actservcost, 0) AS service_cost
# FROM target_wo w
# LEFT JOIN part_costs pc ON w.wonum = pc.wonum
# )
# SELECT
# asset_location,
# ROUND(SUM(material_cost + service_cost)::numeric / COUNT(wonum), 2) AS avg_cost,
@ -139,19 +133,11 @@ ORDER BY avg_cost DESC;
# ORDER BY total_wo_count DESC;
# """)
# parent_nums = []
# result = await collector_db.execute(query, {"parent_nums": parent_nums})
# data = []
# for row in result:
# data.append({
# "location_tag": row.asset_location,
# "avg_cost": float(row.avg_cost or 0.0),
# "total_wo_count": row.total_wo_count,
# })
# return {item["location_tag"]: item["avg_cost"] for item in data}
async def get_oh_cost_summary(collector_db: CollectorDbSession, last_oh_date:datetime, upcoming_oh_date:datetime):
@ -182,13 +168,11 @@ async def get_oh_cost_summary(collector_db: CollectorDbSession, last_oh_date:dat
# LEFT JOIN part_costs pc
# ON w.wonum = pc.wonum
# WHERE
# w.worktype = 'OH'
# AND w.reportdate IS NOT NULL
# AND w.actstart IS NOT NULL
# AND w.actfinish IS NOT NULL
# AND w.asset_unit IN ('3', '00')
# AND w.wonum NOT LIKE 'T%'
# )
# SELECT
# asset_location,
# AVG(total_wo_cost) AS avg_cost

@ -1,46 +1,25 @@
# import logging
# from dispatch.plugins.base import plugins
# from .config import METRIC_PROVIDERS
# log = logging.getLogger(__file__)
# class Metrics(object):
# _providers = []
# def __init__(self):
# if not METRIC_PROVIDERS:
# log.info(
# "No metric providers defined via METRIC_PROVIDERS env var. Metrics will not be sent."
# )
# else:
# self._providers = METRIC_PROVIDERS
# def gauge(self, name, value, tags=None):
# for provider in self._providers:
# log.debug(
# f"Sending gauge metric {name} to provider {provider}. Value: {value} Tags: {tags}"
# )
# p = plugins.get(provider)
# p.gauge(name, value, tags=tags)
# def counter(self, name, value=None, tags=None):
# for provider in self._providers:
# log.debug(
# f"Sending counter metric {name} to provider {provider}. Value: {value} Tags: {tags}"
# )
# p = plugins.get(provider)
# p.counter(name, value=value, tags=tags)
# def timer(self, name, value, tags=None):
# for provider in self._providers:
# log.debug(
# f"Sending timer metric {name} to provider {provider}. Value: {value} Tags: {tags}"
# )
# p = plugins.get(provider)
# p.timer(name, value, tags=tags)
# provider = Metrics()

@ -269,34 +269,20 @@ class RequestValidationMiddleware(BaseHTTPMiddleware):
# # -------------------------
# # 0. Header validation
# # -------------------------
# header_keys = [key.lower() for key, _ in request.headers.items()]
# # Check for duplicate headers
# header_counter = Counter(header_keys)
# duplicate_headers = [key for key, count in header_counter.items() if count > 1]
# ALLOW_DUPLICATE_HEADERS = {'accept', 'accept-encoding', 'accept-language', 'accept-charset', 'cookie'}
# real_duplicates = [h for h in duplicate_headers if h not in ALLOW_DUPLICATE_HEADERS]
# if real_duplicates:
# raise HTTPException(
# status_code=422,
# detail=f"Duplicate headers detected: {real_duplicates}",
# )
# # Whitelist headers
# unknown_headers = [key for key in header_keys if key not in ALLOWED_HEADERS]
# if unknown_headers:
# filtered_unknown = [h for h in unknown_headers if not h.startswith('sec-')]
# if filtered_unknown:
# raise HTTPException(
# status_code=422,
# detail=f"Unknown headers detected: {filtered_unknown}",
# )
# # Inspect header values
# for key, value in request.headers.items():
# if value:
# inspect_value(value, f"header '{key}'")
# -------------------------
# 1. Query string limits
@ -400,7 +386,6 @@ class RequestValidationMiddleware(BaseHTTPMiddleware):
if content_type.startswith("application/json"):
body = await request.body()
# if len(body) > MAX_JSON_BODY_SIZE:
# raise HTTPException(status_code=422, detail="JSON body too large")
if body:
try:
@ -419,7 +404,6 @@ class RequestValidationMiddleware(BaseHTTPMiddleware):
return await call_next(request)
import threading
import asyncio
import json
import logging

@ -4,12 +4,10 @@ from datetime import datetime
from typing import Generic, Optional, TypeVar
import pytz
from pydantic import BaseModel, Field, SecretStr
from sqlalchemy import Column, DateTime, String, event, func
from pydantic import BaseModel, SecretStr
from sqlalchemy import Column, DateTime, String, event
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from src.auth.service import CurrentUser
from src.config import TIMEZONE
from src.enums import ResponseStatus

@ -1,6 +1,6 @@
from typing import List
from fastapi import APIRouter, HTTPException, status
from fastapi import APIRouter
from src.auth.service import Token
from src.database.core import CollectorDbSession, DbSession

@ -1,10 +1,7 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from uuid import UUID
from typing import Any, Dict, List
from pydantic import BaseModel, Field
from src.models import DefultBase, Pagination
from src.overhaul_scope.schema import ScopeRead
@ -33,40 +30,19 @@ class OverhaulRead(OverhaulBase):
systemComponents: Dict[str, Any]
# {
# "overview": {
# "totalEquipment": 30,
# "nextSchedule": {
# "date": "2025-01-12",
# "Overhaul": "B",
# "equipmentCount": 30
# }
# },
# "criticalParts": [
# "Boiler feed pump",
# "Boiler reheater system",
# "Drum Level (Right) Root Valve A",
# "BCP A Discharge Valve",
# "BFPT A EXH Press HI Root VLV"
# ],
# "schedules": [
# {
# "date": "2025-01-12",
# "Overhaul": "B",
# "status": "upcoming"
# }
# // ... other scheduled overhauls
# ],
# "systemComponents": {
# "boiler": {
# "status": "operational",
# "lastOverhaul": "2024-06-15"
# },
# "turbine": {
# "hpt": { "status": "operational" },
# "ipt": { "status": "operational" },
# "lpt": { "status": "operational" }
# }
# // ... other major components
# }
# }

@ -1,19 +1,15 @@
import asyncio
from typing import Optional
import httpx
from sqlalchemy import Delete, Select
from sqlalchemy import Select
from src.auth.service import CurrentUser
from src.calculation_target_reliability.service import RBD_SERVICE_API
from src.config import TC_RBD_ID
from src.database.core import DbSession
from src.contribution_util import calculate_contribution
from src.overhaul_activity.service import get_standard_scope_by_session_id
from src.overhaul_scope.model import OverhaulScope
from src.overhaul_scope.service import get_all as get_all_session
from src.overhaul_scope.service import get_overview_overhaul
from src.standard_scope.service import get_by_oh_session_id
async def get_overhaul_overview(db_session: DbSession):
@ -30,28 +26,23 @@ async def get_simulation_results(*, simulation_id: str, token: str):
}
calc_result_url = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}?nodetype=RegularNode"
# plot_result_url = f"{RBD_SERVICE_API}/aeros/simulation/result/plot/{simulation_id}?nodetype=RegularNode"
calc_plant_result = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}/plant"
async with httpx.AsyncClient(timeout=300.0) as client:
calc_task = client.get(calc_result_url, headers=headers)
# plot_task = client.get(plot_result_url, headers=headers)
plant_task = client.get(calc_plant_result, headers=headers)
# Run all three requests concurrently
calc_response, plant_response = await asyncio.gather(calc_task, plant_task)
calc_response.raise_for_status()
# plot_response.raise_for_status()
plant_response.raise_for_status()
calc_data = calc_response.json()["data"]
# plot_data = plot_response.json()["data"]
plant_data = plant_response.json()["data"]
return {
"calc_result": calc_data,
# "plot_result": plot_data,
"plant_result": plant_data
}
@ -313,43 +304,25 @@ def get_overhaul_system_components():
# async def get(*, db_session: DbSession, scope_id: str) -> Optional[Scope]:
# """Returns a document based on the given document id."""
# query = Select(Scope).filter(Scope.id == scope_id)
# result = await db_session.execute(query)
# return result.scalars().one_or_none()
# async def get_all(*, db_session: DbSession):
# """Returns all documents."""
# query = Select(Scope)
# result = await db_session.execute(query)
# return result.scalars().all()
# async def create(*, db_session: DbSession, scope_id: ScopeCreate):
# """Creates a new document."""
# scope = Scope(**scope_id.model_dump())
# db_session.add(scope)
# await db_session.commit()
# return scope
# async def update(*, db_session: DbSession, scope: Scope, scope_id: ScopeUpdate):
# """Updates a document."""
# data = scope_id.model_dump()
# update_data = scope_id.model_dump(exclude_defaults=True)
# for field in data:
# if field in update_data:
# setattr(scope, field, update_data[field])
# await db_session.commit()
# return scope
# async def delete(*, db_session: DbSession, scope_id: str):
# """Deletes a document."""
# query = Delete(Scope).where(Scope.id == scope_id)
# await db_session.execute(query)
# await db_session.commit()

@ -1,10 +1,7 @@
from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from sqlalchemy import UUID, Column, Float, ForeignKey, String
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.workorder.model import MasterWorkOrder
from src.models import DefaultMixin
class OverhaulActivity(Base, DefaultMixin):
@ -18,26 +15,11 @@ class OverhaulActivity(Base, DefaultMixin):
service_cost = Column(Float, nullable=False, default=0)
status = Column(String, nullable=False, default="pending")
# equipment = relationship(
# "MasterEquipment",
# lazy="raise",
# primaryjoin="and_(OverhaulActivity.assetnum == foreign(MasterEquipment.assetnum))",
# uselist=False, # Add this if it's a one-to-one relationship
# )
# # sparepart_equipments = relationship(
# # "ScopeEquipmentPart",
# # lazy="select", # or "joined", "subquery", "dynamic" depending on your needs
# # primaryjoin="OverhaulActivity.assetnum == foreign(ScopeEquipmentPart.assetnum)",
# # uselist=True
# # )
# overhaul_scope = relationship(
# "OverhaulScope",
# lazy="raise",
# )
# overhaul_jobs = relationship(
# "OverhaulJob", back_populates="overhaul_activity", lazy="raise"
# )

@ -1,18 +1,16 @@
from typing import List, Optional
from typing import Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query, status
from src.csrf_protect import csrf_protect
from src.database.core import CollectorDbSession
from src.database.service import (CommonParameters, DbSession,
search_filter_sort_paginate)
from src.database.service import (CommonParameters, DbSession)
from src.models import StandardResponse
from src.auth.access_control import require_any_role, ALLOWED_ROLES
from .schema import (OverhaulActivityCreate, OverhaulActivityPagination,
OverhaulActivityRead, OverhaulActivityUpdate)
from .service import add_multiple_equipment_to_session, get, get_all, remove_equipment_from_session, update
from .schema import (OverhaulActivityCreate, OverhaulActivityRead)
from .service import add_multiple_equipment_to_session, get, get_all, remove_equipment_from_session
router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))])
@ -28,7 +26,6 @@ async def get_scope_equipments(
scope_name: Optional[str] = Query(None),
):
"""Get all scope activity pagination."""
# return
data = await get_all(
common=common,
location_tag=location_tag,
@ -52,7 +49,7 @@ async def create_overhaul_equipment(
overhaul_session_id: UUID,
):
activity = await add_multiple_equipment_to_session(
await add_multiple_equipment_to_session(
db_session=db_session,
collector_db=collector_db_session,
overhaul_session_id=overhaul_session_id,
@ -82,30 +79,17 @@ async def get_overhaul_equipment(
# @router.put(
# "/{overhaul_session}/{assetnum}",
# response_model=StandardResponse[OverhaulActivityRead],
# )
# async def update_scope(
# db_session: DbSession,
# scope_equipment_activity_in: OverhaulActivityUpdate,
# assetnum: str,
# ):
# activity = await get(db_session=db_session, assetnum=assetnum)
# if not activity:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail="A data with this id does not exist.",
# )
# return StandardResponse(
# data=await update(
# db_session=db_session,
# activity=activity,
# scope_equipment_activity_in=scope_equipment_activity_in,
# ),
# message="Data updated successfully",
# )
@router.post(

@ -1,11 +1,10 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from typing import List, Optional
from uuid import UUID
from pydantic import Field
from src.models import DefultBase, Pagination
from src.standard_scope.schema import MasterEquipmentTree
from src.workscope_group.schema import ActivityMasterRead
class OverhaulActivityBase(DefultBase):
@ -41,9 +40,6 @@ class OverhaulActivityRead(OverhaulActivityBase):
equipment_name: Optional[str]
oh_scope: str
overhaul_cost: Optional[float] = Field(0, ge=0, le=1000000000000000)
# equipment: MasterEquipmentTree
# overhaul_scope: OverhaulScope
# overhaul_jobs: Optional[List[OverhaulJob]] = Field([])
class OverhaulActivityPagination(Pagination):

@ -1,23 +1,16 @@
import asyncio
import datetime
from typing import List, Optional
from uuid import UUID, uuid4
from fastapi import HTTPException, status
from sqlalchemy import Delete, Select, and_, func, select
from sqlalchemy import update as sqlUpdate
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy import Select, and_, select
from sqlalchemy.orm import joinedload, selectinload
from src.auth.service import CurrentUser
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.utils import update_model
from src.overhaul_scope.model import OverhaulScope
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
from src.equipment_workscope_group.model import EquipmentWorkscopeGroup
from src.overhaul_scope.model import MaintenanceType
@ -25,7 +18,7 @@ from src.workscope_group_maintenance_type.model import WorkscopeOHType
from src.overhaul_scope.service import get as get_overhaul
from src.standard_scope.model import EquipmentOHHistory
from .model import OverhaulActivity
from .schema import (OverhaulActivityCreate, OverhaulActivityRead,
from .schema import (OverhaulActivityRead,
OverhaulActivityUpdate)
import json
@ -65,31 +58,17 @@ async def get_all(
all: bool = False,
collector_db: CollectorDbSession
):
# query = (
# Select(OverhaulActivity)
# .where(OverhaulActivity.overhaul_scope_id == overhaul_session_id)
# .options(joinedload(OverhaulActivity.equipment).options(joinedload(MasterEquipment.parent).options(joinedload(MasterEquipment.parent))))
# .options(selectinload(OverhaulActivity.overhaul_scope))
# .options(selectinload(OverhaulActivity.overhaul_jobs).options(joinedload(OverhaulJob.scope_equipment_job).options(joinedload(ScopeEquipmentJob.job))))
# )
# if assetnum:
# query = query.filter(OverhaulActivity.assetnum == assetnum).options(
# joinedload(OverhaulActivity.overhaul_scope)
# )
# if scope_name:
# query = query.filter(OverhaulActivity.scope_name == scope_name).options(
# joinedload(OverhaulActivity.overhaul_scope)
# )
# results = await search_filter_sort_paginate(model=query, **common)
##raise Exception(results['items'][0].equipment.parent.__dict__)
# equipments, overhaul = await get_by_oh_session_id(
# db_session=db_session, oh_session_id=overhaul_session_id
# )
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)
@ -191,7 +170,6 @@ async def get_standard_scope_by_session_id(*, db_session: DbSession, overhaul_se
results = []
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))
overhaul_cost = await get_oh_cost_summary(collector_db=collector_db, last_oh_date=prev_oh_scope.end_date, upcoming_oh_date=overhaul.start_date)
for equipment in eqs:

@ -1,18 +1,15 @@
import re
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends
from sqlalchemy import select
from src.auth.service import CurrentUser, Admin
from src.auth.service import Admin
from src.csrf_protect import csrf_protect
from src.database.core import DbSession
from src.database.service import CommonParameters
from src.models import StandardResponse
from src.overhaul_gantt.model import OverhaulGantt
from src.overhaul_gantt.schema import OverhaulGanttIn
# from .schema import (OverhaulScheduleCreate, OverhaulSchedulePagination, OverhaulScheduleUpdate)
from .service import get_gantt_performance_chart
router = APIRouter()
@ -23,7 +20,6 @@ router = APIRouter()
)
async def get_gantt_performance(db_session: DbSession):
"""Get all scope pagination."""
# return
query = select(OverhaulGantt).limit(1)
data = (await db_session.execute(query)).scalar_one_or_none()
@ -43,7 +39,6 @@ async def get_gantt_performance(db_session: DbSession):
)
async def get_gantt_spreadsheet(db_session: DbSession):
"""Get all scope pagination."""
# return
query = select(OverhaulGantt).limit(1)
data = (await db_session.execute(query)).scalar_one_or_none()
@ -69,7 +64,6 @@ async def get_gantt_spreadsheet(db_session: DbSession):
)
async def update_gantt_spreadsheet(db_session: DbSession, spreadsheet_in: OverhaulGanttIn, admin: Admin):
"""Get all scope pagination."""
# return
match = re.search(r"/d/([a-zA-Z0-9-_]+)", spreadsheet_in.spreadsheet_link)
if not match:
@ -113,31 +107,17 @@ async def update_gantt_spreadsheet(db_session: DbSession, spreadsheet_in: Overha
# db_session: DbSession, overhaul_job_in: OverhaulScheduleCreate
# ):
# await create(
# db_session=db_session,
# overhaul_job_in=overhaul_job_in,
# )
# return StandardResponse(
# data=None,
# message="Data created successfully",
# )
# @router.put("/{overhaul_job_id}", response_model=StandardResponse[None])
# async def update_overhaul_schedule(
# db_session: DbSession, overhaul_job_id: str, overhaul_job_in: OverhaulScheduleUpdate
# ):
# await update(db_session=db_session, overhaul_schedule_id=overhaul_job_id, overhaul_job_in=overhaul_job_in)
# return StandardResponse(
# data=None,
# message="Data updated successfully",
# )
# @router.delete("/{overhaul_job_id}", response_model=StandardResponse[None])
# async def delete_overhaul_equipment_job(db_session: DbSession, overhaul_job_id):
# await delete(db_session=db_session, overhaul_schedule_id=overhaul_job_id)
# return StandardResponse(
# data=None,
# message="Data deleted successfully",
# )

@ -1,13 +1,5 @@
# from datetime import datetime
# from typing import List, Optional
# from uuid import UUID
# from pydantic import Field
# from src.models import DefultBase, Pagination
# from src.overhaul_scope.schema import ScopeRead
# from src.scope_equipment_job.schema import ScopeEquipmentJobRead
# from src.job.schema import ActivityMasterRead
from pydantic import Field
from src.models import DefultBase
@ -18,31 +10,13 @@ class OverhaulGanttIn(DefultBase):
# class OverhaulScheduleCreate(OverhaulScheduleBase):
# year: int
# plan_duration: Optional[int] = Field(None)
# planned_outage: Optional[int] = Field(None)
# actual_shutdown: Optional[int] = Field(None)
# start: datetime
# finish: datetime
# remark: Optional[str] = Field(None)
# class OverhaulScheduleUpdate(OverhaulScheduleBase):
# start: datetime
# finish: datetime
# class OverhaulScheduleRead(OverhaulScheduleBase):
# id: UUID
# year: int
# plan_duration: Optional[int]
# planned_outage: Optional[int]
# actual_shutdown: Optional[int]
# start: datetime
# finish: datetime
# remark: Optional[str]
# class OverhaulSchedulePagination(Pagination):
# items: List[OverhaulScheduleRead] = []

@ -1,19 +1,11 @@
from typing import Optional
from fastapi import HTTPException, status
from sqlalchemy import Delete, Select, func
from sqlalchemy.orm import selectinload
# from .model import OverhaulSchedule
# from .schema import OverhaulScheduleCreate, OverhaulScheduleUpdate
from .utils import fetch_all_sections, get_google_creds, get_spreatsheed_service, process_spreadsheet_data
# async def get_all(*, common):
# """Returns all documents."""
# query = Select(OverhaulSchedule).order_by(OverhaulSchedule.start.desc())
# results = await search_filter_sort_paginate(model=query, **common)
# return results
# async def create(
@ -21,34 +13,21 @@ from .utils import fetch_all_sections, get_google_creds, get_spreatsheed_service
# ):
# schedule = OverhaulSchedule(**overhaul_job_in.model_dump())
# db_session.add(schedule)
# await db_session.commit()
# return schedule
# async def update(*, db_session: DbSession, overhaul_schedule_id: str, overhaul_job_in: OverhaulScheduleUpdate):
# """Updates a document."""
# data = overhaul_job_in.model_dump()
# overhaul_schedule = await db_session.get(OverhaulSchedule, overhaul_schedule_id)
# update_data = overhaul_job_in.model_dump(exclude_defaults=True)
# for field in data:
# if field in update_data:
# setattr(overhaul_schedule, field, update_data[field])
# await db_session.commit()
# return overhaul_schedule
# async def delete(*, db_session: DbSession, overhaul_schedule_id: str):
# """Deletes a document."""
# query = Delete(OverhaulSchedule).where(OverhaulSchedule.id == overhaul_schedule_id)
# await db_session.execute(query)
# await db_session.commit()
async def get_gantt_performance_chart(*, spreadsheet_id = "1gZXuwA97zU1v4QBv56wKeiqadc6skHUucGKYG8qVFRk"):

@ -1,8 +1,5 @@
import urllib
from google.oauth2.service_account import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"]

@ -1,43 +1,16 @@
from sqlalchemy import (UUID, Column, DateTime, Float, ForeignKey, Integer,
String)
from sqlalchemy.orm import relationship
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
# class EquipmentWorkscopeGroup(Base, DefaultMixin):
# __tablename__ = "oh_tr_equipment_workscope_group"
# # overhaul_activity_id = Column(
# # UUID(as_uuid=True), ForeignKey("oh_tr_overhaul_activity.id"), nullable=False
# # )
# # scope_equipment_job_id = Column(
# # UUID(as_uuid=True),
# # ForeignKey("oh_ms_scope_equipment_job.id", ondelete="cascade"),
# # nullable=False,
# # )
# # notes = Column(String, nullable=True)
# # status = Column(String, nullable=True, default="pending")
# workscope_group_id = Column(
# UUID(as_uuid=True), ForeignKey("oh_ms_workscope_group.id"), nullable=False
# )
# location_tag = Column(String, nullable=False)
# equipment = relationship(
# "StandardScope",
# lazy="selectin",
# primaryjoin="and_(OverhaulJob.location_tag == foreign(StandardScope.location_tag))",
# uselist=False, # Add this if it's a one-to-one relationship
# )
# workscope_group = relationship("MasterActivity", lazy="selectin", back_populates="equipment_workscope_groups")
# # scope_equipment_job = relationship(
# # "ScopeEquipmentJob", lazy="raise", back_populates="overhaul_jobs"
# # )
# # overhaul_activity = relationship("OverhaulActivity", lazy="raise", back_populates="overhaul_jobs")

@ -1,14 +1,12 @@
from typing import List, Optional
from typing import Optional
from fastapi import APIRouter, HTTPException, status, Query
from fastapi import APIRouter, Query
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import CommonParameters
from src.models import StandardResponse
from .schema import (OverhaulJobBase, OverhaulJobCreate, OverhaulJobPagination,
OverhaulJobRead)
from .schema import (OverhaulJobCreate, OverhaulJobPagination)
from .service import create, delete, get_all
router = APIRouter()
@ -19,7 +17,6 @@ router = APIRouter()
)
async def get_jobs(common: CommonParameters, location_tag: str, scope: Optional[str] = Query(None)):
"""Get all scope pagination."""
# return
results = await get_all(common=common, location_tag=location_tag, scope=scope)
return StandardResponse(
@ -33,7 +30,6 @@ async def create_overhaul_equipment_jobs(
db_session: DbSession, overhaul_equipment_id, overhaul_job_in: OverhaulJobCreate
):
"""Get all scope activity pagination."""
# return
await create(
db_session=db_session,
overhaul_equipment_id=overhaul_equipment_id,
@ -58,34 +54,21 @@ async def delete_overhaul_equipment_job(db_session: DbSession, overhaul_job_id):
# @router.post("", response_model=StandardResponse[List[str]])
# async def create_scope(db_session: DbSession, scope_in: OverhaulJobCreate):
# overhaul_job = await create(db_session=db_session, scope_in=scope_in)
# return StandardResponse(data=overhaul_job, message="Data created successfully")
# @router.put("/{scope_id}", response_model=StandardResponse[ScopeRead])
# async def update_scope(db_session: DbSession, scope_id: str, scope_in: ScopeUpdate, current_user: CurrentUser):
# scope = await get(db_session=db_session, scope_id=scope_id)
# if not scope:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail="A data with this id does not exist.",
# )
# return StandardResponse(data=await update(db_session=db_session, scope=scope, scope_in=scope_in), message="Data updated successfully")
# @router.delete("/{scope_id}", response_model=StandardResponse[ScopeRead])
# async def delete_scope(db_session: DbSession, scope_id: str):
# scope = await get(db_session=db_session, scope_id=scope_id)
# if not scope:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=[{"msg": "A data with this id does not exist."}],
# )
# await delete(db_session=db_session, scope_id=scope_id)
# return StandardResponse(message="Data deleted successfully", data=scope)

@ -1,12 +1,9 @@
from datetime import datetime
from typing import List, Optional
from uuid import UUID
from pydantic import Field
from src.models import DefultBase, Pagination
from src.overhaul_scope.schema import ScopeRead
from src.scope_equipment_job.schema import ScopeEquipmentJobRead
from src.job.schema import ActivityMasterRead
class OverhaulJobBase(DefultBase):

@ -1,14 +1,10 @@
from typing import Optional
from fastapi import HTTPException, status
from sqlalchemy import Delete, Select, func
from sqlalchemy.orm import selectinload
from sqlalchemy import Select
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import search_filter_sort_paginate
# from src.scope_equipment_job.model import ScopeEquipmentJob
# from src.overhaul_activity.model import OverhaulActivity
from src.workscope_group.model import MasterActivity
from src.workscope_group_maintenance_type.model import WorkscopeOHType
@ -51,7 +47,7 @@ async def create(
OverhaulJob.overhaul_activity_id == overhaul_equipment_id
)
equipment = await db_session.scalar(equipment_stmt)
await db_session.scalar(equipment_stmt)
for job_id in overhaul_job_in.job_ids:
overhaul_equipment_job = OverhaulJob(
@ -96,28 +92,20 @@ async def delete(
return True
except Exception as e:
except Exception:
await db_session.rollback()
raise
# async def update(*, db_session: DbSession, scope: OverhaulScope, scope_in: ScopeUpdate):
# """Updates a document."""
# data = scope_in.model_dump()
# update_data = scope_in.model_dump(exclude_defaults=True)
# for field in data:
# if field in update_data:
# setattr(scope, field, update_data[field])
# await db_session.commit()
# return scope
# async def delete(*, db_session: DbSession, scope_id: str):
# """Deletes a document."""
# query = Delete(OverhaulScope).where(OverhaulScope.id == scope_id)
# await db_session.execute(query)
# await db_session.commit()

@ -1,9 +1,8 @@
from sqlalchemy import (UUID, Column, DateTime, Float, ForeignKey, Integer,
from sqlalchemy import (Column, DateTime, Integer,
String)
from sqlalchemy.orm import relationship
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.models import DefaultMixin
class OverhaulSchedule(Base, DefaultMixin):

@ -1,8 +1,6 @@
from typing import List, Optional
from fastapi import APIRouter, HTTPException, status
from fastapi import APIRouter
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import CommonParameters
from src.models import StandardResponse
@ -18,7 +16,6 @@ router = APIRouter()
)
async def get_schedules(common: CommonParameters):
"""Get all scope pagination."""
# return
results = await get_all(common=common)

@ -5,9 +5,6 @@ from uuid import UUID
from pydantic import Field
from src.models import DefultBase, Pagination
from src.overhaul_scope.schema import ScopeRead
from src.scope_equipment_job.schema import ScopeEquipmentJobRead
from src.job.schema import ActivityMasterRead
class OverhaulScheduleBase(DefultBase):
pass

@ -1,15 +1,9 @@
from typing import Optional
from fastapi import HTTPException, status
from sqlalchemy import Delete, Select, func
from sqlalchemy.orm import selectinload
from sqlalchemy import Select
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import search_filter_sort_paginate
from src.utils import update_model
from src.scope_equipment_job.model import ScopeEquipmentJob
from src.overhaul_activity.model import OverhaulActivity
from src.soft_delete import apply_not_deleted_filter, soft_delete_record
from .model import OverhaulSchedule
@ -39,7 +33,7 @@ async def create(
async def update(*, db_session: DbSession, overhaul_schedule_id: str, overhaul_job_in: OverhaulScheduleUpdate):
"""Updates a document."""
data = overhaul_job_in.model_dump()
overhaul_job_in.model_dump()
query = Select(OverhaulSchedule).where(OverhaulSchedule.id == overhaul_schedule_id)
query = apply_not_deleted_filter(query, OverhaulSchedule)
query = query.with_for_update()

@ -3,7 +3,7 @@ from sqlalchemy import Column, DateTime , Integer, String, ForeignKey, UUID
from sqlalchemy.orm import relationship
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.models import DefaultMixin
from src.auth.access_control import Allow, RolePrincipal, OHPermission
@ -39,7 +39,6 @@ class OverhaulScope(Base, DefaultMixin):
maintenance_type = relationship("MaintenanceType", lazy="selectin", backref="overhaul_scopes")
# activity_equipments = relationship("OverhaulActivity", lazy="selectin")
class MaintenanceType(Base, DefaultMixin):

@ -5,7 +5,7 @@ from fastapi import APIRouter, HTTPException, status
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import CommonParameters, search_filter_sort_paginate
from src.database.service import CommonParameters
from src.models import StandardResponse
from .model import OverhaulScope
@ -21,7 +21,6 @@ router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))])
@router.get("", response_model=StandardResponse[ScopePagination], dependencies=[Depends(required_permission(OHPermission.READ, OverhaulScope))])
async def get_scopes(common: CommonParameters, scope_name: Optional[str] = None):
"""Get all scope pagination."""
# return
results = await get_all(common=common, scope_name=scope_name)
return StandardResponse(

@ -1,9 +1,8 @@
from typing import Optional
from sqlalchemy import Delete, Select, func
from sqlalchemy import Select
from sqlalchemy.orm import selectinload
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import search_filter_sort_paginate
from src.overhaul_activity.model import OverhaulActivity
@ -102,7 +101,6 @@ async def create(*, db_session: DbSession, scope_in: ScopeCreate):
status=scope_in.status # Hardcoded status
)
# raise Exception(overhaul_session.start_date)
db_session.add(overhaul_session)
# Need to flush to get the id

@ -1,10 +1 @@
from slowapi import Limiter
from slowapi.util import get_remote_address
from src.config import RATELIMIT_STORAGE_URI
# limiter = Limiter(
# key_func=get_remote_address,
# default_limits=["120 per hour", "2880 per day"],
# strategy="fixed-window",
# storage_uri=RATELIMIT_STORAGE_URI,
# )

@ -1,10 +1,8 @@
from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String, Date
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.workorder.model import MasterWorkOrder
from src.models import DefaultMixin
class MasterSparePart(Base, DefaultMixin):

@ -1,7 +1,7 @@
from fastapi import APIRouter, Depends, HTTPException, Query, status
from fastapi import APIRouter, Depends
from src.csrf_protect import csrf_protect
from src.database.core import CollectorDbSession
from src.database.service import (CommonParameters, DbSession, search_filter_sort_paginate)
from src.database.service import (DbSession)
from src.models import StandardResponse
from src.sparepart.schema import SparepartRemark
from src.auth.access_control import require_any_role, ALLOWED_ROLES
@ -13,7 +13,6 @@ router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))])
@router.get("", response_model=StandardResponse[list])
async def get_sparepart(collector_db_session:CollectorDbSession, db_session: DbSession):
"""Get all scope activity pagination."""
# return
data = await get_spareparts_paginated(db_session=db_session, collector_db_session=collector_db_session)
return StandardResponse(
@ -36,59 +35,35 @@ async def create_remark_route(collector_db_session:CollectorDbSession, db_sessio
# @router.post("", response_model=StandardResponse[ActivityMasterCreate])
# async def create_activity(db_session: DbSession, activity_in: ActivityMasterCreate):
# activity = await create(db_session=db_session, activty_in=activity_in)
# return StandardResponse(data=activity, message="Data created successfully")
# @router.get(
# "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster]
# )
# async def get_activity(db_session: DbSession, activity_id: str):
# activity = await get(db_session=db_session, activity_id=activity_id)
# if not activity:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail="A data with this id does not exist.",
# )
# return StandardResponse(data=activity, message="Data retrieved successfully")
# @router.put(
# "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster]
# )
# async def update_scope(
# db_session: DbSession, activity_in: ActivityMasterCreate, activity_id
# ):
# activity = await get(db_session=db_session, activity_id=activity_id)
# if not activity:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail="A data with this id does not exist.",
# )
# return StandardResponse(
# data=await update(
# db_session=db_session, activity=activity, activity_in=activity_in
# ),
# message="Data updated successfully",
# )
# @router.delete(
# "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster]
# )
# async def delete_scope(db_session: DbSession, activity_id: str):
# activity = await get(db_session=db_session, activity_id=activity_id)
# if not activity:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=[{"msg": "A data with this id does not exist."}],
# )
# await delete(db_session=db_session, activity_id=activity_id)
# return StandardResponse(message="Data deleted successfully", data=activity)

@ -1,10 +1,9 @@
from dataclasses import dataclass
from datetime import date, datetime
from datetime import date
from enum import Enum
from typing import Any, Dict, List, Optional
from typing import List
from uuid import UUID
from pydantic import BaseModel, Field
from src.models import DefultBase, Pagination

@ -1,23 +1,12 @@
from typing import Optional
import asyncio
import logging
from datetime import datetime, timedelta, date
from typing import List, Dict, Optional, Tuple
from typing import List, Dict, Tuple
from collections import defaultdict
from uuid import UUID
import numpy as np
from dataclasses import dataclass
from enum import Enum
from sqlalchemy import Delete, Select, select, text
from sqlalchemy.orm import joinedload, selectinload
from sqlalchemy import select, text
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import CommonParameters, search_filter_sort_paginate
from src.overhaul_activity.service import get_standard_scope_by_session_id
from src.overhaul_scope.service import get as get_scope, get_overview_overhaul
from src.overhaul_scope.service import get_prev_oh
from src.overhaul_scope.service import get_overview_overhaul
from src.sparepart.model import SparepartRemark
from src.sparepart.schema import ProcurementRecord, ProcurementStatus, SparepartRequirement, SparepartStock
from src.soft_delete import apply_not_deleted_filter
@ -26,7 +15,6 @@ from src.soft_delete import apply_not_deleted_filter
log = logging.getLogger(__name__)
from sqlalchemy import text
import math
from sqlalchemy import text
@ -41,8 +29,6 @@ from sqlalchemy import text
# """
# # Normalize parent_num to array for SQL ANY()
# # parent_nums = parent_num if isinstance(parent_num, (list, tuple)) else [parent_num]
# parent_nums = []
# data_query = text("""
# WITH selected_wo AS (
@ -154,12 +140,10 @@ from sqlalchemy import text
# 'po_vendeliverydate', po_vendeliverydate,
# 'po_receipt', po_receipt,
# 'po_status', po_status
# )
# ORDER BY pr_issue_date DESC
# ) AS pr_po_details
# FROM pr_po_unified
# GROUP BY item_num
# )
# SELECT
# wm.itemnum,
@ -181,22 +165,10 @@ from sqlalchemy import text
# ORDER BY wm.itemnum;
# """)
# rows = await collector_db_session.execute(data_query, {"parent_nums": parent_nums})
# spare_parts = []
# for row in rows:
# spare_parts.append({
# "item_num": row.itemnum,
# "description": row.item_description,
# "current_balance_total": float(row.current_balance_total or 0.0),
# "total_required_for_oh": float(row.total_required_for_oh or 0.0),
# "total_pr_qty": row.total_pr_qty,
# "total_po_qty": row.total_po_qty,
# "total_po_received": row.total_po_received,
# "pr_po_details": row.pr_po_details,
# })
# return spare_parts
async def get_spareparts_paginated(*, db_session, collector_db_session):
@ -210,8 +182,6 @@ async def get_spareparts_paginated(*, db_session, collector_db_session):
items_per_page (int): Number of items per page
"""
# calculate limit/offset
# limit = items_per_page
# offset = (page - 1) * items_per_page
# wo_materials AS (
# SELECT
@ -918,15 +888,11 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s
"""Load sparepart data from database"""
# You'll need to implement these queries based on your database schema
# Get scope dates for analysis window
# scope = await get_scope(db_session=db_session, overhaul_session_id=overhaul_session_id)
# prev_oh_scope = await get_prev_oh(db_session=db_session, overhaul_session=scope)
analysis_start_date = prev_oh_scope.end_date
analysis_window_months = int(((scope.start_date - prev_oh_scope.end_date).days / 30) * 1.2) if not analysis_window_months else analysis_window_months
sparepart_manager = SparepartManager(analysis_start_date, analysis_window_months)
start_date = prev_oh_scope.end_date
end_date = scope.start_date
# Load sparepart stocks
# Example query - adjust based on your schema
@ -963,7 +929,6 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s
)
sparepart_manager.add_sparepart_stock(stock)
# parent_nums = []
# query = text("""
# WITH target_wo AS (
@ -1039,17 +1004,14 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s
# item_num,
# ROUND(CAST(AVG(
# EXTRACT(EPOCH FROM (
# COALESCE(vendeliverydate, estimated_arrival_date) - issue_date
# )) / 86400 / 30.44
# ) AS NUMERIC), 1) as avg_leadtime_months,
# ROUND(CAST(MIN(
# EXTRACT(EPOCH FROM (
# COALESCE(vendeliverydate, estimated_arrival_date) - issue_date
# )) / 86400 / 30.44
# ) AS NUMERIC), 1) as min_leadtime_months,
# ROUND(CAST(MAX(
# EXTRACT(EPOCH FROM (
# COALESCE(vendeliverydate, estimated_arrival_date) - issue_date
# )) / 86400 / 30.44
# ) AS NUMERIC), 1) as max_leadtime_months,
# COUNT(*) as leadtime_sample_size,
@ -1083,7 +1045,6 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s
# ) as description
# FROM public.maximo_sparepart_pr_po_line
# WHERE description IS NOT NULL
# )
# SELECT
# tr.asset_location,
# tr.itemnum,
@ -1100,22 +1061,10 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s
# ORDER BY tr.asset_location, tr.itemnum;
# """)
# equipment_requirements_query = await db_session.execute(query, {"parent_nums": parent_nums})
# equipment_requirements = defaultdict(list)
# for req_record in equipment_requirements_query:
# requirement = SparepartRequirement(
# sparepart_id=req_record.itemnum,
# quantity_required=float(req_record.total_required_for_oh or 0.0),
# lead_time=float(req_record.avg_leadtime_months or 0.0),
# sparepart_name=req_record.item_description,
# unit_cost=float(req_record.avg_unit_cost or 0.0),
# avg_cost=float(req_record.inv_avgcost or 0.0),
# )
# equipment_requirements[req_record.asset_location].append(requirement)
# for equipment_tag, requirements in equipment_requirements.items():
# sparepart_manager.add_equipment_requirements(equipment_tag, requirements)
# Load equipment sparepart requirements
# You'll need to create this table/relationship

@ -1,10 +1,8 @@
from sqlalchemy import UUID, Column, Date, Float, ForeignKey, Integer, String, Boolean
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.workorder.model import MasterWorkOrder
from src.models import DefaultMixin
class StandardScope(Base, DefaultMixin):

@ -1,18 +1,16 @@
from typing import List, Optional
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends
from fastapi.params import Query
from src.auth.service import CurrentUser
from src.database.core import DbSession, CollectorDbSession
from src.database.service import CommonParameters, search_filter_sort_paginate
from src.database.service import CommonParameters
from src.models import StandardResponse
from src.auth.access_control import require_any_role, ALLOWED_ROLES
from .schema import (MasterEquipmentPagination, ScopeEquipmentCreate,
ScopeEquipmentPagination, ScopeEquipmentRead,
ScopeEquipmentUpdate)
from .service import (create, delete, get_all, get_all_master_equipment, update, get_history_standard_scope_wo_service)
ScopeEquipmentPagination)
from .service import (create, get_all, get_all_master_equipment, get_history_standard_scope_wo_service)
from uuid import UUID
router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))])
@ -20,7 +18,6 @@ router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))])
@router.get("", response_model=StandardResponse[ScopeEquipmentPagination])
async def get_scope_equipments(common: CommonParameters, scope_name: str = Query(None)):
"""Get all scope pagination."""
# return
data = await get_all(common=common, oh_scope=scope_name)
return StandardResponse(
@ -59,34 +56,18 @@ async def get_history_standard_scope_wo(
# async def update_scope_equipment(
# db_session: DbSession, assetnum: str, scope__equipment_in: ScopeEquipmentUpdate
# ):
# scope_equipment = await get_by_assetnum(db_session=db_session, assetnum=assetnum)
# if not scope_equipment:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail="A data with this id does not exist.",
# )
# return StandardResponse(
# data=await update(
# db_session=db_session,
# scope_equipment=scope_equipment,
# scope__equipment_in=scope__equipment_in,
# ),
# message="Data updated successfully",
# )
# @router.delete("/{assetnum}", response_model=StandardResponse[None])
# async def delete_scope_equipment(db_session: DbSession, assetnum: str):
# scope_equipment = await get_by_assetnum(db_session=db_session, assetnum=assetnum)
# if not scope_equipment:
# raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail=[{"msg": "A data with this id does not exist."}],
# )
# await delete(db_session=db_session, assetnum=assetnum)
# return StandardResponse(message="Data deleted successfully", data=None)

@ -2,10 +2,9 @@ from datetime import datetime
from typing import List, Optional, ForwardRef
from uuid import UUID
from pydantic import Field, computed_field, field_validator, validator
from pydantic import Field
from src.models import DefultBase, Pagination
from src.overhaul_scope.schema import ScopeRead
from .enum import ScopeEquipmentType

@ -1,20 +1,18 @@
from datetime import datetime, timedelta
from typing import Optional, Union
from typing import Optional
from fastapi import HTTPException, status
from sqlalchemy import Delete, Select, and_, desc, func, not_, or_
from sqlalchemy import Select, desc
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.orm import selectinload
from src.auth.service import CurrentUser
from src.database.core import DbSession, CollectorDbSession
from src.utils import update_model
from src.database.service import CommonParameters, search_filter_sort_paginate
from src.overhaul_scope.model import OverhaulScope
from src.standard_scope.enum import ScopeEquipmentType
from src.standard_scope.model import EquipmentOHHistory
from src.soft_delete import apply_not_deleted_filter, soft_delete_record
from src.workorder.model import MasterWorkOrder
from src.soft_delete import apply_not_deleted_filter
from src.equipment_workscope_group.model import EquipmentWorkscopeGroup
from src.workscope_group.model import MasterActivity
from src.workscope_group_maintenance_type.model import WorkscopeOHType
@ -61,7 +59,6 @@ async def get_all(*, common, oh_scope: Optional[str] = None):
# .filter(
# (StandardScope.is_alternating_oh == False) |
# (StandardScope.oh_history == None) |
# (StandardScope.oh_history.has(EquipmentOHHistory.last_oh_type != selected_overhaul.maintenance_type.name))
# ).distinct()
)
@ -100,7 +97,6 @@ async def get_by_oh_session_id(*, db_session: DbSession, oh_session_id: UUID):
async def create(*, db_session: DbSession, scope_equipment_in: ScopeEquipmentCreate):
"""Creates a new document."""
# scope_equipment = StandardScope(**scope_equipment_in.model_dump())
assetnums = scope_equipment_in.assetnums
results = []
removal_date = scope_equipment_in.removal_date
@ -204,7 +200,6 @@ async def get_by_oh_scope(
# .filter(
# (StandardScope.is_alternating_oh == False) |
# (StandardScope.oh_history == None) |
# (StandardScope.oh_history.has(EquipmentOHHistory.last_oh_type != selected_overhaul.maintenance_type.name))
# ).distinct()
)

@ -6,7 +6,6 @@ Run with:
pytest src/testing/integration_test.py -v
"""
import pytest
import requests
BASE_URL = "http://100.125.115.116:8000/optimumoh"
@ -22,17 +21,13 @@ HEADERS = {
# Placeholder IDs — replace with real values from your database before running
OVERHAUL_SESSION_ID = "3704c82c-cfc5-47f7-813a-1661f89b0738"
# OVERHAUL_EQUIPMENT_ID = 1 ## Source code got commented
SCOPE_JOB_ID = "db590764-72cb-4eb1-a30f-014116df0472"
CALCULATION_ID = "44f483f3-bfe4-4094-a59f-b97a10f2fea6"
BUDGET_THRESHOLD = 100000
RISK_COST = 1
LOCATION_TAG = "3AL-F501A"
SCOPE_NAME = "A"
# OVERHAUL_SESSION_HISTORY_ID = 1
ASSET_NUM = "A19825"
# SCOPE_EQUIPMENT_JOB_ID = 1 ## Source code got commented
# EQUIPMENT_ACTIVITY_ID = 1 ## Source code got commented
EAF_INPUT = 0.85
DURATION = 17520
SCOPE_EQUIPMENT_ACTIVITY_ID = "4527eb2b-0bc4-46b3-80a1-e1d2f67882f8"
@ -102,8 +97,6 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# def test_get_overhaul_gantt():
# """GET /overhaul-gantt — no role guard; all roles allowed"""
# for user in USER_LIST:
# res = requests.get(f"{BASE_URL}/overhaul-gantt", headers=user_auth_headers(user["authorization_token"]))
# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}"
@ -113,30 +106,13 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # [[Source code got commented]]
# def test_get_overhaul_session():
# """GET /overhaul-session?all=1 — require_any_role blocks Management"""
# for user in USER_LIST:
# res = requests.get(f"{BASE_URL}/overhaul-session?all=1", headers=user_auth_headers(user["authorization_token"]))
# expected = 200 if user["name"] in NON_MANAGEMENT else 403
# print(f"[{user['name']}] expected {expected}, got {res.status_code}, response: {res.text}")
# assert res.status_code == expected, f"[{user['name']}] expected {expected}, got {res.status_code}"
# # [[Source code got commented]]
# def test_post_overhaul_session():
# """POST /overhaul-session — require_any_role + CREATE blocks Management"""
# payload = {
# "type": "Major",
# "start_date": "2025-01-01",
# "end_date": "2025-12-31",
# }
# for user in USER_LIST:
# csrf_token = get_csrf_token("/overhaul-session", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(f"{BASE_URL}/overhaul-session", json=payload, headers=headers)
# print(f"[{user['name']}], got {res.status_code}, response: {res.text}")
# if user["name"] in NON_MANAGEMENT:
# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}"
# else:
# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}"
# # ─────────────────────────────────────────────
# # Overhaul Schedules
@ -145,39 +121,15 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # [[Attribute error]]
# def test_post_overhaul_schedules():
# """POST /overhaul-schedules — require_any_role + CREATE blocks Management"""
# payload = {
# "start_date": "2024-06-01T00:00:00Z",
# "end_date": "2024-06-30T23:59:59Z",
# "duration_oh": 720,
# "crew_number": 10,
# "status": "Upcoming"
# }
# for user in USER_LIST:
# print(f"Testing payload: {payload}, headers: {user_auth_headers(user['authorization_token'])}")
# csrf_token = get_csrf_token("/overhaul-schedules", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(f"{BASE_URL}/overhaul-schedules", json=payload, headers=headers)
# print(f"[{user['name']}], got {res.status_code}, response: {res.text}")
# if user["name"] in NON_MANAGEMENT:
# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}"
# else:
# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}"
# # [[Source code got commented]]
# def test_delete_overhaul_schedule():
# """POST /overhaul-schedules/delete/{scope_id} — require_any_role + DELETE blocks Management. CSRF required."""
# for user in USER_LIST:
# csrf_token = get_csrf_token("/overhaul-schedules/delete", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(
# f"{BASE_URL}/overhaul-schedules/delete/{OVERHAUL_SCHEDULE_ID}",
# headers=headers,
# )
# if user["name"] in NON_MANAGEMENT:
# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}"
# else:
# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}"
@ -188,19 +140,7 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # [[Source code got commented]]
# def test_post_overhaul_jobs():
# """POST /overhaul-jobs/{overhaul_equipment_id} — no role guard; all roles allowed"""
# payload = {
# "job_ids": [],
# }
# for user in USER_LIST:
# csrf_token = get_csrf_token("/overhaul-jobs", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(
# f"{BASE_URL}/overhaul-jobs/{SCOPE_JOB_ID}",
# json=payload,
# headers=headers,
# )
# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}"
# ─────────────────────────────────────────────
@ -210,32 +150,14 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # [[Database error: Unconsumed column names: scope_overhaul, assetnum, type, removal_date]]
# def test_post_scope_equipments():
# """POST /scope-equipments — require_any_role blocks Management"""
# payload = {
# "assetnums": ["A26312", "A26313", "A26314"],
# "scope_name": "A"
# }
# for user in USER_LIST:
# csrf_token = get_csrf_token("/scope-equipments", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(f"{BASE_URL}/scope-equipments", json=payload, headers=headers)
# print(f"[{user['name']}], got {res.status_code}, response: {res.text}")
# if user["name"] in NON_MANAGEMENT:
# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}"
# else:
# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}"
# # [[No endpoint exists]]
# def test_delete_scope_equipment():
# """DELETE /scope-equipments/delete/{id} — require_any_role blocks Management"""
# for user in USER_LIST:
# res = requests.delete(
# f"{BASE_URL}/scope-equipments/delete/{OVERHAUL_EQUIPMENT_ID}",
# headers=user_auth_headers(user["authorization_token"]),
# )
# if user["name"] in NON_MANAGEMENT:
# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}"
# else:
# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}"
# # ─────────────────────────────────────────────
@ -244,31 +166,11 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # [[Source code got commented]]
# def test_post_scope_equipment_jobs():
# """POST /scope-equipment-jobs/{assetnum} — no role guard; all roles allowed"""
# payload = {
# "job_ids": [],
# }
# for user in USER_LIST:
# csrf_token = get_csrf_token("/scope-equipment-jobs", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(
# f"{BASE_URL}/scope-equipment-jobs/{ASSET_NUM}",
# json=payload,
# headers=headers,
# )
# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}"
# # [[Source code got commented]]
# def test_delete_scope_equipment_job():
# """POST /equipment-workscopes/delete/{scope_job_id} — no role guard; all roles allowed"""
# for user in USER_LIST:
# csrf_token = get_csrf_token("/equipment-workscopes/delete", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(
# f"{BASE_URL}/equipment-workscopes/delete/{SCOPE_EQUIPMENT_JOB_ID}",
# headers=headers,
# )
# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}"
# # ─────────────────────────────────────────────
# # Overhaul Jobs
@ -276,43 +178,16 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # [[Source code got commented]]
# def test_get_overhaul_jobs():
# """GET /overhaul-jobs/{overhaul_equipment_id} — no role guard; all roles allowed"""
# for user in USER_LIST:
# res = requests.get(
# f"{BASE_URL}/overhaul-jobs/{OVERHAUL_EQUIPMENT_ID}",
# headers=user_auth_headers(user["authorization_token"]),
# )
# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}"
# # ─────────────────────────────────────────────
# # Calculation — Budget Constraint
# # ─────────────────────────────────────────────
# # [[AttributeError]]
# def test_post_calculation_time_constraint():
# """POST /calculation/time-constraint — required_permission(CREATE); Management has no CREATE"""
# payload = {
# "ohSessionId": OVERHAUL_SESSION_ID,
# "costPerFailure": 0,
# "overhaulCost": 0,
# }
# params = {
# "with_results": 0,
# }
# for user in USER_LIST:
# csrf_token = get_csrf_token("/calculation/time-constraint", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(
# f"{BASE_URL}/calculation/time-constraint",
# json=payload,
# params=params,
# headers=headers,
# )
# if user["name"] in NON_MANAGEMENT:
# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}"
# else:
# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}"
# # ─────────────────────────────────────────────
@ -324,18 +199,7 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# """POST /equipment-activities — no role guard; all roles allowed
# Note: route commented out in api.py; active equivalent is POST /overhaul-activity/{session_id}
# """
# payload = {
# "location_tags": [LOCATION_TAG],
# }
# for user in USER_LIST:
# csrf_token = get_csrf_token("/equipment-activities", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(
# f"{BASE_URL}/equipment-activities",
# json=payload,
# headers=headers,
# )
# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}"
# # [[Source code got commented]]
@ -344,13 +208,6 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# Note: route commented out in api.py; active equivalent is POST /overhaul-activity/delete/{session}/{location_tag}
# """
# for user in USER_LIST:
# csrf_token = get_csrf_token("/equipment-activities/delete", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(
# f"{BASE_URL}/equipment-activities/delete/{EQUIPMENT_ACTIVITY_ID}",
# headers=headers,
# )
# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}"
# # ─────────────────────────────────────────────
@ -361,29 +218,17 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# def test_get_jobs():
# """GET /jobs — no role guard; all roles allowed"""
# for user in USER_LIST:
# res = requests.get(f"{BASE_URL}/jobs", headers=user_auth_headers(user["authorization_token"]))
# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}"
# ─────────────────────────────────────────────
# Overhauls sub-routes
# ─────────────────────────────────────────────
# # [[TypeError]]
# def test_get_overhauls_schedules():
# """GET /overhauls/schedules — required_permission(READ); Management has READ"""
# for user in USER_LIST:
# res = requests.get(f"{BASE_URL}/overhauls/schedules", headers=user_auth_headers(user["authorization_token"]))
# print(f"[{user['name']}], got {res.status_code}, response: {res.text}")
# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}: {res.text}"
# # [[TypeError]]
# def test_get_overhauls_critical_parts():
# """GET /overhauls/critical-parts — required_permission(READ); Management has READ"""
# for user in USER_LIST:
# res = requests.get(f"{BASE_URL}/overhauls/critical-parts", headers=user_auth_headers(user["authorization_token"]))
# print(f"[{user['name']}], got {res.status_code}, response: {res.text}")
# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}: {res.text}"
# # ─────────────────────────────────────────────
@ -392,113 +237,50 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # [[Attribute Error]]
# def test_get_overhaul_activity_by_assetnum():
# """GET /overhaul-activity/{overhaul_session}/{assetnum} — require_any_role blocks Management"""
# for user in USER_LIST:
# res = requests.get(
# f"{BASE_URL}/overhaul-activity/{OVERHAUL_SESSION_ID}/{ASSET_NUM}",
# headers=user_auth_headers(user["authorization_token"]),
# )
# expected = 200 if user["name"] in NON_MANAGEMENT else 403
# assert res.status_code == expected, f"[{user['name']}] expected {expected}, got {res.status_code}: {res.text}"
# # ─────────────────────────────────────────────
# # Workscopes
# # ─────────────────────────────────────────────
# # [[TypeError]]
# def test_post_workscopes():
# """POST /workscopes — no role guard; all roles allowed
# Payload: workscope (str)
# """
# payload = {
# # "workscope": "Test workscope description",
# # "system": "Test system",
# # "subsystem": "Test subsystem",
# # "tasks": "Test task",
# # "equipment_workscope_groups": "Test group",
# # "oh_types": "Test OH type",
# "description": "Test description",
# # "job_id": SCOPE_JOB_ID,
# }
# for user in USER_LIST:
# csrf_token = get_csrf_token("/workscopes", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(f"{BASE_URL}/workscopes", json=payload, headers=headers)
# print(f"[{user['name']}], got {res.status_code}, response: {res.text}")
# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}"
# ─────────────────────────────────────────────
# Equipment Workscopes
# ─────────────────────────────────────────────
# # [[NameError]]
# def test_post_equipment_workscopes_delete():
# """POST /equipment-workscopes/delete/{scope_job_id} — no role guard; all roles allowed
# No body required.
# """
# for user in USER_LIST:
# csrf_token = get_csrf_token("/equipment-workscopes/delete", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(
# f"{BASE_URL}/equipment-workscopes/delete/{SCOPE_JOB_ID}",
# headers=headers,
# )
# print(f"[{user['name']}], got {res.status_code}, response: {res.text}")
# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}"
# # ─────────────────────────────────────────────
# # Calculation — Time Constraint
# # ─────────────────────────────────────────────
# # [[AttributeError]]
# def test_post_calculation_time_constraint():
# """POST /calculation/time-constraint — required_permission(CREATE); Management has no CREATE
# Payload: overhaulCost (float), ohSessionId (UUID), costPerFailure (float)
# Query params: scope_calculation_id (optional), with_results (optional int), simulation_id (optional)
# """
# payload = {
# "overhaulCost": 0,
# "ohSessionId": OVERHAUL_SESSION_ID,
# "costPerFailure": 0,
# }
# for user in USER_LIST:
# csrf_token = get_csrf_token("/calculation/time-constraint", user["authorization_token"])
# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token}
# res = requests.post(
# f"{BASE_URL}/calculation/time-constraint",
# json=payload,
# headers=headers,
# )
# print(f"[{user['name']}], got {res.status_code}, response: {res.text}")
# if user["name"] in NON_MANAGEMENT:
# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}"
# else:
# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}"
# # [[AttributeError]]
# def test_get_calculation_time_constraint_parameters():
# """GET /calculation/time-constraint/parameters — required_permission(READ); Management has READ
# Query params: calculation_id (optional)
# """
# for user in USER_LIST:
# res = requests.get(
# f"{BASE_URL}/calculation/time-constraint/parameters",
# headers=user_auth_headers(user["authorization_token"]),
# )
# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}: {res.text}"
# # [[AttributeError]]
# def test_get_calculation_time_constraint_by_id_and_assetnum():
# """GET /calculation/time-constraint/{calculation_id}/{assetnum} — required_permission(READ); Management has READ"""
# for user in USER_LIST:
# res = requests.get(
# f"{BASE_URL}/calculation/time-constraint/{CALCULATION_ID}/{ASSET_NUM}",
# headers=user_auth_headers(user["authorization_token"]),
# )
# assert res.status_code in (200, 404), f"[{user['name']}] expected 200/404, got {res.status_code}: {res.text}"
@ -507,15 +289,8 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # Calculation — Budget Constraint
# # ─────────────────────────────────────────────
# # [[AttributeError]]
# def test_get_calculation_budget_constraint():
# """GET /calculation/budget-constraint/{session_id} — require_any_role blocks Management
# Query params: cost_threshold (float, default 100)
# """
# for user in USER_LIST:
# res = requests.get(
# f"{BASE_URL}/calculation/budget-constraint/{OVERHAUL_SESSION_ID}?cost_threshold={BUDGET_THRESHOLD}",
# headers=user_auth_headers(user["authorization_token"]),
# )
# expected = 200 if user["name"] in NON_MANAGEMENT else 403
# assert res.status_code == expected, f"[{user['name']}] expected {expected}, got {res.status_code}: {res.text}"

@ -6,7 +6,6 @@ Run with:
pytest src/testing/integration_test.py -v
"""
import pytest
import requests
BASE_URL = "http://100.125.115.116:8000/optimumoh"

@ -1,5 +1,5 @@
import re
from datetime import datetime, timedelta, timezone
from datetime import datetime, timedelta
from typing import Optional
import pytz

@ -1,8 +1,7 @@
from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from sqlalchemy import Column, Float, String
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.models import DefaultMixin
class MasterWorkOrder(Base, DefaultMixin):
@ -13,8 +12,4 @@ class MasterWorkOrder(Base, DefaultMixin):
workgroup = Column(String, nullable=True)
total_cost_max = Column(Float, nullable=True)
# scope_equipments = relationship(
# "ScopeEquipment",
# lazy="raise",
# primaryjoin="and_(MasterWorkOrder.assetnum == foreign(ScopeEquipment.assetnum))",
# )

@ -1,10 +1,8 @@
from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy import UUID, Column, ForeignKey, String
from sqlalchemy.orm import relationship
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.workorder.model import MasterWorkOrder
from src.models import DefaultMixin
class MasterActivitytask(Base, DefaultMixin):
@ -45,15 +43,8 @@ class MasterActivity(Base, DefaultMixin):
back_populates="workscope_group",
)
# details = relationship(
# "MasterActivityDetail",
# lazy="raise",
# primaryjoin="and_(MasterActivity.id == foreign(MasterActivityDetail.activity_id))",
# )
# class MasterActivityDetail(Base, DefaultMixin):
# __tablename__ = "oh_ms_activity_detail"
# name = Column(String, nullable=False)
# activity_id = Column(UUID(as_uuid=True))

@ -1,7 +1,6 @@
from fastapi import APIRouter, HTTPException, Query, status
from fastapi import APIRouter, HTTPException, status
from src.database.service import (CommonParameters, DbSession,
search_filter_sort_paginate)
from src.database.service import (CommonParameters, DbSession)
from src.models import StandardResponse
from .schema import (ActivityMaster, ActivityMasterCreate,
@ -14,7 +13,6 @@ router = APIRouter()
@router.get("", response_model=StandardResponse[ActivityMasterPagination])
async def get_activities(common: CommonParameters):
"""Get all scope activity pagination."""
# return
data = await get_all(common=common)

@ -1,8 +1,6 @@
from datetime import datetime
from typing import Any, Dict, List, Optional
from typing import List
from uuid import UUID
from pydantic import BaseModel, Field
from src.models import DefultBase, Pagination
@ -44,40 +42,19 @@ class ActivityMasterPagination(Pagination):
items: List[ActivityMasterRead] = []
# {
# "overview": {
# "totalEquipment": 30,
# "nextSchedule": {
# "date": "2025-01-12",
# "Overhaul": "B",
# "equipmentCount": 30
# }
# },
# "criticalParts": [
# "Boiler feed pump",
# "Boiler reheater system",
# "Drum Level (Right) Root Valve A",
# "BCP A Discharge Valve",
# "BFPT A EXH Press HI Root VLV"
# ],
# "schedules": [
# {
# "date": "2025-01-12",
# "Overhaul": "B",
# "status": "upcoming"
# }
# // ... other scheduled overhauls
# ],
# "systemComponents": {
# "boiler": {
# "status": "operational",
# "lastOverhaul": "2024-06-15"
# },
# "turbine": {
# "hpt": { "status": "operational" },
# "ipt": { "status": "operational" },
# "lpt": { "status": "operational" }
# }
# // ... other major components
# }
# }

@ -1,10 +1,8 @@
from typing import Optional
from fastapi import HTTPException, status
from sqlalchemy import Delete, Select
from sqlalchemy.orm import joinedload, selectinload
from sqlalchemy import Select
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import CommonParameters, search_filter_sort_paginate
from src.utils import update_model

@ -1,10 +1,7 @@
from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy import UUID, Column, ForeignKey
from sqlalchemy.orm import relationship
from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin
from src.workorder.model import MasterWorkOrder
class WorkscopeOHType(Base):

@ -1,10 +1,8 @@
from typing import Optional
from fastapi import HTTPException, status
from sqlalchemy import Delete, Select
from sqlalchemy.orm import joinedload, selectinload
from sqlalchemy import Select
from src.auth.service import CurrentUser
from src.database.core import DbSession
from src.database.service import CommonParameters, search_filter_sort_paginate
from src.utils import update_model

Loading…
Cancel
Save