chore: clear all unused imports and variables

oh_security
Cizz22 4 weeks ago
parent 95ae35c0fe
commit 0ecae96664

@ -0,0 +1 @@
tests/

3
.gitignore vendored

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

@ -12,32 +12,18 @@ from src.calculation_target_reliability.router import \
router as calculation_target_reliability router as calculation_target_reliability
from src.calculation_time_constrains.router import \ from src.calculation_time_constrains.router import \
router as calculation_time_constrains_router, get_calculation 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.overhaul.router import router as overhaul_router
from src.standard_scope.router import router as standard_scope_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.overhaul_activity.router import router as overhaul_activity_router
from src.workscope_group.router import router as workscope_group_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.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.overhaul_gantt.router import router as gantt_router
from src.sparepart.router import router as sparepart_router from src.sparepart.router import router as sparepart_router
from src.equipment_sparepart.router import router as equipment_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.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): 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 # # # Overhaul session data
# authenticated_api_router.include_router( # authenticated_api_router.include_router(
# scope_router, prefix="/overhaul-session", tags=["overhaul-session"] # scope_router, prefix="/overhaul-session", tags=["overhaul-session"]
# )
authenticated_api_router.include_router( authenticated_api_router.include_router(
standard_scope_router, prefix="/scope-equipments", tags=["scope_equipment"] standard_scope_router, prefix="/scope-equipments", tags=["scope_equipment"]
@ -110,19 +94,12 @@ authenticated_api_router.include_router(
) )
# authenticated_api_router.include_router( # authenticated_api_router.include_router(
# scope_equipment_job_router, # scope_equipment_job_router,
# prefix="/scope-equipment-jobs",
# tags=["scope_equipment", "job"],
# )
# authenticated_api_router.include_router( # authenticated_api_router.include_router(
# overhaul_schedule_router, # overhaul_schedule_router,
# prefix="/overhaul-schedules",
# tags=["overhaul_schedule"],
# )
# authenticated_api_router.include_router( # authenticated_api_router.include_router(
# job_overhaul_router, prefix="/overhaul-jobs", tags=["job", "overhaul"] # job_overhaul_router, prefix="/overhaul-jobs", tags=["job", "overhaul"]
# )
authenticated_api_router.include_router( authenticated_api_router.include_router(
gantt_router, prefix="/overhaul-gantt", tags=["gantt"] gantt_router, prefix="/overhaul-gantt", tags=["gantt"]
@ -130,19 +107,15 @@ authenticated_api_router.include_router(
# authenticated_api_router.include_router( # authenticated_api_router.include_router(
# overhaul_history_router, prefix="/overhaul-history", tags=["overhaul_history"] # overhaul_history_router, prefix="/overhaul-history", tags=["overhaul_history"]
# )
# authenticated_api_router.include_router( # authenticated_api_router.include_router(
# scope_equipment_activity_router, prefix="/equipment-activities", tags=["scope_equipment_activities"] # scope_equipment_activity_router, prefix="/equipment-activities", tags=["scope_equipment_activities"]
# )
# authenticated_api_router.include_router( # authenticated_api_router.include_router(
# activity_router, prefix="/activities", tags=["activities"] # activity_router, prefix="/activities", tags=["activities"]
# )
# authenticated_api_router.include_router( # authenticated_api_router.include_router(
# scope_equipment_part_router, prefix="/equipment-parts", tags=["scope_equipment_parts"] # scope_equipment_part_router, prefix="/equipment-parts", tags=["scope_equipment_parts"]
# )
authenticated_api_router.include_router( authenticated_api_router.include_router(
ovehaul_schedule_router, prefix="/overhaul-schedules", tags=["overhaul_schedules"] ovehaul_schedule_router, prefix="/overhaul-schedules", tags=["overhaul_schedules"]

@ -1,12 +1,11 @@
# app/auth/auth_bearer.py # app/auth/auth_bearer.py
import json import json
from typing import Annotated, Optional from typing import Annotated
import requests import requests
from fastapi import Depends, HTTPException, Request from fastapi import Depends, HTTPException, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy.sql.expression import false
import src.config as config 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 fastapi.params import Query
from src.auth.service import Token from src.auth.service import Token

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

@ -1,17 +1,11 @@
from collections import defaultdict from collections import defaultdict
import math import math
import random
from typing import Optional, List, Dict, Tuple from typing import Optional, List, Dict, Tuple
from uuid import UUID 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.database.core import CollectorDbSession, DbSession
# from src.scope_equipment.model import ScopeEquipment from src.overhaul_activity.service import get_standard_scope_by_session_id
# 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
# async def get_all_budget_constrains( # async def get_all_budget_constrains(
# *, db_session: DbSession, session_id: str, cost_threshold: float = 100000000 # *, 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.database.core import CollectorDbSession, DbSession
from src.overhaul_activity.service import get_standard_scope_by_session_id 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( async def get_all_budget_constrains(
@ -84,7 +77,7 @@ async def get_all_budget_constrains(
# Calculate efficiency and composite score # Calculate efficiency and composite score
for item in result: for item in result:
cost = item["total_cost"] or 1.0 cost = item["total_cost"] or 1.0
efficiency = item["contribution_norm"] / cost item["contribution_norm"] / cost
item["priority_score"] = item["contribution_norm"] item["priority_score"] = item["contribution_norm"]
# Choose method # Choose method

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

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

@ -1,20 +1,9 @@
import math import math
from typing import Optional, List
from dataclasses import dataclass
from sqlalchemy import Delete, Select
import httpx import httpx
from src.auth.service import CurrentUser
from src.config import RBD_SERVICE_API 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 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 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 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" 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" calc_plant_result = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}/plant"
async with httpx.AsyncClient(timeout=300.0) as client: async with httpx.AsyncClient(timeout=300.0) as client:
calc_task = client.get(calc_result_url, headers=headers) 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) plant_task = client.get(calc_plant_result, headers=headers)
# Run all three requests concurrently # Run all three requests concurrently
calc_response, plant_response = await asyncio.gather(calc_task, plant_task) calc_response, plant_response = await asyncio.gather(calc_task, plant_task)
calc_response.raise_for_status() calc_response.raise_for_status()
# plot_response.raise_for_status()
plant_response.raise_for_status() plant_response.raise_for_status()
calc_data = calc_response.json()["data"] calc_data = calc_response.json()["data"]
# plot_data = plot_response.json()["data"]
plant_data = plant_response.json()["data"] plant_data = plant_response.json()["data"]
return { return {
"calc_result": calc_data, "calc_result": calc_data,
# "plot_result": plot_data,
"plant_result": plant_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_PERCENT = 0.0001
min_improvement_fraction = MIN_IMPROVEMENT_PERCENT / 100.0 min_improvement_fraction = MIN_IMPROVEMENT_PERCENT / 100.0
EPSILON = 0.001 # 1 ms or a fraction of an hour for comparison tolerance
results = [] results = []
weighted_assets = [] weighted_assets = []
@ -223,7 +206,7 @@ async def identify_worst_eaf_contributors(
# Target feasibility check # Target feasibility check
warning_message = None warning_message = None
if target_eaf > max_eaf_possible: 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_scheduled_hours = total_hours * (1 - target_eaf / 100)
required_reduction = reduced_outage - required_scheduled_hours required_reduction = reduced_outage - required_scheduled_hours

@ -1,10 +1,10 @@
import asyncio import asyncio
from datetime import datetime, timedelta from datetime import datetime, timedelta
import random import random
from typing import List, Optional from typing import Optional
from temporalio.client import Client 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, def generate_down_periods(start_date: datetime, end_date: datetime,
num_periods: Optional[int] = None, min_duration: int = 3, 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 failure_cost = (failure_prob * failure_replacement_cost) / expected_operating_time
preventive_cost = (reliability * preventive_cost) / expected_operating_time preventive_cost = (reliability * preventive_cost) / expected_operating_time
else: else:
# failure_cost = failure_prob * self.IDRu
# preventive_cost = reliability * self.IDRp
continue continue
@ -167,13 +165,13 @@ class OptimumCostModel:
Calculate optimization for entire fleet of equipment Calculate optimization for entire fleet of equipment
""" """
max_interval = self._get_months_between(start_date, end_date) max_interval = self._get_months_between(start_date, end_date)
preventive_cost_per_equipment = preventive_cost / len(equipments) preventive_cost / len(equipments)
fleet_results = [] fleet_results = []
total_corrective_costs = np.zeros(max_interval) total_corrective_costs = np.zeros(max_interval)
total_preventive_costs = np.zeros(max_interval) total_preventive_costs = np.zeros(max_interval)
total_procurement_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: for equipment in equipments:
# Get reliability data # Get reliability data

@ -1,12 +1,8 @@
from typing import Optional from typing import Optional
from uuid import UUID
import numpy as np
from fastapi import HTTPException, status from fastapi import HTTPException, status
from sqlalchemy import Select, func, select from sqlalchemy import func, select
from sqlalchemy.orm import joinedload
from src.auth.service import Token
from src.config import TC_RBD_ID from src.config import TC_RBD_ID
from src.database.core import DbSession from src.database.core import DbSession
from src.overhaul_scope.service import get_all from src.overhaul_scope.service import get_all
@ -15,13 +11,9 @@ from src.workorder.model import MasterWorkOrder
from .schema import (CalculationTimeConstrainsParametersCreate, from .schema import (CalculationTimeConstrainsParametersCreate,
CalculationTimeConstrainsParametersRead, CalculationTimeConstrainsParametersRead,
CalculationTimeConstrainsParametersRetrive, CalculationTimeConstrainsParametersRetrive)
CalculationTimeConstrainsRead) from .service import (create_param_and_data,
from .service import (create_calculation_result_service, create_param_and_data, get_calculation_data_by_id, run_simulation_with_spareparts)
get_avg_cost_by_asset,
get_calculation_by_reference_and_parameter,
get_calculation_data_by_id, get_calculation_result,
run_simulation_with_spareparts)
from src.database.core import CollectorDbSession from src.database.core import CollectorDbSession
@ -69,12 +61,6 @@ async def get_create_calculation_parameters(
costPerFailure=costFailurePerScope, costPerFailure=costFailurePerScope,
availableScopes=avaiableScopes.values(), availableScopes=avaiableScopes.values(),
recommendedScope="A", 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 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( 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 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 # Check if calculation already exist
# return CalculationTimeConstrainsRead( # 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 enum import Enum
from typing import List, Optional, Union from typing import Optional
from sqlalchemy import (JSON, UUID, Boolean, Column, Float, ForeignKey, from sqlalchemy import (JSON, UUID, Boolean, Column, Float, ForeignKey,
Integer, Numeric, String) Integer, String)
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from src.database.core import Base, DbSession 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): class OverhaulReferenceType(str, Enum):
@ -32,29 +32,12 @@ class CalculationParam(Base, DefaultMixin, IdentityMixin):
# overhaul_cost: float, # overhaul_cost: float,
# created_by: str, # created_by: str,
# # list of {"reference_type": OverhaulReferenceType, "reference_id": str} # # list of {"reference_type": OverhaulReferenceType, "reference_id": str}
# references: List[dict]
# ): # ):
# # Create parameter # # 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 # # Create reference links
# for ref in references: # 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): class CalculationData(Base, DefaultMixin, IdentityMixin):

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

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

@ -1,5 +1,5 @@
import datetime import datetime
from typing import Coroutine, List, Optional, Tuple,Dict from typing import List, Optional, Tuple,Dict
from uuid import UUID from uuid import UUID
import calendar import calendar
@ -14,11 +14,8 @@ from sqlalchemy.orm import joinedload, selectinload
from src.database.core import DbSession from src.database.core import DbSession
from src.overhaul_activity.service import get_all as get_all_by_session_id 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.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.sparepart.service import load_sparepart_data_from_db
from src.utils import get_latest_numOfFail
from src.workorder.model import MasterWorkOrder from src.workorder.model import MasterWorkOrder
from src.database.core import CollectorDbSession
from src.overhaul_activity.model import OverhaulActivity
from .model import (CalculationData, CalculationEquipmentResult, from .model import (CalculationData, CalculationEquipmentResult,
CalculationResult) CalculationResult)
from .schema import (CalculationResultsRead, from .schema import (CalculationResultsRead,
@ -26,21 +23,15 @@ from .schema import (CalculationResultsRead,
CalculationTimeConstrainsParametersCreate, CalculationTimeConstrainsParametersCreate,
CalculationTimeConstrainsRead, OptimumResult) 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 .utils import analyze_monthly_metrics, create_time_series_data, get_months_between, plant_simulation_metrics
from src.equipment_sparepart.model import ScopeEquipmentPart
import copy
import random
import math import math
from src.overhaul_activity.service import get_standard_scope_by_session_id from src.overhaul_activity.service import get_standard_scope_by_session_id
from collections import defaultdict
from datetime import timedelta from datetime import timedelta
import pandas as pd
import logging import logging
import aiohttp import aiohttp
from datetime import datetime, date from datetime import datetime, date
import asyncio import asyncio
import json import json
# from src.utils import save_to_pastebin
from src.config import BASE_URL, REALIBILITY_SERVICE_API from src.config import BASE_URL, REALIBILITY_SERVICE_API
client = httpx.AsyncClient(timeout=300.0) client = httpx.AsyncClient(timeout=300.0)
@ -184,7 +175,6 @@ class OptimumCostModelWithSpareparts:
failure_counts = [] failure_counts = []
cumulative_risk = 0
monthly_risk_cost_per_failure = 0 monthly_risk_cost_per_failure = 0
if ecs: if ecs:
@ -272,24 +262,18 @@ class OptimumCostModelWithSpareparts:
feasible_results = [r for r in cost_results if r['can_proceed']] feasible_results = [r for r in cost_results if r['can_proceed']]
# if not feasible_results: # 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 # # 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: # for result in cost_results:
# if result['missing_critical_parts'] == min_critical_missing: # 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 # Find month with minimum total cost among feasible options
min_cost = float('inf') min_cost = float('inf')
optimal_result = None optimal_result = None
optimal_index = -1
for i, result in enumerate(cost_results): for i, result in enumerate(cost_results):
if result in feasible_results and result['total_cost'] < min_cost: if result in feasible_results and result['total_cost'] < min_cost:
min_cost = result['total_cost'] min_cost = result['total_cost']
optimal_result = result optimal_result = result
optimal_index = i
if optimal_result is None: if optimal_result is None:
return None return None
@ -325,7 +309,6 @@ class OptimumCostModelWithSpareparts:
'sparepart_available': optimal_result['sparepart_available'], 'sparepart_available': optimal_result['sparepart_available'],
'sparepart_status': optimal_result['sparepart_status'], 'sparepart_status': optimal_result['sparepart_status'],
'procurement_details': optimal_result['procurement_details'], 'procurement_details': optimal_result['procurement_details'],
# 'missing_critical_parts': optimal_result['missing_critical_parts'],
'optimization_status': status, 'optimization_status': status,
'all_monthly_costs': [] # Will be filled by calling function 'all_monthly_costs': [] # Will be filled by calling function
} }
@ -364,13 +347,9 @@ class OptimumCostModelWithSpareparts:
eq["Location"]: eq eq["Location"]: eq
for eq in data 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) 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) contribution_factor = equipment_birnbaum.get(location_tag, 0.0)
ecs = ecs_tags.get(location_tag, None) ecs = ecs_tags.get(location_tag, None)
# try:
# # Get failure predictions # # Get failure predictions
monthly_data = await self.get_failures_prediction(simulation_id, location_tag, contribution_factor) 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']}") 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 # Phase 2: Optimize considering sparepart interactions
self.logger.info("Phase 2: Optimizing with sparepart interactions...") self.logger.info("Phase 2: Optimizing with sparepart interactions...")
# Start with individual optimal timings # 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 # Iteratively improve the plan considering sparepart constraints
improved_plan = self._optimize_fleet_with_sparepart_constraints( improved_plan = self._optimize_fleet_with_sparepart_constraints(
@ -515,7 +490,7 @@ class OptimumCostModelWithSpareparts:
fleet_optimal_cost = total_costs[fleet_optimal_index] fleet_optimal_cost = total_costs[fleet_optimal_index]
# Generate procurement optimization report # 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 # Update calculation with results
calculation.optimum_oh_day = fleet_optimal_index 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 # Filter included equipment for performance
included_equipment = [eq for eq in scope_calculation.equipment_results if eq.is_included] included_equipment = [eq for eq in scope_calculation.equipment_results if eq.is_included]
all_equipment = scope_calculation.equipment_results 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 # Pre-calculate aggregated statistics
calculation_results = [] calculation_results = []
@ -850,13 +824,8 @@ async def get_calculation_result(db_session: DbSession, calculation_id: str, tok
k = 5 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) 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 # Process each monthself
for month_index in range(data_num): 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) (scope_overhaul.start_date.month - prev_oh_scope.end_date.month)
# Find minimum cost point # Find minimum cost point
min_cost_result = min(calculation_results, key=lambda x: x.total_cost) min(calculation_results, key=lambda x: x.total_cost)
min_cost_month = min_cost_result.month
# Calculate timing recommendation # Calculate timing recommendation
timing_recommendation = "OPTIMAL" timing_recommendation = "OPTIMAL"

@ -1,5 +1,4 @@
import datetime import datetime
import json
import pandas as pd import pandas as pd
import requests import requests
@ -230,7 +229,6 @@ def calculate_risk_cost_per_failure(monthly_results, birnbaum_importance, energy
failures_count = data['failures_count'] failures_count = data['failures_count']
# 1. Calculate equipment capacity contribution to system # 1. Calculate equipment capacity contribution to system
# Capacity = avg_flowrate * birnbaum_importance * availability
equipment_capacity = avg_flow_rate * birnbaum_importance * availability equipment_capacity = avg_flow_rate * birnbaum_importance * availability
# 2. Calculate capacity lost to downtime per month # 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: # 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 logging
import os import os
from typing import List from typing import List
@ -6,7 +5,6 @@ from urllib import parse
from pydantic import BaseModel from pydantic import BaseModel
from starlette.config import Config from starlette.config import Config
from starlette.datastructures import CommaSeparatedStrings
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

@ -1,8 +1,7 @@
import json import json
import logging import logging
from typing import Dict, Union, Tuple from typing import Dict, Union
from decimal import Decimal, getcontext from decimal import Decimal, getcontext
import math
# Set high precision for decimal calculations # Set high precision for decimal calculations
getcontext().prec = 50 getcontext().prec = 50
@ -34,7 +33,6 @@ def system_availability(structure: Structure, availabilities: Dict[str, float])
if not components: # Handle empty series if not components: # Handle empty series
return 1.0 return 1.0
# Series: A_system = A1 * A2 * ... * An
product = Decimal('1.0') product = Decimal('1.0')
for s in components: for s in components:
availability = system_availability(s, availabilities) availability = system_availability(s, availabilities)
@ -46,7 +44,6 @@ def system_availability(structure: Structure, availabilities: Dict[str, float])
if not components: # Handle empty parallel if not components: # Handle empty parallel
return 0.0 return 0.0
# Parallel: A_system = 1 - (1-A1) * (1-A2) * ... * (1-An)
product = Decimal('1.0') product = Decimal('1.0')
for s in components: for s in components:
availability = system_availability(s, availabilities) 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'], key=lambda x: x[1]['birnbaum_importance'],
reverse=True) 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: for component, measures in sorted_components:
print(f"{component:<20} {measures['component_availability']:<12.6f} " 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}") f"{measures['fussell_vesely_importance']:<12.6f} {measures['contribution_percentage']*100:<12.4f}")
# Return results with system info included # Return results with system info included
# results['_system_info'] = system_info
return results return results

@ -6,7 +6,7 @@ from typing import Annotated, Any
from fastapi import Depends from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker 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 sqlalchemy.orm import DeclarativeBase
from starlette.requests import Request from starlette.requests import Request

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

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

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

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

@ -1,23 +1,13 @@
import random
from typing import Optional from typing import Optional
from sqlalchemy import Delete, Select, and_, text from sqlalchemy import text
from sqlalchemy.orm import selectinload
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]: # async def get(*, db_session: DbSession, scope_equipment_activity_id: str) -> Optional[ScopeEquipmentActivity]:
# """Returns a document based on the given document id.""" # """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 typing import Optional, List, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession as DbSession
from sqlalchemy.sql import text from sqlalchemy.sql import text
import logging import logging
@ -25,10 +15,6 @@ logger = logging.getLogger(__name__)
# async def get_all( # async def get_all(
# db_session: CollectorDbSession, # 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]]: # ) -> List[Dict[str, Any]]:
# """ # """
# Retrieve overhaul spare parts consumption data. # Retrieve overhaul spare parts consumption data.
@ -47,14 +33,10 @@ logger = logging.getLogger(__name__)
# # --- 1. Basic validation --- # # --- 1. Basic validation ---
# if not parent_wonum: # 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): # if start_year < 1900 or (end_year and end_year < start_year):
# raise ValueError("Invalid year range provided.")
# if end_year is None: # if end_year is None:
# end_year = start_year + 1
# # --- 2. Build SQL safely --- # # --- 2. Build SQL safely ---
# base_query = """ # base_query = """
@ -65,13 +47,8 @@ logger = logging.getLogger(__name__)
# AND xx_parent = :parent_wonum # AND xx_parent = :parent_wonum
# """ # """
# params = {
# "parent_wonum": parent_wonum,
# }
# if location_tag: # if location_tag:
# base_query += " AND location_tag = :location_tag"
# params["location_tag"] = location_tag
# base_query += """ # base_query += """
# ), # ),
@ -79,7 +56,6 @@ logger = logging.getLogger(__name__)
# SELECT wonum, itemnum, itemqty, inv_curbaltotal, inv_avgcost # SELECT wonum, itemnum, itemqty, inv_curbaltotal, inv_avgcost
# FROM public.wo_max_material # FROM public.wo_max_material
# WHERE wonum IN (SELECT wonum FROM filtered_wo) # WHERE wonum IN (SELECT wonum FROM filtered_wo)
# )
# SELECT # SELECT
# fwo.location_tag AS location_tag, # fwo.location_tag AS location_tag,
# fm.itemnum, # fm.itemnum,
@ -95,36 +71,16 @@ logger = logging.getLogger(__name__)
# """ # """
# # --- 3. Execute query --- # # --- 3. Execute query ---
# try:
# result = await db_session.execute(text(base_query), params)
# rows = result.fetchall()
# # Handle "no data found" # # Handle "no data found"
# if not rows: # if not rows:
# logger.info(f"No spare part data found for parent WO {parent_wonum}.")
# return []
# # --- 4. Map results cleanly --- # # --- 4. Map results cleanly ---
# equipment_parts = []
# for row in rows: # for row in rows:
# try:
# equipment_parts.append({ # 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) # "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 typing import List, Dict, Any, Optional
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession

@ -1,10 +1,8 @@
from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String from sqlalchemy import UUID, Column, ForeignKey, String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from src.database.core import Base from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin from src.models import DefaultMixin
from src.workorder.model import MasterWorkOrder
class EquipmentWorkscopeGroup(Base, 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, from src.database.service import (CommonParameters, DbSession)
search_filter_sort_paginate)
from src.models import StandardResponse from src.models import StandardResponse
from .schema import ScopeEquipmentJobCreate, ScopeEquipmentJobPagination 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)): async def get_jobs(common: CommonParameters, location_tag: str, scope: Optional[str] = Query(None)):
"""Get all scope pagination.""" """Get all scope pagination."""
# return
results = await get_all(common=common, location_tag=location_tag, scope=scope) results = await get_all(common=common, location_tag=location_tag, scope=scope)
return StandardResponse( return StandardResponse(
@ -31,7 +29,6 @@ async def create_scope_equipment_jobs(
db_session: DbSession, assetnum, scope_job_in: ScopeEquipmentJobCreate db_session: DbSession, assetnum, scope_job_in: ScopeEquipmentJobCreate
): ):
"""Get all scope activity pagination.""" """Get all scope activity pagination."""
# return
await create(db_session=db_session, assetnum=assetnum, scope_job_in=scope_job_in) await create(db_session=db_session, assetnum=assetnum, scope_job_in=scope_job_in)
return StandardResponse( return StandardResponse(

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

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

@ -2,39 +2,26 @@ import logging
import asyncio import asyncio
import time import time
from src.context import set_request_id, reset_request_id, get_request_id 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 uuid import uuid1
from fastapi import Depends, FastAPI, HTTPException, status from fastapi import Depends, FastAPI, HTTPException
from src.auth.service import JWTBearer from src.auth.service import JWTBearer
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from pydantic import ValidationError
from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
from sqlalchemy import inspect
from sqlalchemy.ext.asyncio import async_scoped_session from sqlalchemy.ext.asyncio import async_scoped_session
from sqlalchemy.orm import scoped_session from starlette.middleware.base import (BaseHTTPMiddleware)
from starlette.middleware.base import (BaseHTTPMiddleware,
RequestResponseEndpoint)
from starlette.middleware.gzip import GZipMiddleware from starlette.middleware.gzip import GZipMiddleware
from starlette.requests import Request from starlette.requests import Request
from starlette.responses import FileResponse, Response, StreamingResponse from starlette.responses import Response
from starlette.routing import compile_path
from starlette.staticfiles import StaticFiles
import uuid as _uuid_module import uuid as _uuid_module
from src.api import api_router 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.enums import ResponseStatus
from src.exceptions import handle_exception from src.exceptions import handle_exception
from src.app_logging import configure_logging from src.app_logging import configure_logging
from src.middleware import RequestValidationMiddleware, RequestTimeoutMiddleware from src.middleware import RequestValidationMiddleware, RequestTimeoutMiddleware
# from src.rate_limiter import limiter
from fastapi.exceptions import RequestValidationError from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException from starlette.exceptions import HTTPException as StarletteHTTPException
from src.csrf_protect import csrf_protect 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(RequestValidationError, handle_exception)
app.add_exception_handler(RateLimitExceeded, _sync_rate_limit_handler) app.add_exception_handler(RateLimitExceeded, _sync_rate_limit_handler)
# app.state.limiter = limiter
app.add_middleware(GZipMiddleware, minimum_size=1000) app.add_middleware(GZipMiddleware, minimum_size=1000)
# app.add_middleware(SlowAPIMiddleware)
app.add_middleware(RequestTimeoutMiddleware, timeout=REQUEST_TIMEOUT_SECONDS) app.add_middleware(RequestTimeoutMiddleware, timeout=REQUEST_TIMEOUT_SECONDS)
# credentials: "include", # credentials: "include",
@ -160,8 +145,6 @@ security_headers_middleware(app)
app.add_middleware(RequestValidationMiddleware) app.add_middleware(RequestValidationMiddleware)
# app.add_middleware( # app.add_middleware(
# CSRFProtectMiddleware, # CSRFProtectMiddleware,
# excluded_paths=["/healthcheck"]
# )
@ -301,24 +284,9 @@ async def add_security_headers(request: Request, call_next):
# class MetricsMiddleware(BaseHTTPMiddleware): # class MetricsMiddleware(BaseHTTPMiddleware):
# async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: # 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 # raise e from None
# return response
# app.add_middleware(ExceptionMiddleware)
app.include_router(api_router) app.include_router(api_router)

@ -1,10 +1,6 @@
from datetime import datetime from datetime import datetime
from typing import Optional, Union from typing import Optional, Union
from sqlalchemy import select, func, cast, Numeric, text from sqlalchemy import select, 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 src.database.core import CollectorDbSession, DbSession from src.database.core import CollectorDbSession, DbSession
from src.overhaul_scope.model import OverhaulScope from src.overhaul_scope.model import OverhaulScope
@ -107,7 +103,6 @@ ORDER BY avg_cost DESC;
# wm.wonum, # wm.wonum,
# SUM( # SUM(
# wm.itemqty * # wm.itemqty *
# COALESCE(wm.inv_avgcost, po.unit_cost, 0)
# ) AS parts_total_cost # ) AS parts_total_cost
# FROM public.wo_maxim_material wm # FROM public.wo_maxim_material wm
# LEFT JOIN ( # LEFT JOIN (
@ -129,7 +124,6 @@ ORDER BY avg_cost DESC;
# COALESCE(w.actservcost, 0) AS service_cost # COALESCE(w.actservcost, 0) AS service_cost
# FROM target_wo w # FROM target_wo w
# LEFT JOIN part_costs pc ON w.wonum = pc.wonum # LEFT JOIN part_costs pc ON w.wonum = pc.wonum
# )
# SELECT # SELECT
# asset_location, # asset_location,
# ROUND(SUM(material_cost + service_cost)::numeric / COUNT(wonum), 2) AS avg_cost, # 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; # ORDER BY total_wo_count DESC;
# """) # """)
# parent_nums = []
# result = await collector_db.execute(query, {"parent_nums": parent_nums})
# data = []
# for row in result: # for row in result:
# data.append({ # 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): 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 # LEFT JOIN part_costs pc
# ON w.wonum = pc.wonum # ON w.wonum = pc.wonum
# WHERE # WHERE
# w.worktype = 'OH'
# AND w.reportdate IS NOT NULL # AND w.reportdate IS NOT NULL
# AND w.actstart IS NOT NULL # AND w.actstart IS NOT NULL
# AND w.actfinish IS NOT NULL # AND w.actfinish IS NOT NULL
# AND w.asset_unit IN ('3', '00') # AND w.asset_unit IN ('3', '00')
# AND w.wonum NOT LIKE 'T%' # AND w.wonum NOT LIKE 'T%'
# )
# SELECT # SELECT
# asset_location, # asset_location,
# AVG(total_wo_cost) AS avg_cost # 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): # class Metrics(object):
# _providers = []
# def __init__(self): # def __init__(self):
# if not METRIC_PROVIDERS: # if not METRIC_PROVIDERS:
# log.info( # log.info(
# "No metric providers defined via METRIC_PROVIDERS env var. Metrics will not be sent." # "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): # def gauge(self, name, value, tags=None):
# for provider in self._providers: # for provider in self._providers:
# log.debug( # 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): # def counter(self, name, value=None, tags=None):
# for provider in self._providers: # for provider in self._providers:
# log.debug( # 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): # def timer(self, name, value, tags=None):
# for provider in self._providers: # for provider in self._providers:
# log.debug( # 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 # # 0. Header validation
# # ------------------------- # # -------------------------
# header_keys = [key.lower() for key, _ in request.headers.items()]
# # Check for duplicate headers # # 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: # if real_duplicates:
# raise HTTPException( # raise HTTPException(
# status_code=422,
# detail=f"Duplicate headers detected: {real_duplicates}",
# )
# # Whitelist headers # # Whitelist headers
# unknown_headers = [key for key in header_keys if key not in ALLOWED_HEADERS]
# if unknown_headers: # if unknown_headers:
# filtered_unknown = [h for h in unknown_headers if not h.startswith('sec-')]
# if filtered_unknown: # if filtered_unknown:
# raise HTTPException( # raise HTTPException(
# status_code=422,
# detail=f"Unknown headers detected: {filtered_unknown}",
# )
# # Inspect header values # # Inspect header values
# for key, value in request.headers.items(): # for key, value in request.headers.items():
# if value: # if value:
# inspect_value(value, f"header '{key}'")
# ------------------------- # -------------------------
# 1. Query string limits # 1. Query string limits
@ -400,7 +386,6 @@ class RequestValidationMiddleware(BaseHTTPMiddleware):
if content_type.startswith("application/json"): if content_type.startswith("application/json"):
body = await request.body() body = await request.body()
# if len(body) > MAX_JSON_BODY_SIZE: # if len(body) > MAX_JSON_BODY_SIZE:
# raise HTTPException(status_code=422, detail="JSON body too large")
if body: if body:
try: try:
@ -419,7 +404,6 @@ class RequestValidationMiddleware(BaseHTTPMiddleware):
return await call_next(request) return await call_next(request)
import threading
import asyncio import asyncio
import json import json
import logging import logging

@ -4,12 +4,10 @@ from datetime import datetime
from typing import Generic, Optional, TypeVar from typing import Generic, Optional, TypeVar
import pytz import pytz
from pydantic import BaseModel, Field, SecretStr from pydantic import BaseModel, SecretStr
from sqlalchemy import Column, DateTime, String, event, func from sqlalchemy import Column, DateTime, String, event
from sqlalchemy.dialects.postgresql import UUID 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.config import TIMEZONE
from src.enums import ResponseStatus from src.enums import ResponseStatus

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

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

@ -1,19 +1,15 @@
import asyncio import asyncio
from typing import Optional
import httpx 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.calculation_target_reliability.service import RBD_SERVICE_API
from src.config import TC_RBD_ID from src.config import TC_RBD_ID
from src.database.core import DbSession from src.database.core import DbSession
from src.contribution_util import calculate_contribution from src.contribution_util import calculate_contribution
from src.overhaul_activity.service import get_standard_scope_by_session_id from src.overhaul_activity.service import get_standard_scope_by_session_id
from src.overhaul_scope.model import OverhaulScope 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.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): 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" 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" calc_plant_result = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}/plant"
async with httpx.AsyncClient(timeout=300.0) as client: async with httpx.AsyncClient(timeout=300.0) as client:
calc_task = client.get(calc_result_url, headers=headers) 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) plant_task = client.get(calc_plant_result, headers=headers)
# Run all three requests concurrently # Run all three requests concurrently
calc_response, plant_response = await asyncio.gather(calc_task, plant_task) calc_response, plant_response = await asyncio.gather(calc_task, plant_task)
calc_response.raise_for_status() calc_response.raise_for_status()
# plot_response.raise_for_status()
plant_response.raise_for_status() plant_response.raise_for_status()
calc_data = calc_response.json()["data"] calc_data = calc_response.json()["data"]
# plot_data = plot_response.json()["data"]
plant_data = plant_response.json()["data"] plant_data = plant_response.json()["data"]
return { return {
"calc_result": calc_data, "calc_result": calc_data,
# "plot_result": plot_data,
"plant_result": plant_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]: # async def get(*, db_session: DbSession, scope_id: str) -> Optional[Scope]:
# """Returns a document based on the given document id.""" # """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): # async def get_all(*, db_session: DbSession):
# """Returns all documents.""" # """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): # async def create(*, db_session: DbSession, scope_id: ScopeCreate):
# """Creates a new document.""" # """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): # async def update(*, db_session: DbSession, scope: Scope, scope_id: ScopeUpdate):
# """Updates a document.""" # """Updates a document."""
# data = scope_id.model_dump()
# update_data = scope_id.model_dump(exclude_defaults=True)
# for field in data: # for field in data:
# if field in update_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): # async def delete(*, db_session: DbSession, scope_id: str):
# """Deletes a document.""" # """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 import UUID, Column, Float, ForeignKey, String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from src.database.core import Base from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin from src.models import DefaultMixin
from src.workorder.model import MasterWorkOrder
class OverhaulActivity(Base, DefaultMixin): class OverhaulActivity(Base, DefaultMixin):
@ -18,26 +15,11 @@ class OverhaulActivity(Base, DefaultMixin):
service_cost = Column(Float, nullable=False, default=0) service_cost = Column(Float, nullable=False, default=0)
status = Column(String, nullable=False, default="pending") status = Column(String, nullable=False, default="pending")
# equipment = relationship(
# "MasterEquipment", # "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", # # "ScopeEquipmentPart",
# # lazy="select", # or "joined", "subquery", "dynamic" depending on your needs
# # primaryjoin="OverhaulActivity.assetnum == foreign(ScopeEquipmentPart.assetnum)",
# # uselist=True
# # )
# overhaul_scope = relationship(
# "OverhaulScope", # "OverhaulScope",
# lazy="raise",
# )
# overhaul_jobs = relationship(
# "OverhaulJob", back_populates="overhaul_activity", lazy="raise" # "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 uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query, status from fastapi import APIRouter, Depends, HTTPException, Query, status
from src.csrf_protect import csrf_protect from src.csrf_protect import csrf_protect
from src.database.core import CollectorDbSession from src.database.core import CollectorDbSession
from src.database.service import (CommonParameters, DbSession, from src.database.service import (CommonParameters, DbSession)
search_filter_sort_paginate)
from src.models import StandardResponse from src.models import StandardResponse
from src.auth.access_control import require_any_role, ALLOWED_ROLES from src.auth.access_control import require_any_role, ALLOWED_ROLES
from .schema import (OverhaulActivityCreate, OverhaulActivityPagination, from .schema import (OverhaulActivityCreate, OverhaulActivityRead)
OverhaulActivityRead, OverhaulActivityUpdate) from .service import add_multiple_equipment_to_session, get, get_all, remove_equipment_from_session
from .service import add_multiple_equipment_to_session, get, get_all, remove_equipment_from_session, update
router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))]) router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))])
@ -28,7 +26,6 @@ async def get_scope_equipments(
scope_name: Optional[str] = Query(None), scope_name: Optional[str] = Query(None),
): ):
"""Get all scope activity pagination.""" """Get all scope activity pagination."""
# return
data = await get_all( data = await get_all(
common=common, common=common,
location_tag=location_tag, location_tag=location_tag,
@ -52,7 +49,7 @@ async def create_overhaul_equipment(
overhaul_session_id: UUID, overhaul_session_id: UUID,
): ):
activity = await add_multiple_equipment_to_session( await add_multiple_equipment_to_session(
db_session=db_session, db_session=db_session,
collector_db=collector_db_session, collector_db=collector_db_session,
overhaul_session_id=overhaul_session_id, overhaul_session_id=overhaul_session_id,
@ -82,30 +79,17 @@ async def get_overhaul_equipment(
# @router.put( # @router.put(
# "/{overhaul_session}/{assetnum}",
# response_model=StandardResponse[OverhaulActivityRead],
# )
# async def update_scope( # async def update_scope(
# db_session: DbSession, # db_session: DbSession,
# scope_equipment_activity_in: OverhaulActivityUpdate, # scope_equipment_activity_in: OverhaulActivityUpdate,
# assetnum: str, # assetnum: str,
# ): # ):
# activity = await get(db_session=db_session, assetnum=assetnum)
# if not activity: # if not activity:
# raise HTTPException( # raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail="A data with this id does not exist.",
# )
# return StandardResponse( # 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( @router.post(

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

@ -1,23 +1,16 @@
import asyncio
import datetime import datetime
from typing import List, Optional from typing import List, Optional
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from fastapi import HTTPException, status from fastapi import HTTPException, status
from sqlalchemy import Delete, Select, and_, func, select from sqlalchemy import Select, and_, select
from sqlalchemy import update as sqlUpdate
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.orm import joinedload, selectinload from sqlalchemy.orm import joinedload, selectinload
from src.auth.service import CurrentUser
from src.database.core import DbSession from src.database.core import DbSession
from src.database.service import CommonParameters, search_filter_sort_paginate 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.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.overhaul_scope.service import get as get_session, get_prev_oh
from src.standard_scope.model import MasterEquipment, StandardScope 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.workscope_group.model import MasterActivity
from src.equipment_workscope_group.model import EquipmentWorkscopeGroup from src.equipment_workscope_group.model import EquipmentWorkscopeGroup
from src.overhaul_scope.model import MaintenanceType 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.overhaul_scope.service import get as get_overhaul
from src.standard_scope.model import EquipmentOHHistory from src.standard_scope.model import EquipmentOHHistory
from .model import OverhaulActivity from .model import OverhaulActivity
from .schema import (OverhaulActivityCreate, OverhaulActivityRead, from .schema import (OverhaulActivityRead,
OverhaulActivityUpdate) OverhaulActivityUpdate)
import json import json
@ -65,31 +58,17 @@ async def get_all(
all: bool = False, all: bool = False,
collector_db: CollectorDbSession collector_db: CollectorDbSession
): ):
# query = (
# Select(OverhaulActivity)
# .where(OverhaulActivity.overhaul_scope_id == overhaul_session_id) # .where(OverhaulActivity.overhaul_scope_id == overhaul_session_id)
# .options(joinedload(OverhaulActivity.equipment).options(joinedload(MasterEquipment.parent).options(joinedload(MasterEquipment.parent)))) # .options(joinedload(OverhaulActivity.equipment).options(joinedload(MasterEquipment.parent).options(joinedload(MasterEquipment.parent))))
# .options(selectinload(OverhaulActivity.overhaul_scope)) # .options(selectinload(OverhaulActivity.overhaul_scope))
# .options(selectinload(OverhaulActivity.overhaul_jobs).options(joinedload(OverhaulJob.scope_equipment_job).options(joinedload(ScopeEquipmentJob.job)))) # .options(selectinload(OverhaulActivity.overhaul_jobs).options(joinedload(OverhaulJob.scope_equipment_job).options(joinedload(ScopeEquipmentJob.job))))
# )
# if assetnum: # if assetnum:
# query = query.filter(OverhaulActivity.assetnum == assetnum).options(
# joinedload(OverhaulActivity.overhaul_scope)
# )
# if scope_name: # 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) 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) 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 = [] 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) 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) 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: for equipment in eqs:

@ -1,18 +1,15 @@
import re import re
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends
from sqlalchemy import select 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.csrf_protect import csrf_protect
from src.database.core import DbSession from src.database.core import DbSession
from src.database.service import CommonParameters
from src.models import StandardResponse from src.models import StandardResponse
from src.overhaul_gantt.model import OverhaulGantt from src.overhaul_gantt.model import OverhaulGantt
from src.overhaul_gantt.schema import OverhaulGanttIn from src.overhaul_gantt.schema import OverhaulGanttIn
# from .schema import (OverhaulScheduleCreate, OverhaulSchedulePagination, OverhaulScheduleUpdate)
from .service import get_gantt_performance_chart from .service import get_gantt_performance_chart
router = APIRouter() router = APIRouter()
@ -23,7 +20,6 @@ router = APIRouter()
) )
async def get_gantt_performance(db_session: DbSession): async def get_gantt_performance(db_session: DbSession):
"""Get all scope pagination.""" """Get all scope pagination."""
# return
query = select(OverhaulGantt).limit(1) query = select(OverhaulGantt).limit(1)
data = (await db_session.execute(query)).scalar_one_or_none() 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): async def get_gantt_spreadsheet(db_session: DbSession):
"""Get all scope pagination.""" """Get all scope pagination."""
# return
query = select(OverhaulGantt).limit(1) query = select(OverhaulGantt).limit(1)
data = (await db_session.execute(query)).scalar_one_or_none() 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): async def update_gantt_spreadsheet(db_session: DbSession, spreadsheet_in: OverhaulGanttIn, admin: Admin):
"""Get all scope pagination.""" """Get all scope pagination."""
# return
match = re.search(r"/d/([a-zA-Z0-9-_]+)", spreadsheet_in.spreadsheet_link) match = re.search(r"/d/([a-zA-Z0-9-_]+)", spreadsheet_in.spreadsheet_link)
if not match: 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 # db_session: DbSession, overhaul_job_in: OverhaulScheduleCreate
# ): # ):
# await create( # await create(
# db_session=db_session,
# overhaul_job_in=overhaul_job_in,
# )
# return StandardResponse( # return StandardResponse(
# data=None,
# message="Data created successfully",
# )
# @router.put("/{overhaul_job_id}", response_model=StandardResponse[None]) # @router.put("/{overhaul_job_id}", response_model=StandardResponse[None])
# async def update_overhaul_schedule( # async def update_overhaul_schedule(
# db_session: DbSession, overhaul_job_id: str, overhaul_job_in: OverhaulScheduleUpdate # 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( # return StandardResponse(
# data=None,
# message="Data updated successfully",
# )
# @router.delete("/{overhaul_job_id}", response_model=StandardResponse[None]) # @router.delete("/{overhaul_job_id}", response_model=StandardResponse[None])
# async def delete_overhaul_equipment_job(db_session: DbSession, overhaul_job_id): # 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( # 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 pydantic import Field
from src.models import DefultBase from src.models import DefultBase
@ -18,31 +10,13 @@ class OverhaulGanttIn(DefultBase):
# class OverhaulScheduleCreate(OverhaulScheduleBase): # 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): # class OverhaulScheduleUpdate(OverhaulScheduleBase):
# start: datetime
# finish: datetime
# class OverhaulScheduleRead(OverhaulScheduleBase): # 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): # class OverhaulSchedulePagination(Pagination):
# items: List[OverhaulScheduleRead] = []

@ -1,19 +1,11 @@
from typing import Optional
from fastapi import HTTPException, status 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 from .utils import fetch_all_sections, get_google_creds, get_spreatsheed_service, process_spreadsheet_data
# async def get_all(*, common): # async def get_all(*, common):
# """Returns all documents.""" # """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( # 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): # async def update(*, db_session: DbSession, overhaul_schedule_id: str, overhaul_job_in: OverhaulScheduleUpdate):
# """Updates a document.""" # """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: # for field in data:
# if field in update_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): # async def delete(*, db_session: DbSession, overhaul_schedule_id: str):
# """Deletes a document.""" # """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"): async def get_gantt_performance_chart(*, spreadsheet_id = "1gZXuwA97zU1v4QBv56wKeiqadc6skHUucGKYG8qVFRk"):

@ -1,8 +1,5 @@
import urllib
from google.oauth2.service_account import Credentials from google.oauth2.service_account import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"] 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): # 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 # # 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 # UUID(as_uuid=True), ForeignKey("oh_ms_workscope_group.id"), nullable=False
# )
# location_tag = Column(String, nullable=False)
# equipment = relationship(
# "StandardScope", # "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" # # "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.core import DbSession
from src.database.service import CommonParameters from src.database.service import CommonParameters
from src.models import StandardResponse from src.models import StandardResponse
from .schema import (OverhaulJobBase, OverhaulJobCreate, OverhaulJobPagination, from .schema import (OverhaulJobCreate, OverhaulJobPagination)
OverhaulJobRead)
from .service import create, delete, get_all from .service import create, delete, get_all
router = APIRouter() router = APIRouter()
@ -19,7 +17,6 @@ router = APIRouter()
) )
async def get_jobs(common: CommonParameters, location_tag: str, scope: Optional[str] = Query(None)): async def get_jobs(common: CommonParameters, location_tag: str, scope: Optional[str] = Query(None)):
"""Get all scope pagination.""" """Get all scope pagination."""
# return
results = await get_all(common=common, location_tag=location_tag, scope=scope) results = await get_all(common=common, location_tag=location_tag, scope=scope)
return StandardResponse( return StandardResponse(
@ -33,7 +30,6 @@ async def create_overhaul_equipment_jobs(
db_session: DbSession, overhaul_equipment_id, overhaul_job_in: OverhaulJobCreate db_session: DbSession, overhaul_equipment_id, overhaul_job_in: OverhaulJobCreate
): ):
"""Get all scope activity pagination.""" """Get all scope activity pagination."""
# return
await create( await create(
db_session=db_session, db_session=db_session,
overhaul_equipment_id=overhaul_equipment_id, 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]]) # @router.post("", response_model=StandardResponse[List[str]])
# async def create_scope(db_session: DbSession, scope_in: OverhaulJobCreate): # 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]) # @router.put("/{scope_id}", response_model=StandardResponse[ScopeRead])
# async def update_scope(db_session: DbSession, scope_id: str, scope_in: ScopeUpdate, current_user: CurrentUser): # 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: # if not scope:
# raise HTTPException( # 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]) # @router.delete("/{scope_id}", response_model=StandardResponse[ScopeRead])
# async def delete_scope(db_session: DbSession, scope_id: str): # async def delete_scope(db_session: DbSession, scope_id: str):
# scope = await get(db_session=db_session, scope_id=scope_id)
# if not scope: # if not scope:
# raise HTTPException( # 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 typing import List, Optional
from uuid import UUID from uuid import UUID
from pydantic import Field
from src.models import DefultBase, Pagination from src.models import DefultBase, Pagination
from src.overhaul_scope.schema import ScopeRead from src.overhaul_scope.schema import ScopeRead
from src.scope_equipment_job.schema import ScopeEquipmentJobRead
from src.job.schema import ActivityMasterRead from src.job.schema import ActivityMasterRead
class OverhaulJobBase(DefultBase): class OverhaulJobBase(DefultBase):

@ -1,14 +1,10 @@
from typing import Optional from typing import Optional
from fastapi import HTTPException, status from fastapi import HTTPException, status
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.core import DbSession
from src.database.service import search_filter_sort_paginate 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.model import MasterActivity
from src.workscope_group_maintenance_type.model import WorkscopeOHType from src.workscope_group_maintenance_type.model import WorkscopeOHType
@ -51,7 +47,7 @@ async def create(
OverhaulJob.overhaul_activity_id == overhaul_equipment_id 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: for job_id in overhaul_job_in.job_ids:
overhaul_equipment_job = OverhaulJob( overhaul_equipment_job = OverhaulJob(
@ -96,28 +92,20 @@ async def delete(
return True return True
except Exception as e: except Exception:
await db_session.rollback() await db_session.rollback()
raise raise
# async def update(*, db_session: DbSession, scope: OverhaulScope, scope_in: ScopeUpdate): # async def update(*, db_session: DbSession, scope: OverhaulScope, scope_in: ScopeUpdate):
# """Updates a document.""" # """Updates a document."""
# data = scope_in.model_dump()
# update_data = scope_in.model_dump(exclude_defaults=True)
# for field in data: # for field in data:
# if field in update_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): # async def delete(*, db_session: DbSession, scope_id: str):
# """Deletes a document.""" # """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) String)
from sqlalchemy.orm import relationship
from src.database.core import Base from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin from src.models import DefaultMixin
class OverhaulSchedule(Base, 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.core import DbSession
from src.database.service import CommonParameters from src.database.service import CommonParameters
from src.models import StandardResponse from src.models import StandardResponse
@ -18,7 +16,6 @@ router = APIRouter()
) )
async def get_schedules(common: CommonParameters): async def get_schedules(common: CommonParameters):
"""Get all scope pagination.""" """Get all scope pagination."""
# return
results = await get_all(common=common) results = await get_all(common=common)

@ -5,9 +5,6 @@ from uuid import UUID
from pydantic import Field from pydantic import Field
from src.models import DefultBase, Pagination 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): class OverhaulScheduleBase(DefultBase):
pass pass

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

@ -3,7 +3,7 @@ from sqlalchemy import Column, DateTime , Integer, String, ForeignKey, UUID
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from src.database.core import Base 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 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") maintenance_type = relationship("MaintenanceType", lazy="selectin", backref="overhaul_scopes")
# activity_equipments = relationship("OverhaulActivity", lazy="selectin")
class MaintenanceType(Base, DefaultMixin): class MaintenanceType(Base, DefaultMixin):

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

@ -1,9 +1,8 @@
from typing import Optional from typing import Optional
from sqlalchemy import Delete, Select, func from sqlalchemy import Select
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
from src.auth.service import CurrentUser
from src.database.core import DbSession from src.database.core import DbSession
from src.database.service import search_filter_sort_paginate from src.database.service import search_filter_sort_paginate
from src.overhaul_activity.model import OverhaulActivity 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 status=scope_in.status # Hardcoded status
) )
# raise Exception(overhaul_session.start_date)
db_session.add(overhaul_session) db_session.add(overhaul_session)
# Need to flush to get the id # 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 import UUID, Column, Float, ForeignKey, Integer, String, Date
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from src.database.core import Base from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin from src.models import DefaultMixin
from src.workorder.model import MasterWorkOrder
class MasterSparePart(Base, 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.csrf_protect import csrf_protect
from src.database.core import CollectorDbSession 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.models import StandardResponse
from src.sparepart.schema import SparepartRemark from src.sparepart.schema import SparepartRemark
from src.auth.access_control import require_any_role, ALLOWED_ROLES 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]) @router.get("", response_model=StandardResponse[list])
async def get_sparepart(collector_db_session:CollectorDbSession, db_session: DbSession): async def get_sparepart(collector_db_session:CollectorDbSession, db_session: DbSession):
"""Get all scope activity pagination.""" """Get all scope activity pagination."""
# return
data = await get_spareparts_paginated(db_session=db_session, collector_db_session=collector_db_session) data = await get_spareparts_paginated(db_session=db_session, collector_db_session=collector_db_session)
return StandardResponse( return StandardResponse(
@ -36,59 +35,35 @@ async def create_remark_route(collector_db_session:CollectorDbSession, db_sessio
# @router.post("", response_model=StandardResponse[ActivityMasterCreate]) # @router.post("", response_model=StandardResponse[ActivityMasterCreate])
# async def create_activity(db_session: DbSession, activity_in: 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( # @router.get(
# "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster] # "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster]
# )
# async def get_activity(db_session: DbSession, activity_id: str): # async def get_activity(db_session: DbSession, activity_id: str):
# activity = await get(db_session=db_session, activity_id=activity_id)
# if not activity: # if not activity:
# raise HTTPException( # 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( # @router.put(
# "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster] # "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster]
# )
# async def update_scope( # async def update_scope(
# db_session: DbSession, activity_in: ActivityMasterCreate, activity_id # db_session: DbSession, activity_in: ActivityMasterCreate, activity_id
# ): # ):
# activity = await get(db_session=db_session, activity_id=activity_id)
# if not activity: # if not activity:
# raise HTTPException( # raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail="A data with this id does not exist.",
# )
# return StandardResponse( # return StandardResponse(
# data=await update(
# db_session=db_session, activity=activity, activity_in=activity_in
# ), # ),
# message="Data updated successfully",
# )
# @router.delete( # @router.delete(
# "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster] # "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster]
# )
# async def delete_scope(db_session: DbSession, activity_id: str): # async def delete_scope(db_session: DbSession, activity_id: str):
# activity = await get(db_session=db_session, activity_id=activity_id)
# if not activity: # if not activity:
# raise HTTPException( # 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 dataclasses import dataclass
from datetime import date, datetime from datetime import date
from enum import Enum from enum import Enum
from typing import Any, Dict, List, Optional from typing import List
from uuid import UUID from uuid import UUID
from pydantic import BaseModel, Field
from src.models import DefultBase, Pagination from src.models import DefultBase, Pagination

@ -1,23 +1,12 @@
from typing import Optional
import asyncio
import logging import logging
from datetime import datetime, timedelta, date from datetime import datetime, timedelta, date
from typing import List, Dict, Optional, Tuple from typing import List, Dict, Tuple
from collections import defaultdict 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 import select, text
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.service import get_standard_scope_by_session_id 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_overview_overhaul
from src.overhaul_scope.service import get_prev_oh
from src.sparepart.model import SparepartRemark from src.sparepart.model import SparepartRemark
from src.sparepart.schema import ProcurementRecord, ProcurementStatus, SparepartRequirement, SparepartStock from src.sparepart.schema import ProcurementRecord, ProcurementStatus, SparepartRequirement, SparepartStock
from src.soft_delete import apply_not_deleted_filter 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__) log = logging.getLogger(__name__)
from sqlalchemy import text from sqlalchemy import text
import math
from sqlalchemy import text from sqlalchemy import text
@ -41,8 +29,6 @@ from sqlalchemy import text
# """ # """
# # Normalize parent_num to array for SQL ANY() # # 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(""" # data_query = text("""
# WITH selected_wo AS ( # WITH selected_wo AS (
@ -154,12 +140,10 @@ from sqlalchemy import text
# 'po_vendeliverydate', po_vendeliverydate, # 'po_vendeliverydate', po_vendeliverydate,
# 'po_receipt', po_receipt, # 'po_receipt', po_receipt,
# 'po_status', po_status # 'po_status', po_status
# )
# ORDER BY pr_issue_date DESC # ORDER BY pr_issue_date DESC
# ) AS pr_po_details # ) AS pr_po_details
# FROM pr_po_unified # FROM pr_po_unified
# GROUP BY item_num # GROUP BY item_num
# )
# SELECT # SELECT
# wm.itemnum, # wm.itemnum,
@ -181,22 +165,10 @@ from sqlalchemy import text
# ORDER BY wm.itemnum; # ORDER BY wm.itemnum;
# """) # """)
# rows = await collector_db_session.execute(data_query, {"parent_nums": parent_nums})
# spare_parts = []
# for row in rows: # for row in rows:
# spare_parts.append({ # 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): 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 items_per_page (int): Number of items per page
""" """
# calculate limit/offset # calculate limit/offset
# limit = items_per_page
# offset = (page - 1) * items_per_page
# wo_materials AS ( # wo_materials AS (
# SELECT # 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""" """Load sparepart data from database"""
# You'll need to implement these queries based on your database schema # You'll need to implement these queries based on your database schema
# Get scope dates for analysis window # 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_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 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) sparepart_manager = SparepartManager(analysis_start_date, analysis_window_months)
start_date = prev_oh_scope.end_date
end_date = scope.start_date
# Load sparepart stocks # Load sparepart stocks
# Example query - adjust based on your schema # 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) sparepart_manager.add_sparepart_stock(stock)
# parent_nums = []
# query = text(""" # query = text("""
# WITH target_wo AS ( # 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, # item_num,
# ROUND(CAST(AVG( # ROUND(CAST(AVG(
# EXTRACT(EPOCH FROM ( # EXTRACT(EPOCH FROM (
# COALESCE(vendeliverydate, estimated_arrival_date) - issue_date
# )) / 86400 / 30.44 # )) / 86400 / 30.44
# ) AS NUMERIC), 1) as avg_leadtime_months, # ) AS NUMERIC), 1) as avg_leadtime_months,
# ROUND(CAST(MIN( # ROUND(CAST(MIN(
# EXTRACT(EPOCH FROM ( # EXTRACT(EPOCH FROM (
# COALESCE(vendeliverydate, estimated_arrival_date) - issue_date
# )) / 86400 / 30.44 # )) / 86400 / 30.44
# ) AS NUMERIC), 1) as min_leadtime_months, # ) AS NUMERIC), 1) as min_leadtime_months,
# ROUND(CAST(MAX( # ROUND(CAST(MAX(
# EXTRACT(EPOCH FROM ( # EXTRACT(EPOCH FROM (
# COALESCE(vendeliverydate, estimated_arrival_date) - issue_date
# )) / 86400 / 30.44 # )) / 86400 / 30.44
# ) AS NUMERIC), 1) as max_leadtime_months, # ) AS NUMERIC), 1) as max_leadtime_months,
# COUNT(*) as leadtime_sample_size, # 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 # ) as description
# FROM public.maximo_sparepart_pr_po_line # FROM public.maximo_sparepart_pr_po_line
# WHERE description IS NOT NULL # WHERE description IS NOT NULL
# )
# SELECT # SELECT
# tr.asset_location, # tr.asset_location,
# tr.itemnum, # 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; # 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: # 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(): # for equipment_tag, requirements in equipment_requirements.items():
# sparepart_manager.add_equipment_requirements(equipment_tag, requirements)
# Load equipment sparepart requirements # Load equipment sparepart requirements
# You'll need to create this table/relationship # 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 import UUID, Column, Date, Float, ForeignKey, Integer, String, Boolean
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from src.database.core import Base from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin from src.models import DefaultMixin
from src.workorder.model import MasterWorkOrder
class StandardScope(Base, 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 fastapi.params import Query
from src.auth.service import CurrentUser
from src.database.core import DbSession, CollectorDbSession 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.models import StandardResponse
from src.auth.access_control import require_any_role, ALLOWED_ROLES from src.auth.access_control import require_any_role, ALLOWED_ROLES
from .schema import (MasterEquipmentPagination, ScopeEquipmentCreate, from .schema import (MasterEquipmentPagination, ScopeEquipmentCreate,
ScopeEquipmentPagination, ScopeEquipmentRead, ScopeEquipmentPagination)
ScopeEquipmentUpdate) from .service import (create, get_all, get_all_master_equipment, get_history_standard_scope_wo_service)
from .service import (create, delete, get_all, get_all_master_equipment, update, get_history_standard_scope_wo_service)
from uuid import UUID from uuid import UUID
router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))]) 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]) @router.get("", response_model=StandardResponse[ScopeEquipmentPagination])
async def get_scope_equipments(common: CommonParameters, scope_name: str = Query(None)): async def get_scope_equipments(common: CommonParameters, scope_name: str = Query(None)):
"""Get all scope pagination.""" """Get all scope pagination."""
# return
data = await get_all(common=common, oh_scope=scope_name) data = await get_all(common=common, oh_scope=scope_name)
return StandardResponse( return StandardResponse(
@ -59,34 +56,18 @@ async def get_history_standard_scope_wo(
# async def update_scope_equipment( # async def update_scope_equipment(
# db_session: DbSession, assetnum: str, scope__equipment_in: ScopeEquipmentUpdate # 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: # if not scope_equipment:
# raise HTTPException( # raise HTTPException(
# status_code=status.HTTP_404_NOT_FOUND,
# detail="A data with this id does not exist.",
# )
# return StandardResponse( # 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]) # @router.delete("/{assetnum}", response_model=StandardResponse[None])
# async def delete_scope_equipment(db_session: DbSession, assetnum: str): # 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: # if not scope_equipment:
# raise HTTPException( # 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 typing import List, Optional, ForwardRef
from uuid import UUID 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.models import DefultBase, Pagination
from src.overhaul_scope.schema import ScopeRead
from .enum import ScopeEquipmentType from .enum import ScopeEquipmentType

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

@ -6,7 +6,6 @@ Run with:
pytest src/testing/integration_test.py -v pytest src/testing/integration_test.py -v
""" """
import pytest
import requests import requests
BASE_URL = "http://100.125.115.116:8000/optimumoh" 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 # Placeholder IDs — replace with real values from your database before running
OVERHAUL_SESSION_ID = "3704c82c-cfc5-47f7-813a-1661f89b0738" OVERHAUL_SESSION_ID = "3704c82c-cfc5-47f7-813a-1661f89b0738"
# OVERHAUL_EQUIPMENT_ID = 1 ## Source code got commented
SCOPE_JOB_ID = "db590764-72cb-4eb1-a30f-014116df0472" SCOPE_JOB_ID = "db590764-72cb-4eb1-a30f-014116df0472"
CALCULATION_ID = "44f483f3-bfe4-4094-a59f-b97a10f2fea6" CALCULATION_ID = "44f483f3-bfe4-4094-a59f-b97a10f2fea6"
BUDGET_THRESHOLD = 100000 BUDGET_THRESHOLD = 100000
RISK_COST = 1 RISK_COST = 1
LOCATION_TAG = "3AL-F501A" LOCATION_TAG = "3AL-F501A"
SCOPE_NAME = "A" SCOPE_NAME = "A"
# OVERHAUL_SESSION_HISTORY_ID = 1
ASSET_NUM = "A19825" ASSET_NUM = "A19825"
# SCOPE_EQUIPMENT_JOB_ID = 1 ## Source code got commented
# EQUIPMENT_ACTIVITY_ID = 1 ## Source code got commented
EAF_INPUT = 0.85 EAF_INPUT = 0.85
DURATION = 17520 DURATION = 17520
SCOPE_EQUIPMENT_ACTIVITY_ID = "4527eb2b-0bc4-46b3-80a1-e1d2f67882f8" 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(): # def test_get_overhaul_gantt():
# """GET /overhaul-gantt — no role guard; all roles allowed""" # """GET /overhaul-gantt — no role guard; all roles allowed"""
# for user in USER_LIST: # 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]] # # [[Source code got commented]]
# def test_get_overhaul_session(): # def test_get_overhaul_session():
# """GET /overhaul-session?all=1 — require_any_role blocks Management"""
# for user in USER_LIST: # 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]] # # [[Source code got commented]]
# def test_post_overhaul_session(): # def test_post_overhaul_session():
# """POST /overhaul-session — require_any_role + CREATE blocks Management""" # """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: # 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: # 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 # # Overhaul Schedules
@ -145,39 +121,15 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # [[Attribute error]] # # [[Attribute error]]
# def test_post_overhaul_schedules(): # def test_post_overhaul_schedules():
# """POST /overhaul-schedules — require_any_role + CREATE blocks Management""" # """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" # "status": "Upcoming"
# }
# for user in USER_LIST: # 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: # 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]] # # [[Source code got commented]]
# def test_delete_overhaul_schedule(): # def test_delete_overhaul_schedule():
# """POST /overhaul-schedules/delete/{scope_id} — require_any_role + DELETE blocks Management. CSRF required."""
# for user in USER_LIST: # 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: # 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]] # # [[Source code got commented]]
# def test_post_overhaul_jobs(): # 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: # 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]] # # [[Database error: Unconsumed column names: scope_overhaul, assetnum, type, removal_date]]
# def test_post_scope_equipments(): # def test_post_scope_equipments():
# """POST /scope-equipments — require_any_role blocks Management""" # """POST /scope-equipments — require_any_role blocks Management"""
# payload = {
# "assetnums": ["A26312", "A26313", "A26314"],
# "scope_name": "A" # "scope_name": "A"
# }
# for user in USER_LIST: # 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: # 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]] # # [[No endpoint exists]]
# def test_delete_scope_equipment(): # def test_delete_scope_equipment():
# """DELETE /scope-equipments/delete/{id} — require_any_role blocks Management"""
# for user in USER_LIST: # 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: # 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]] # # [[Source code got commented]]
# def test_post_scope_equipment_jobs(): # 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: # 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]] # # [[Source code got commented]]
# def test_delete_scope_equipment_job(): # def test_delete_scope_equipment_job():
# """POST /equipment-workscopes/delete/{scope_job_id} — no role guard; all roles allowed"""
# for user in USER_LIST: # 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 # # Overhaul Jobs
@ -276,43 +178,16 @@ def get_csrf_token(path: str, auth_token: str) -> str:
# # [[Source code got commented]] # # [[Source code got commented]]
# def test_get_overhaul_jobs(): # def test_get_overhaul_jobs():
# """GET /overhaul-jobs/{overhaul_equipment_id} — no role guard; all roles allowed"""
# for user in USER_LIST: # 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 # # Calculation — Budget Constraint
# # ───────────────────────────────────────────── # # ─────────────────────────────────────────────
# # [[AttributeError]]
# def test_post_calculation_time_constraint(): # 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: # 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: # 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 # """POST /equipment-activities — no role guard; all roles allowed
# Note: route commented out in api.py; active equivalent is POST /overhaul-activity/{session_id} # 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: # 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]] # # [[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} # Note: route commented out in api.py; active equivalent is POST /overhaul-activity/delete/{session}/{location_tag}
# """ # """
# for user in USER_LIST: # 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(): # def test_get_jobs():
# """GET /jobs — no role guard; all roles allowed""" # """GET /jobs — no role guard; all roles allowed"""
# for user in USER_LIST: # 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 # Overhauls sub-routes
# ───────────────────────────────────────────── # ─────────────────────────────────────────────
# # [[TypeError]]
# def test_get_overhauls_schedules(): # def test_get_overhauls_schedules():
# """GET /overhauls/schedules — required_permission(READ); Management has READ"""
# for user in USER_LIST: # 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(): # def test_get_overhauls_critical_parts():
# """GET /overhauls/critical-parts — required_permission(READ); Management has READ"""
# for user in USER_LIST: # 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]] # # [[Attribute Error]]
# def test_get_overhaul_activity_by_assetnum(): # def test_get_overhaul_activity_by_assetnum():
# """GET /overhaul-activity/{overhaul_session}/{assetnum} — require_any_role blocks Management"""
# for user in USER_LIST: # 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 # # Workscopes
# # ───────────────────────────────────────────── # # ─────────────────────────────────────────────
# # [[TypeError]]
# def test_post_workscopes(): # def test_post_workscopes():
# """POST /workscopes — no role guard; all roles allowed # """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: # 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 # Equipment Workscopes
# ───────────────────────────────────────────── # ─────────────────────────────────────────────
# # [[NameError]]
# def test_post_equipment_workscopes_delete(): # def test_post_equipment_workscopes_delete():
# """POST /equipment-workscopes/delete/{scope_job_id} — no role guard; all roles allowed # """POST /equipment-workscopes/delete/{scope_job_id} — no role guard; all roles allowed
# No body required. # No body required.
# """ # """
# for user in USER_LIST: # 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 # # Calculation — Time Constraint
# # ───────────────────────────────────────────── # # ─────────────────────────────────────────────
# # [[AttributeError]]
# def test_post_calculation_time_constraint(): # def test_post_calculation_time_constraint():
# """POST /calculation/time-constraint — required_permission(CREATE); Management has no CREATE # """POST /calculation/time-constraint — required_permission(CREATE); Management has no CREATE
# Payload: overhaulCost (float), ohSessionId (UUID), costPerFailure (float) # Payload: overhaulCost (float), ohSessionId (UUID), costPerFailure (float)
# Query params: scope_calculation_id (optional), with_results (optional int), simulation_id (optional) # 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: # 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: # 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(): # def test_get_calculation_time_constraint_parameters():
# """GET /calculation/time-constraint/parameters — required_permission(READ); Management has READ # """GET /calculation/time-constraint/parameters — required_permission(READ); Management has READ
# Query params: calculation_id (optional) # Query params: calculation_id (optional)
# """ # """
# for user in USER_LIST: # 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(): # 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: # 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 # # Calculation — Budget Constraint
# # ───────────────────────────────────────────── # # ─────────────────────────────────────────────
# # [[AttributeError]]
# def test_get_calculation_budget_constraint(): # def test_get_calculation_budget_constraint():
# """GET /calculation/budget-constraint/{session_id} — require_any_role blocks Management # """GET /calculation/budget-constraint/{session_id} — require_any_role blocks Management
# Query params: cost_threshold (float, default 100) # Query params: cost_threshold (float, default 100)
# """ # """
# for user in USER_LIST: # 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 pytest src/testing/integration_test.py -v
""" """
import pytest
import requests import requests
BASE_URL = "http://100.125.115.116:8000/optimumoh" BASE_URL = "http://100.125.115.116:8000/optimumoh"

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

@ -1,8 +1,7 @@
from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String from sqlalchemy import Column, Float, String
from sqlalchemy.orm import relationship
from src.database.core import Base from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin from src.models import DefaultMixin
class MasterWorkOrder(Base, DefaultMixin): class MasterWorkOrder(Base, DefaultMixin):
@ -13,8 +12,4 @@ class MasterWorkOrder(Base, DefaultMixin):
workgroup = Column(String, nullable=True) workgroup = Column(String, nullable=True)
total_cost_max = Column(Float, nullable=True) total_cost_max = Column(Float, nullable=True)
# scope_equipments = relationship(
# "ScopeEquipment", # "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 import UUID, Column, ForeignKey, String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from src.database.core import Base from src.database.core import Base
from src.models import DefaultMixin, IdentityMixin, TimeStampMixin from src.models import DefaultMixin
from src.workorder.model import MasterWorkOrder
class MasterActivitytask(Base, DefaultMixin): class MasterActivitytask(Base, DefaultMixin):
@ -45,15 +43,8 @@ class MasterActivity(Base, DefaultMixin):
back_populates="workscope_group", back_populates="workscope_group",
) )
# details = relationship(
# "MasterActivityDetail", # "MasterActivityDetail",
# lazy="raise",
# primaryjoin="and_(MasterActivity.id == foreign(MasterActivityDetail.activity_id))",
# )
# class MasterActivityDetail(Base, DefaultMixin): # 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, from src.database.service import (CommonParameters, DbSession)
search_filter_sort_paginate)
from src.models import StandardResponse from src.models import StandardResponse
from .schema import (ActivityMaster, ActivityMasterCreate, from .schema import (ActivityMaster, ActivityMasterCreate,
@ -14,7 +13,6 @@ router = APIRouter()
@router.get("", response_model=StandardResponse[ActivityMasterPagination]) @router.get("", response_model=StandardResponse[ActivityMasterPagination])
async def get_activities(common: CommonParameters): async def get_activities(common: CommonParameters):
"""Get all scope activity pagination.""" """Get all scope activity pagination."""
# return
data = await get_all(common=common) data = await get_all(common=common)

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

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

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

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

Loading…
Cancel
Save