diff --git a/.dockerignore b/.dockerignore index e69de29..2bfa6a4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -0,0 +1 @@ +tests/ diff --git a/.gitignore b/.gitignore index 7f92caf..d3d5f67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ env/ __pycache__/ -venv/ \ No newline at end of file +venv/ +tests/ \ No newline at end of file diff --git a/src/api.py b/src/api.py index 0741a14..170c2d9 100644 --- a/src/api.py +++ b/src/api.py @@ -12,32 +12,18 @@ from src.calculation_target_reliability.router import \ router as calculation_target_reliability from src.calculation_time_constrains.router import \ router as calculation_time_constrains_router, get_calculation -# from src.job.router import router as job_router from src.overhaul.router import router as overhaul_router from src.standard_scope.router import router as standard_scope_router from src.overhaul_activity.router import router as overhaul_activity_router from src.workscope_group.router import router as workscope_group_router from src.equipment_workscope_group.router import router as equipment_workscope_group_router -# from src.overhaul_job.router import router as job_overhaul_router -# from src.overhaul_scope.router import router as scope_router -# from src.scope_equipment.router import router as scope_equipment_router -# from src.scope_equipment_job.router import router as scope_equipment_job_router -# from src.overhaul_schedule.router import router as overhaul_schedule_router from src.overhaul_gantt.router import router as gantt_router from src.sparepart.router import router as sparepart_router from src.equipment_sparepart.router import router as equipment_sparepart_router -# from src.overhaul_scope.router import router as scope_router -# from src.scope_equipment.router import router as scope_equipment_router -# from src.overhaul.router import router as overhaul_router -# from src.overhaul_history.router import router as overhaul_history_router -# from src.overhaul_activity.router import router as scope_equipment_activity_router from src.overhaul_scope.router import router as ovehaul_schedule_router -# from src.scope_equipment_part.router import router as scope_equipment_part_router -# from src.calculation_target_reliability.router import router as calculation_target_reliability # -# from src.master_activity.router import router as activity_router class ErrorMessage(BaseModel): @@ -74,12 +60,10 @@ authenticated_api_router.include_router( ) -# authenticated_api_router.include_router(job_router, prefix="/jobs", tags=["job"]) # # # Overhaul session data # authenticated_api_router.include_router( # scope_router, prefix="/overhaul-session", tags=["overhaul-session"] -# ) authenticated_api_router.include_router( standard_scope_router, prefix="/scope-equipments", tags=["scope_equipment"] @@ -110,19 +94,12 @@ authenticated_api_router.include_router( ) # authenticated_api_router.include_router( # scope_equipment_job_router, -# prefix="/scope-equipment-jobs", -# tags=["scope_equipment", "job"], -# ) # authenticated_api_router.include_router( # overhaul_schedule_router, -# prefix="/overhaul-schedules", -# tags=["overhaul_schedule"], -# ) # authenticated_api_router.include_router( # job_overhaul_router, prefix="/overhaul-jobs", tags=["job", "overhaul"] -# ) authenticated_api_router.include_router( gantt_router, prefix="/overhaul-gantt", tags=["gantt"] @@ -130,19 +107,15 @@ authenticated_api_router.include_router( # authenticated_api_router.include_router( # overhaul_history_router, prefix="/overhaul-history", tags=["overhaul_history"] -# ) # authenticated_api_router.include_router( # scope_equipment_activity_router, prefix="/equipment-activities", tags=["scope_equipment_activities"] -# ) # authenticated_api_router.include_router( # activity_router, prefix="/activities", tags=["activities"] -# ) # authenticated_api_router.include_router( # scope_equipment_part_router, prefix="/equipment-parts", tags=["scope_equipment_parts"] -# ) authenticated_api_router.include_router( ovehaul_schedule_router, prefix="/overhaul-schedules", tags=["overhaul_schedules"] diff --git a/src/auth/service.py b/src/auth/service.py index 78abc8e..cf3b114 100644 --- a/src/auth/service.py +++ b/src/auth/service.py @@ -1,12 +1,11 @@ # app/auth/auth_bearer.py import json -from typing import Annotated, Optional +from typing import Annotated import requests from fastapi import Depends, HTTPException, Request from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer -from sqlalchemy.sql.expression import false import src.config as config diff --git a/src/calculation_budget_constrains/router.py b/src/calculation_budget_constrains/router.py index 399f427..cf0441b 100644 --- a/src/calculation_budget_constrains/router.py +++ b/src/calculation_budget_constrains/router.py @@ -1,6 +1,6 @@ -from typing import Annotated, Dict, List, Optional +from typing import Annotated, Dict -from fastapi import APIRouter, HTTPException, status +from fastapi import APIRouter from fastapi.params import Query from src.auth.service import Token diff --git a/src/calculation_budget_constrains/schema.py b/src/calculation_budget_constrains/schema.py index 1aefe76..f60e220 100644 --- a/src/calculation_budget_constrains/schema.py +++ b/src/calculation_budget_constrains/schema.py @@ -1,10 +1,8 @@ -from datetime import datetime -from typing import Any, Dict, List, Optional -from uuid import UUID +from typing import Any, Dict, List from pydantic import BaseModel, Field -from src.models import DefultBase, Pagination +from src.models import DefultBase class OverhaulBase(BaseModel): @@ -35,40 +33,19 @@ class OverhaulRead(OverhaulBase): class BudgetContraintQuery(DefultBase): cost_threshold: float = Field(100, ge=0, le=1000000000000000) -# { -# "overview": { -# "totalEquipment": 30, -# "nextSchedule": { -# "date": "2025-01-12", -# "Overhaul": "B", # "equipmentCount": 30 -# } # }, # "criticalParts": [ # "Boiler feed pump", # "Boiler reheater system", -# "Drum Level (Right) Root Valve A", # "BCP A Discharge Valve", # "BFPT A EXH Press HI Root VLV" # ], # "schedules": [ -# { -# "date": "2025-01-12", -# "Overhaul": "B", # "status": "upcoming" -# } # // ... other scheduled overhauls # ], -# "systemComponents": { -# "boiler": { -# "status": "operational", # "lastOverhaul": "2024-06-15" # }, -# "turbine": { -# "hpt": { "status": "operational" }, -# "ipt": { "status": "operational" }, # "lpt": { "status": "operational" } -# } # // ... other major components -# } -# } diff --git a/src/calculation_budget_constrains/service.py b/src/calculation_budget_constrains/service.py index 4a838b4..b10fbaf 100644 --- a/src/calculation_budget_constrains/service.py +++ b/src/calculation_budget_constrains/service.py @@ -1,17 +1,11 @@ from collections import defaultdict import math -import random from typing import Optional, List, Dict, Tuple from uuid import UUID -from sqlalchemy import Delete, Select -from src.auth.service import CurrentUser -from src.contribution_util import calculate_contribution_accurate from src.database.core import CollectorDbSession, DbSession -# from src.scope_equipment.model import ScopeEquipment -# from src.scope_equipment.service import get_by_scope_name -from src.overhaul_activity.service import get_all_by_session_id, get_standard_scope_by_session_id +from src.overhaul_activity.service import get_standard_scope_by_session_id # async def get_all_budget_constrains( # *, db_session: DbSession, session_id: str, cost_threshold: float = 100000000 @@ -24,7 +18,6 @@ from typing import List, Dict, Tuple from src.database.core import CollectorDbSession, DbSession from src.overhaul_activity.service import get_standard_scope_by_session_id -from src.contribution_util import calculate_contribution_accurate async def get_all_budget_constrains( @@ -84,7 +77,7 @@ async def get_all_budget_constrains( # Calculate efficiency and composite score for item in result: cost = item["total_cost"] or 1.0 - efficiency = item["contribution_norm"] / cost + item["contribution_norm"] / cost item["priority_score"] = item["contribution_norm"] # Choose method diff --git a/src/calculation_target_reliability/router.py b/src/calculation_target_reliability/router.py index f72cf5d..9a2cc36 100644 --- a/src/calculation_target_reliability/router.py +++ b/src/calculation_target_reliability/router.py @@ -1,17 +1,14 @@ -import asyncio -from typing import Dict, List, Optional from typing_extensions import Annotated from temporalio.client import Client from fastapi import APIRouter, HTTPException, status from fastapi.params import Query -from src.calculation_target_reliability.utils import wait_for_workflow from src.config import TEMPORAL_URL, TR_RBD_ID from src.database.core import DbSession, CollectorDbSession from src.auth.service import Token from src.models import StandardResponse -from .service import run_rbd_simulation, get_simulation_results, identify_worst_eaf_contributors +from .service import get_simulation_results, identify_worst_eaf_contributors from .schema import OptimizationResult, TargetReliabiltiyQuery from src.auth.access_control import required_permission, OHPermission from src.overhaul_scope.model import OverhaulScope @@ -22,18 +19,10 @@ router = APIRouter() # @router.get("", response_model=StandardResponse[List[Dict]]) # async def get_target_reliability( # db_session: DbSession, -# scope_name: Optional[str] = Query(None), -# eaf_threshold: float = Query(100), # ): # """Get all scope pagination.""" -# results = await get_all_target_reliability( -# db_session=db_session, scope_name=scope_name, eaf_threshold=eaf_threshold -# ) # return StandardResponse( -# data=results, -# message="Data retrieved successfully", -# ) @router.get("", response_model=StandardResponse[OptimizationResult], dependencies=[Depends(required_permission(OHPermission.READ, OverhaulScope))]) @@ -42,11 +31,6 @@ async def get_target_reliability( token: Token, collector_db: CollectorDbSession, params: Annotated[TargetReliabiltiyQuery, Query()], - # oh_session_id: Optional[str] = Query(None), - # eaf_input: float = Query(99.8), - # duration: int = Query(17520), - # simulation_id: Optional[str] = Query(None), - # cut_hours = Query(0) ): """Get all scope pagination.""" oh_session_id = params.oh_session_id @@ -61,12 +45,6 @@ async def get_target_reliability( detail="oh_session_id is required", ) - # results = await get_eaf_timeline( - # db_session=db_session, - # oh_session_id=oh_session_id, - # eaf_input=eaf_input, - # oh_duration=duration - # ) if duration != 17520: if not simulation_id: diff --git a/src/calculation_target_reliability/schema.py b/src/calculation_target_reliability/schema.py index 08389bd..cfb47c0 100644 --- a/src/calculation_target_reliability/schema.py +++ b/src/calculation_target_reliability/schema.py @@ -1,10 +1,8 @@ -from datetime import datetime from typing import Any, Dict, List, Optional -from uuid import UUID from pydantic import BaseModel, Field -from src.models import DefultBase, Pagination +from src.models import DefultBase class OverhaulBase(BaseModel): @@ -71,40 +69,19 @@ class TargetReliabiltiyQuery(DefultBase): simulation_id: Optional[str] = Field(None) cut_hours:int = Field(0) -# { -# "overview": { -# "totalEquipment": 30, -# "nextSchedule": { -# "date": "2025-01-12", -# "Overhaul": "B", # "equipmentCount": 30 -# } # }, # "criticalParts": [ # "Boiler feed pump", # "Boiler reheater system", -# "Drum Level (Right) Root Valve A", # "BCP A Discharge Valve", # "BFPT A EXH Press HI Root VLV" # ], # "schedules": [ -# { -# "date": "2025-01-12", -# "Overhaul": "B", # "status": "upcoming" -# } # // ... other scheduled overhauls # ], -# "systemComponents": { -# "boiler": { -# "status": "operational", # "lastOverhaul": "2024-06-15" # }, -# "turbine": { -# "hpt": { "status": "operational" }, -# "ipt": { "status": "operational" }, # "lpt": { "status": "operational" } -# } # // ... other major components -# } -# } diff --git a/src/calculation_target_reliability/service.py b/src/calculation_target_reliability/service.py index 8b1ca24..c37dcfc 100644 --- a/src/calculation_target_reliability/service.py +++ b/src/calculation_target_reliability/service.py @@ -1,20 +1,9 @@ import math -from typing import Optional, List -from dataclasses import dataclass -from sqlalchemy import Delete, Select import httpx -from src.auth.service import CurrentUser from src.config import RBD_SERVICE_API -from src.contribution_util import calculate_contribution, calculate_contribution_accurate from src.database.core import DbSession, CollectorDbSession -from datetime import datetime, timedelta -import random -from .utils import generate_down_periods -from src.overhaul_scope.service import get as get_overhaul -from bisect import bisect_left -from collections import defaultdict import asyncio -from .schema import AssetWeight,MaintenanceScenario,OptimizationResult +from .schema import AssetWeight,OptimizationResult from src.overhaul_activity.service import get_standard_scope_by_session_id @@ -53,28 +42,23 @@ async def get_simulation_results(*, simulation_id: str, token: str): } calc_result_url = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}?nodetype=RegularNode" - # plot_result_url = f"{RBD_SERVICE_API}/aeros/simulation/result/plot/{simulation_id}?nodetype=RegularNode" calc_plant_result = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}/plant" async with httpx.AsyncClient(timeout=300.0) as client: calc_task = client.get(calc_result_url, headers=headers) - # plot_task = client.get(plot_result_url, headers=headers) plant_task = client.get(calc_plant_result, headers=headers) # Run all three requests concurrently calc_response, plant_response = await asyncio.gather(calc_task, plant_task) calc_response.raise_for_status() - # plot_response.raise_for_status() plant_response.raise_for_status() calc_data = calc_response.json()["data"] - # plot_data = plot_response.json()["data"] plant_data = plant_response.json()["data"] return { "calc_result": calc_data, - # "plot_result": plot_data, "plant_result": plant_data } @@ -95,7 +79,6 @@ def calculate_asset_eaf_contributions(plant_result, eq_results, standard_scope, MIN_IMPROVEMENT_PERCENT = 0.0001 min_improvement_fraction = MIN_IMPROVEMENT_PERCENT / 100.0 - EPSILON = 0.001 # 1 ms or a fraction of an hour for comparison tolerance results = [] weighted_assets = [] @@ -223,7 +206,7 @@ async def identify_worst_eaf_contributors( # Target feasibility check warning_message = None if target_eaf > max_eaf_possible: - impossible_gap = target_eaf - max_eaf_possible + target_eaf - max_eaf_possible required_scheduled_hours = total_hours * (1 - target_eaf / 100) required_reduction = reduced_outage - required_scheduled_hours diff --git a/src/calculation_target_reliability/utils.py b/src/calculation_target_reliability/utils.py index 8083122..2328eaa 100644 --- a/src/calculation_target_reliability/utils.py +++ b/src/calculation_target_reliability/utils.py @@ -1,10 +1,10 @@ import asyncio from datetime import datetime, timedelta import random -from typing import List, Optional +from typing import Optional from temporalio.client import Client -from src.config import TEMPORAL_URL, TR_RBD_ID +from src.config import TEMPORAL_URL def generate_down_periods(start_date: datetime, end_date: datetime, num_periods: Optional[int] = None, min_duration: int = 3, diff --git a/src/calculation_time_constrains/class.py b/src/calculation_time_constrains/class.py index fc73344..f2a1477 100644 --- a/src/calculation_time_constrains/class.py +++ b/src/calculation_time_constrains/class.py @@ -106,8 +106,6 @@ class OptimumCostModel: failure_cost = (failure_prob * failure_replacement_cost) / expected_operating_time preventive_cost = (reliability * preventive_cost) / expected_operating_time else: - # failure_cost = failure_prob * self.IDRu - # preventive_cost = reliability * self.IDRp continue @@ -167,13 +165,13 @@ class OptimumCostModel: Calculate optimization for entire fleet of equipment """ max_interval = self._get_months_between(start_date, end_date) - preventive_cost_per_equipment = preventive_cost / len(equipments) + preventive_cost / len(equipments) fleet_results = [] total_corrective_costs = np.zeros(max_interval) total_preventive_costs = np.zeros(max_interval) total_procurement_costs = np.zeros(max_interval) - total_failures = np.zeros(max_interval) + np.zeros(max_interval) for equipment in equipments: # Get reliability data diff --git a/src/calculation_time_constrains/flows.py b/src/calculation_time_constrains/flows.py index 14e0556..1ccde99 100644 --- a/src/calculation_time_constrains/flows.py +++ b/src/calculation_time_constrains/flows.py @@ -1,12 +1,8 @@ from typing import Optional -from uuid import UUID -import numpy as np from fastapi import HTTPException, status -from sqlalchemy import Select, func, select -from sqlalchemy.orm import joinedload +from sqlalchemy import func, select -from src.auth.service import Token from src.config import TC_RBD_ID from src.database.core import DbSession from src.overhaul_scope.service import get_all @@ -15,13 +11,9 @@ from src.workorder.model import MasterWorkOrder from .schema import (CalculationTimeConstrainsParametersCreate, CalculationTimeConstrainsParametersRead, - CalculationTimeConstrainsParametersRetrive, - CalculationTimeConstrainsRead) -from .service import (create_calculation_result_service, create_param_and_data, - get_avg_cost_by_asset, - get_calculation_by_reference_and_parameter, - get_calculation_data_by_id, get_calculation_result, - run_simulation_with_spareparts) + CalculationTimeConstrainsParametersRetrive) +from .service import (create_param_and_data, + get_calculation_data_by_id, run_simulation_with_spareparts) from src.database.core import CollectorDbSession @@ -69,12 +61,6 @@ async def get_create_calculation_parameters( costPerFailure=costFailurePerScope, availableScopes=avaiableScopes.values(), recommendedScope="A", - # historicalData={ - # "averageOverhaulCost": 10000000, - # "lastCalculation": { - # "id": "calc_122", - # "date": "2024-10-15", - # "scope": "B", # }, # }, ) @@ -97,9 +83,6 @@ async def create_calculation( rbd_simulation_id = simulation_id or TC_RBD_ID - # results = await create_calculation_result_service( - # db_session=db_session, calculation=calculation_data, token=token - # ) results = await run_simulation_with_spareparts( db_session=db_session, calculation=calculation_data, token=token, collector_db_session=collector_db_session, simulation_id=rbd_simulation_id ) @@ -127,9 +110,3 @@ async def get_or_create_scope_equipment_calculation( # Check if calculation already exist # return CalculationTimeConstrainsRead( - # id=scope_calculation.id, - # reference=scope_calculation.overhaul_session_id, - # results=scope_calculation.results, - # optimum_oh=scope_calculation.optimum_oh_day, - # equipment_results=scope_calculation.equipment_results, - # ) diff --git a/src/calculation_time_constrains/model.py b/src/calculation_time_constrains/model.py index 8f82da9..fa237a4 100644 --- a/src/calculation_time_constrains/model.py +++ b/src/calculation_time_constrains/model.py @@ -1,12 +1,12 @@ from enum import Enum -from typing import List, Optional, Union +from typing import Optional from sqlalchemy import (JSON, UUID, Boolean, Column, Float, ForeignKey, - Integer, Numeric, String) + Integer, String) from sqlalchemy.orm import relationship from src.database.core import Base, DbSession -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin, UUIDMixin +from src.models import DefaultMixin, IdentityMixin class OverhaulReferenceType(str, Enum): @@ -32,29 +32,12 @@ class CalculationParam(Base, DefaultMixin, IdentityMixin): # overhaul_cost: float, # created_by: str, # # list of {"reference_type": OverhaulReferenceType, "reference_id": str} - # references: List[dict] # ): # # Create parameter - # param = cls( - # avg_failure_cost=avg_failure_cost, - # overhaul_cost=overhaul_cost, - # created_by=created_by - # ) - # db.add(param) - # await db.flush() # Flush to get the param.id # # Create reference links # for ref in references: - # reference_link = ReferenceLink( - # parameter_id=param.id, - # overhaul_reference_type=ref["reference_type"], - # reference_id=ref["reference_id"] - # ) - # db.add(reference_link) - - # await db.commit() - # await db.refresh(param) - # return param + class CalculationData(Base, DefaultMixin, IdentityMixin): diff --git a/src/calculation_time_constrains/router.py b/src/calculation_time_constrains/router.py index 6304722..bb3ea01 100644 --- a/src/calculation_time_constrains/router.py +++ b/src/calculation_time_constrains/router.py @@ -2,9 +2,7 @@ from typing import Annotated, List, Optional, Union from fastapi import APIRouter from fastapi.params import Query -import requests -from src import config from src.auth.service import CurrentUser, InternalKey, Token from src.config import DEFAULT_TC_ID from src.database.core import DbSession @@ -42,13 +40,9 @@ async def create_calculation_time_constrains( current_user: CurrentUser, calculation_time_constrains_in: CalculationTimeConstrainsParametersCreate, params: Annotated[CreateCalculationQuery, Query()], - # scope_calculation_id: Optional[str] = Query(None), - # with_results: Optional[int] = Query(0), - # simulation_id = Query(None) ): """Save calculation time constrains Here""" scope_calculation_id = params.scope_calculation_id - with_results = params.with_results simulation_id = params.simulation_id if scope_calculation_id: @@ -132,7 +126,6 @@ async def get_calculation_results(db_session: DbSession, calculation_id, token:I # requests.post(f"{config.AUTH_SERVICE_API}/sign-out", headers={ # "Authorization": f"Bearer {token}" - # }) return StandardResponse( data=results, diff --git a/src/calculation_time_constrains/schema.py b/src/calculation_time_constrains/schema.py index 6c9cd71..8eedbd7 100644 --- a/src/calculation_time_constrains/schema.py +++ b/src/calculation_time_constrains/schema.py @@ -1,7 +1,6 @@ from src.overhaul_scope.schema import ScopeRead -from dataclasses import dataclass from datetime import datetime -from typing import Any, Dict, List, Optional, Union +from typing import Dict, List, Optional, Union from uuid import UUID from pydantic import Field @@ -24,7 +23,6 @@ class CalculationTimeConstrainsParametersRetrive(CalculationTimeConstrainsBase): costPerFailure: Union[dict, float] = Field(..., description="Cost per failure") availableScopes: List[str] = Field(..., description="Available scopes") recommendedScope: str = Field(..., description="Recommended scope") - # historicalData: Dict[str, Any] = Field(..., description="Historical data") class CalculationTimeConstrainsParametersRead(CalculationTimeConstrainsBase): @@ -40,10 +38,6 @@ class CalculationTimeConstrainsParametersCreate(CalculationTimeConstrainsBase): # class CalculationTimeConstrainsCreate(CalculationTimeConstrainsBase): -# overhaulCost: float = Field(..., description="Overhaul cost") -# scopeOH: str = Field(..., description="Scope OH") -# costPerFailure: float = Field(..., description="Cost per failure") -# metadata: Dict[str, Any] = Field(..., description="Metadata") class CalculationResultsRead(CalculationTimeConstrainsBase): @@ -112,10 +106,6 @@ class CalculationTimeConstrainsReadNoResult(CalculationTimeConstrainsBase): max_interval: Optional[int] optimum_analysis: Optional[dict] session: ScopeRead - # optimum_oh_day: int - # max_interval: int - # optimal_analysis: dict - # analysis_metadata: dict class CalculationTimeConstrainsRead(CalculationTimeConstrainsBase): id: UUID diff --git a/src/calculation_time_constrains/service.py b/src/calculation_time_constrains/service.py index b4b2e7a..9a8a134 100644 --- a/src/calculation_time_constrains/service.py +++ b/src/calculation_time_constrains/service.py @@ -1,5 +1,5 @@ import datetime -from typing import Coroutine, List, Optional, Tuple,Dict +from typing import List, Optional, Tuple,Dict from uuid import UUID import calendar @@ -14,11 +14,8 @@ from sqlalchemy.orm import joinedload, selectinload from src.database.core import DbSession from src.overhaul_activity.service import get_all as get_all_by_session_id from src.overhaul_scope.service import get as get_scope, get_prev_oh -from src.sparepart.service import get_spareparts_paginated, load_sparepart_data_from_db -from src.utils import get_latest_numOfFail +from src.sparepart.service import load_sparepart_data_from_db from src.workorder.model import MasterWorkOrder -from src.database.core import CollectorDbSession -from src.overhaul_activity.model import OverhaulActivity from .model import (CalculationData, CalculationEquipmentResult, CalculationResult) from .schema import (CalculationResultsRead, @@ -26,21 +23,15 @@ from .schema import (CalculationResultsRead, CalculationTimeConstrainsParametersCreate, CalculationTimeConstrainsRead, OptimumResult) -from .utils import analyze_monthly_metrics, calculate_failures_per_month, calculate_risk_cost_per_failure, create_time_series_data, get_monthly_risk_analysis, get_months_between, plant_simulation_metrics -from src.equipment_sparepart.model import ScopeEquipmentPart -import copy -import random +from .utils import analyze_monthly_metrics, create_time_series_data, get_months_between, plant_simulation_metrics import math from src.overhaul_activity.service import get_standard_scope_by_session_id -from collections import defaultdict from datetime import timedelta -import pandas as pd import logging import aiohttp from datetime import datetime, date import asyncio import json -# from src.utils import save_to_pastebin from src.config import BASE_URL, REALIBILITY_SERVICE_API client = httpx.AsyncClient(timeout=300.0) @@ -184,7 +175,6 @@ class OptimumCostModelWithSpareparts: failure_counts = [] - cumulative_risk = 0 monthly_risk_cost_per_failure = 0 if ecs: @@ -272,24 +262,18 @@ class OptimumCostModelWithSpareparts: feasible_results = [r for r in cost_results if r['can_proceed']] # if not feasible_results: - # self.logger.warning(f"No feasible overhaul months for {location_tag} due to sparepart constraints") # # Return the earliest month with the least critical parts missing - # min_critical_missing = min(r['missing_critical_parts'] for r in cost_results) # for result in cost_results: # if result['missing_critical_parts'] == min_critical_missing: - # return self._create_optimal_result(result, location_tag, "INFEASIBLE") - # return None # Find month with minimum total cost among feasible options min_cost = float('inf') optimal_result = None - optimal_index = -1 for i, result in enumerate(cost_results): if result in feasible_results and result['total_cost'] < min_cost: min_cost = result['total_cost'] optimal_result = result - optimal_index = i if optimal_result is None: return None @@ -325,7 +309,6 @@ class OptimumCostModelWithSpareparts: 'sparepart_available': optimal_result['sparepart_available'], 'sparepart_status': optimal_result['sparepart_status'], 'procurement_details': optimal_result['procurement_details'], - # 'missing_critical_parts': optimal_result['missing_critical_parts'], 'optimization_status': status, 'all_monthly_costs': [] # Will be filled by calling function } @@ -364,13 +347,9 @@ class OptimumCostModelWithSpareparts: eq["Location"]: eq for eq in data } - plant_monthly_metrics = await self.get_failures_prediction(simulation_id=simulation_id, location_tag="plant", use_location_tag=0, birnbaum_importance=0) + await self.get_failures_prediction(simulation_id=simulation_id, location_tag="plant", use_location_tag=0, birnbaum_importance=0) - REFERENCE_CAPACITY = 630 # or 550 - COST_PER_MWH = 1_000_000 # rupiah - # plant_capacity_loss_money = [metrics['derated_mwh'] * COST_PER_MWH for metrics in plant_monthly_metrics.values()] - # cumulative_loss_money = np.cumsum(plant_capacity_loss_money) total_simulation_period = int((importance_results["plant_result"]['total_downtime'] + importance_results["plant_result"]['total_uptime'])/720) @@ -385,7 +364,6 @@ class OptimumCostModelWithSpareparts: contribution_factor = equipment_birnbaum.get(location_tag, 0.0) ecs = ecs_tags.get(location_tag, None) - # try: # # Get failure predictions monthly_data = await self.get_failures_prediction(simulation_id, location_tag, contribution_factor) @@ -419,15 +397,12 @@ class OptimumCostModelWithSpareparts: self.logger.info(f"Individual optimal for {location_tag}: Month {optimal_timing['optimal_month']}") - # except Exception as e: - # self.logger.error(f"Failed to calculate individual timing for {location_tag}: {e}") - # raise Exception(e) # Phase 2: Optimize considering sparepart interactions self.logger.info("Phase 2: Optimizing with sparepart interactions...") # Start with individual optimal timings - current_plan = [(tag, result['optimal_month']) for tag, result in individual_results.items()] + [(tag, result['optimal_month']) for tag, result in individual_results.items()] # Iteratively improve the plan considering sparepart constraints improved_plan = self._optimize_fleet_with_sparepart_constraints( @@ -515,7 +490,7 @@ class OptimumCostModelWithSpareparts: fleet_optimal_cost = total_costs[fleet_optimal_index] # Generate procurement optimization report - procurement_plan = self.sparepart_manager.optimize_procurement_timing(improved_plan) + self.sparepart_manager.optimize_procurement_timing(improved_plan) # Update calculation with results calculation.optimum_oh_day = fleet_optimal_index @@ -825,7 +800,6 @@ async def get_calculation_result(db_session: DbSession, calculation_id: str, tok # Filter included equipment for performance included_equipment = [eq for eq in scope_calculation.equipment_results if eq.is_included] all_equipment = scope_calculation.equipment_results - # all_spareparts = await get_spareparts_paginated(db_session=db_session, collector_db_session=collector_db_session ) # Pre-calculate aggregated statistics calculation_results = [] @@ -850,13 +824,8 @@ async def get_calculation_result(db_session: DbSession, calculation_id: str, tok k = 5 loss_exp = (plant_monthly_metrics['total_downtime'] * 660 * 500_000 * include_risk_cost) * (np.exp(k * (loss_production_per_month / total_simulation_period)) - 1) / (np.exp(k) - 1) - # REFERENCE_CAPACITY = 630 # or 550 - # COST_PER_MWH = 1_000_000 # rupiah - # plant_capacity_loss_money = [metrics['derated_mwh'] * COST_PER_MWH for metrics in plant_monthly_metrics.values()] - # cumulative_loss_money = np.cumsum(plant_capacity_loss_money) - # # raise Exception(plant_monthly_metrics) # Process each monthself for month_index in range(data_num): @@ -1050,8 +1019,7 @@ def _analyze_optimal_timing(calculation_results: List, optimum_oh_day: int, (scope_overhaul.start_date.month - prev_oh_scope.end_date.month) # Find minimum cost point - min_cost_result = min(calculation_results, key=lambda x: x.total_cost) - min_cost_month = min_cost_result.month + min(calculation_results, key=lambda x: x.total_cost) # Calculate timing recommendation timing_recommendation = "OPTIMAL" diff --git a/src/calculation_time_constrains/utils.py b/src/calculation_time_constrains/utils.py index 76e0ea5..043e62d 100644 --- a/src/calculation_time_constrains/utils.py +++ b/src/calculation_time_constrains/utils.py @@ -1,5 +1,4 @@ import datetime -import json import pandas as pd import requests @@ -230,7 +229,6 @@ def calculate_risk_cost_per_failure(monthly_results, birnbaum_importance, energy failures_count = data['failures_count'] # 1. Calculate equipment capacity contribution to system - # Capacity = avg_flowrate * birnbaum_importance * availability equipment_capacity = avg_flow_rate * birnbaum_importance * availability # 2. Calculate capacity lost to downtime per month @@ -294,9 +292,4 @@ def get_monthly_risk_analysis(timestamp_outs, birnbaum_importance, energy_price) } # Usage example: -# birnbaum_importance = 0.85 # Example value -# energy_price = 100 # Example: $100 per unit # -# results = get_monthly_risk_analysis(timestamp_outs, birnbaum_importance, energy_price) -# risk_cost_array = results['risk_cost_array'] -# print("Risk cost per failure each month:", risk_cost_array) \ No newline at end of file diff --git a/src/config.py b/src/config.py index 933332c..1f8c729 100644 --- a/src/config.py +++ b/src/config.py @@ -1,4 +1,3 @@ -import base64 import logging import os from typing import List @@ -6,7 +5,6 @@ from urllib import parse from pydantic import BaseModel from starlette.config import Config -from starlette.datastructures import CommaSeparatedStrings log = logging.getLogger(__name__) diff --git a/src/contribution_util.py b/src/contribution_util.py index b77a6f2..fda38b9 100644 --- a/src/contribution_util.py +++ b/src/contribution_util.py @@ -1,8 +1,7 @@ import json import logging -from typing import Dict, Union, Tuple +from typing import Dict, Union from decimal import Decimal, getcontext -import math # Set high precision for decimal calculations getcontext().prec = 50 @@ -34,7 +33,6 @@ def system_availability(structure: Structure, availabilities: Dict[str, float]) if not components: # Handle empty series return 1.0 - # Series: A_system = A1 * A2 * ... * An product = Decimal('1.0') for s in components: availability = system_availability(s, availabilities) @@ -46,7 +44,6 @@ def system_availability(structure: Structure, availabilities: Dict[str, float]) if not components: # Handle empty parallel return 0.0 - # Parallel: A_system = 1 - (1-A1) * (1-A2) * ... * (1-An) product = Decimal('1.0') for s in components: availability = system_availability(s, availabilities) @@ -256,12 +253,6 @@ def calculate_contribution_accurate(availabilities: Dict[str, float], structure_ key=lambda x: x[1]['birnbaum_importance'], reverse=True) - # print("\n=== COMPONENT IMPORTANCE ANALYSIS ===") - # print(f"System Availability: {system_info['system_availability']:.6f} ({system_info['system_availability']*100:.4f}%)") - # print(f"System Unavailability: {system_info['system_unavailability']:.6f}") - # print("\nComponent Rankings (by Birnbaum Importance):") - # print(f"{'Component':<20} {'Availability':<12} {'Birnbaum':<12} {'Criticality':<12} {'F-V':<12} {'Contribution%':<12}") - # print("-" * 92) for component, measures in sorted_components: print(f"{component:<20} {measures['component_availability']:<12.6f} " @@ -269,7 +260,6 @@ def calculate_contribution_accurate(availabilities: Dict[str, float], structure_ f"{measures['fussell_vesely_importance']:<12.6f} {measures['contribution_percentage']*100:<12.4f}") # Return results with system info included - # results['_system_info'] = system_info return results diff --git a/src/database/core.py b/src/database/core.py index 74845c0..514e016 100644 --- a/src/database/core.py +++ b/src/database/core.py @@ -6,7 +6,7 @@ from typing import Annotated, Any from fastapi import Depends from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker -from sqlalchemy.ext.declarative import declarative_base, declared_attr +from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import DeclarativeBase from starlette.requests import Request diff --git a/src/database/service.py b/src/database/service.py index 2418e8b..efe7dcc 100644 --- a/src/database/service.py +++ b/src/database/service.py @@ -4,8 +4,6 @@ from typing import Annotated, List, Type, TypeVar from fastapi import Depends, Query from pydantic.types import Json, constr from sqlalchemy import Select, desc, func, or_ -from sqlalchemy.exc import ProgrammingError -from sqlalchemy_filters import apply_pagination from src.database.schema import CommonParams @@ -28,7 +26,6 @@ def common_parameters( descending: List[bool] = Query([], alias="descending[]"), exclude: List[str] = Query([], alias="exclude[]"), all: int = Query(0, ge=0, le=1000000), - # role: QueryStr = Depends(get_current_role), ): return { "db_session": db_session, @@ -40,7 +37,6 @@ def common_parameters( "descending": descending, "current_user": current_user, "all": bool(all), - # "role": role, } @@ -108,7 +104,6 @@ async def search_filter_sort_paginate( all: bool = False, ): """Common functionality for searching, filtering, sorting, and pagination.""" - # try: # Check if model is Select if not isinstance(model, Select): query = Select(model) diff --git a/src/equipment_sparepart/model.py b/src/equipment_sparepart/model.py index 83331b4..215b46b 100644 --- a/src/equipment_sparepart/model.py +++ b/src/equipment_sparepart/model.py @@ -1,10 +1,8 @@ -from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String -from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy import UUID, Column, Float, ForeignKey, String from sqlalchemy.orm import relationship from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin -from src.workorder.model import MasterWorkOrder +from src.models import DefaultMixin class ScopeEquipmentPart(Base, DefaultMixin): diff --git a/src/equipment_sparepart/router.py b/src/equipment_sparepart/router.py index 36b5de6..9486f59 100644 --- a/src/equipment_sparepart/router.py +++ b/src/equipment_sparepart/router.py @@ -1,16 +1,11 @@ from typing import Dict, List -from fastapi import APIRouter, Depends, HTTPException, Query, status +from fastapi import APIRouter, Depends from src.database.core import CollectorDbSession -from src.database.service import (CommonParameters, DbSession, - search_filter_sort_paginate) from src.models import StandardResponse from src.auth.access_control import require_any_role, ALLOWED_ROLES -from .schema import (ScopeEquipmentActivityCreate, - ScopeEquipmentActivityPagination, - ScopeEquipmentActivityRead, ScopeEquipmentActivityUpdate) from .service import get_all router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))]) @@ -19,7 +14,6 @@ router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))]) @router.get("/{location_tag}", response_model=StandardResponse[List[Dict]]) async def get_scope_equipment_parts(collector_db_session: CollectorDbSession, location_tag): """Get all scope activity pagination.""" - # return data = await get_all(db_session=collector_db_session, location_tag=location_tag) return StandardResponse( diff --git a/src/equipment_sparepart/schema.py b/src/equipment_sparepart/schema.py index bc40adb..50ff670 100644 --- a/src/equipment_sparepart/schema.py +++ b/src/equipment_sparepart/schema.py @@ -1,8 +1,6 @@ -from datetime import datetime -from typing import Any, Dict, List, Optional -from uuid import UUID +from typing import List, Optional -from pydantic import BaseModel, Field +from pydantic import Field from src.models import DefultBase, Pagination @@ -30,40 +28,19 @@ class ScopeEquipmentActivityPagination(Pagination): items: List[ScopeEquipmentActivityRead] = [] -# { -# "overview": { -# "totalEquipment": 30, -# "nextSchedule": { -# "date": "2025-01-12", -# "Overhaul": "B", # "equipmentCount": 30 -# } # }, # "criticalParts": [ # "Boiler feed pump", # "Boiler reheater system", -# "Drum Level (Right) Root Valve A", # "BCP A Discharge Valve", # "BFPT A EXH Press HI Root VLV" # ], # "schedules": [ -# { -# "date": "2025-01-12", -# "Overhaul": "B", # "status": "upcoming" -# } # // ... other scheduled overhauls # ], -# "systemComponents": { -# "boiler": { -# "status": "operational", # "lastOverhaul": "2024-06-15" # }, -# "turbine": { -# "hpt": { "status": "operational" }, -# "ipt": { "status": "operational" }, # "lpt": { "status": "operational" } -# } # // ... other major components -# } -# } diff --git a/src/equipment_sparepart/service.py b/src/equipment_sparepart/service.py index 1ec2ab4..b028f71 100644 --- a/src/equipment_sparepart/service.py +++ b/src/equipment_sparepart/service.py @@ -1,23 +1,13 @@ -import random from typing import Optional -from sqlalchemy import Delete, Select, and_, text -from sqlalchemy.orm import selectinload +from sqlalchemy import text -from src.auth.service import CurrentUser -from src.database.core import CollectorDbSession, DbSession -from src.database.service import CommonParameters, search_filter_sort_paginate -from .model import ScopeEquipmentPart -from .schema import ScopeEquipmentActivityCreate, ScopeEquipmentActivityUpdate # async def get(*, db_session: DbSession, scope_equipment_activity_id: str) -> Optional[ScopeEquipmentActivity]: # """Returns a document based on the given document id.""" -# result = await db_session.get(ScopeEquipmentActivity, scope_equipment_activity_id) -# return result from typing import Optional, List, Dict, Any -from sqlalchemy.ext.asyncio import AsyncSession as DbSession from sqlalchemy.sql import text import logging @@ -25,10 +15,6 @@ logger = logging.getLogger(__name__) # async def get_all( # db_session: CollectorDbSession, -# location_tag: Optional[str] = None, -# start_year: int = 2023, -# end_year: Optional[int] = None, -# parent_wonum: Optional[str] = None # ) -> List[Dict[str, Any]]: # """ # Retrieve overhaul spare parts consumption data. @@ -47,14 +33,10 @@ logger = logging.getLogger(__name__) # # --- 1. Basic validation --- # if not parent_wonum: -# logger.warning("Parent WO number not provided. Returning empty result.") -# return [] # if start_year < 1900 or (end_year and end_year < start_year): -# raise ValueError("Invalid year range provided.") # if end_year is None: -# end_year = start_year + 1 # # --- 2. Build SQL safely --- # base_query = """ @@ -65,13 +47,8 @@ logger = logging.getLogger(__name__) # AND xx_parent = :parent_wonum # """ -# params = { -# "parent_wonum": parent_wonum, -# } # if location_tag: -# base_query += " AND location_tag = :location_tag" -# params["location_tag"] = location_tag # base_query += """ # ), @@ -79,7 +56,6 @@ logger = logging.getLogger(__name__) # SELECT wonum, itemnum, itemqty, inv_curbaltotal, inv_avgcost # FROM public.wo_max_material # WHERE wonum IN (SELECT wonum FROM filtered_wo) -# ) # SELECT # fwo.location_tag AS location_tag, # fm.itemnum, @@ -95,36 +71,16 @@ logger = logging.getLogger(__name__) # """ # # --- 3. Execute query --- -# try: -# result = await db_session.execute(text(base_query), params) -# rows = result.fetchall() # # Handle "no data found" # if not rows: -# logger.info(f"No spare part data found for parent WO {parent_wonum}.") -# return [] # # --- 4. Map results cleanly --- -# equipment_parts = [] # for row in rows: -# try: # equipment_parts.append({ -# "location_tag": row.location_tag, -# "itemnum": row.itemnum, -# "sparepart_name": row.sparepart_name or "-", -# "parts_consumed_in_oh": float(row.parts_consumed_in_oh or 0), -# "avgcost": float(row.avgcost or 0), # "inv_curbaltotal": float(row.inv_curbaltotal or 0) -# }) -# except Exception as parse_err: -# logger.error(f"Failed to parse row {row}: {parse_err}") -# continue # Skip malformed rows -# return equipment_parts -# except Exception as e: -# logger.exception(f"Database query failed: {e}") -# raise RuntimeError("Failed to fetch overhaul spare parts data.") from e from typing import List, Dict, Any, Optional from sqlalchemy.ext.asyncio import AsyncSession diff --git a/src/equipment_workscope_group/model.py b/src/equipment_workscope_group/model.py index 0ba3f5c..6433d11 100644 --- a/src/equipment_workscope_group/model.py +++ b/src/equipment_workscope_group/model.py @@ -1,10 +1,8 @@ -from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String -from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy import UUID, Column, ForeignKey, String from sqlalchemy.orm import relationship from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin -from src.workorder.model import MasterWorkOrder +from src.models import DefaultMixin class EquipmentWorkscopeGroup(Base, DefaultMixin): diff --git a/src/equipment_workscope_group/router.py b/src/equipment_workscope_group/router.py index 893844a..02dfbe3 100644 --- a/src/equipment_workscope_group/router.py +++ b/src/equipment_workscope_group/router.py @@ -1,9 +1,8 @@ -from typing import Dict, List, Optional +from typing import Optional -from fastapi import APIRouter, HTTPException, Query, status +from fastapi import APIRouter, Query -from src.database.service import (CommonParameters, DbSession, - search_filter_sort_paginate) +from src.database.service import (CommonParameters, DbSession) from src.models import StandardResponse from .schema import ScopeEquipmentJobCreate, ScopeEquipmentJobPagination @@ -17,7 +16,6 @@ router = APIRouter() ) async def get_jobs(common: CommonParameters, location_tag: str, scope: Optional[str] = Query(None)): """Get all scope pagination.""" - # return results = await get_all(common=common, location_tag=location_tag, scope=scope) return StandardResponse( @@ -31,7 +29,6 @@ async def create_scope_equipment_jobs( db_session: DbSession, assetnum, scope_job_in: ScopeEquipmentJobCreate ): """Get all scope activity pagination.""" - # return await create(db_session=db_session, assetnum=assetnum, scope_job_in=scope_job_in) return StandardResponse( diff --git a/src/equipment_workscope_group/schema.py b/src/equipment_workscope_group/schema.py index 19b9890..07d962d 100644 --- a/src/equipment_workscope_group/schema.py +++ b/src/equipment_workscope_group/schema.py @@ -1,8 +1,7 @@ -from datetime import datetime -from typing import Any, Dict, List, Optional +from typing import List, Optional from uuid import UUID -from pydantic import BaseModel, Field +from pydantic import Field from src.workscope_group.schema import ActivityMasterRead from src.models import DefultBase, Pagination @@ -41,40 +40,19 @@ class ScopeEquipmentJobPagination(Pagination): items: List[ScopeEquipmentJobRead] = [] -# { -# "overview": { -# "totalEquipment": 30, -# "nextSchedule": { -# "date": "2025-01-12", -# "Overhaul": "B", # "equipmentCount": 30 -# } # }, # "criticalParts": [ # "Boiler feed pump", # "Boiler reheater system", -# "Drum Level (Right) Root Valve A", # "BCP A Discharge Valve", # "BFPT A EXH Press HI Root VLV" # ], # "schedules": [ -# { -# "date": "2025-01-12", -# "Overhaul": "B", # "status": "upcoming" -# } # // ... other scheduled overhauls # ], -# "systemComponents": { -# "boiler": { -# "status": "operational", # "lastOverhaul": "2024-06-15" # }, -# "turbine": { -# "hpt": { "status": "operational" }, -# "ipt": { "status": "operational" }, # "lpt": { "status": "operational" } -# } # // ... other major components -# } -# } diff --git a/src/equipment_workscope_group/service.py b/src/equipment_workscope_group/service.py index 192e940..b08ca3d 100644 --- a/src/equipment_workscope_group/service.py +++ b/src/equipment_workscope_group/service.py @@ -1,16 +1,11 @@ -import random from typing import Optional from fastapi import HTTPException, status -from sqlalchemy import Delete, Select, and_ -from sqlalchemy.orm import selectinload +from sqlalchemy import Select -from src.auth.service import CurrentUser from src.database.core import DbSession -from src.database.service import CommonParameters, search_filter_sort_paginate -from src.overhaul_activity.model import OverhaulActivity +from src.database.service import search_filter_sort_paginate from src.standard_scope.model import MasterEquipment -from src.standard_scope.service import get_equipment_level_by_no from src.workscope_group.model import MasterActivity from src.workscope_group_maintenance_type.model import WorkscopeOHType from src.overhaul_scope.model import MaintenanceType @@ -21,37 +16,23 @@ from src.soft_delete import apply_not_deleted_filter # async def get(*, db_session: DbSession, scope_equipment_activity_id: str) -> Optional[ScopeEquipmentActivity]: # """Returns a document based on the given document id.""" -# result = await db_session.get(ScopeEquipmentActivity, scope_equipment_activity_id) -# return result # async def get_all(db_session: DbSession, assetnum: Optional[str], common): # # Example usage # if not assetnum: -# raise ValueError("assetnum parameter is required") # # First get the parent equipment -# equipment_stmt = Select(MasterEquipment).where(MasterEquipment.assetnum == assetnum) -# equipment: MasterEquipment = await db_session.scalar(equipment_stmt) # if not equipment: -# raise ValueError(f"No equipment found with assetnum: {assetnum}") # # Build query for parts -# stmt = ( -# Select(ScopeEquipmentJob) # .where(ScopeEquipmentJob.assetnum == assetnum) # .options( -# selectinload(ScopeEquipmentJob.job), -# selectinload(ScopeEquipmentJob.overhaul_jobs) # .selectinload(OverhaulJob.overhaul_activity) # .selectinload(OverhaulActivity.overhaul_scope), -# ) -# ) -# results = await search_filter_sort_paginate(model=stmt, **common) -# return results async def get_all(*, common, location_tag: str, scope: Optional[str] = None): """Returns all documents.""" @@ -100,17 +81,12 @@ async def create( # async def update(*, db_session: DbSession, activity: ScopeEquipmentActivity, scope_equipment_activty_in: ScopeEquipmentActivityUpdate): # """Updates a document.""" -# data = scope_equipment_activty_in.model_dump() -# update_data = scope_equipment_activty_in.model_dump(exclude_defaults=True) # for field in data: # if field in update_data: -# setattr(activity, field, update_data[field]) -# await db_session.commit() -# return activity async def delete( @@ -148,6 +124,6 @@ async def delete( return True - except Exception as e: + except Exception: await db_session.rollback() raise diff --git a/src/main.py b/src/main.py index 9a9f301..d1aaa07 100644 --- a/src/main.py +++ b/src/main.py @@ -2,39 +2,26 @@ import logging import asyncio import time from src.context import set_request_id, reset_request_id, get_request_id -from contextvars import ContextVar -from os import path -from typing import Final, Optional from uuid import uuid1 -from fastapi import Depends, FastAPI, HTTPException, status +from fastapi import Depends, FastAPI, HTTPException from src.auth.service import JWTBearer -from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse -from pydantic import ValidationError -from slowapi import _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded -from slowapi.middleware import SlowAPIMiddleware -from sqlalchemy import inspect from sqlalchemy.ext.asyncio import async_scoped_session -from sqlalchemy.orm import scoped_session -from starlette.middleware.base import (BaseHTTPMiddleware, - RequestResponseEndpoint) +from starlette.middleware.base import (BaseHTTPMiddleware) from starlette.middleware.gzip import GZipMiddleware from starlette.requests import Request -from starlette.responses import FileResponse, Response, StreamingResponse -from starlette.routing import compile_path -from starlette.staticfiles import StaticFiles +from starlette.responses import Response import uuid as _uuid_module from src.api import api_router -from src.database.core import async_session, engine, async_collector_session +from src.database.core import async_session, async_collector_session from src.enums import ResponseStatus from src.exceptions import handle_exception from src.app_logging import configure_logging from src.middleware import RequestValidationMiddleware, RequestTimeoutMiddleware -# from src.rate_limiter import limiter from fastapi.exceptions import RequestValidationError from starlette.exceptions import HTTPException as StarletteHTTPException from src.csrf_protect import csrf_protect @@ -74,9 +61,7 @@ app.add_exception_handler(StarletteHTTPException, handle_exception) app.add_exception_handler(RequestValidationError, handle_exception) app.add_exception_handler(RateLimitExceeded, _sync_rate_limit_handler) -# app.state.limiter = limiter app.add_middleware(GZipMiddleware, minimum_size=1000) -# app.add_middleware(SlowAPIMiddleware) app.add_middleware(RequestTimeoutMiddleware, timeout=REQUEST_TIMEOUT_SECONDS) # credentials: "include", @@ -160,8 +145,6 @@ security_headers_middleware(app) app.add_middleware(RequestValidationMiddleware) # app.add_middleware( # CSRFProtectMiddleware, -# excluded_paths=["/healthcheck"] -# ) @@ -301,24 +284,9 @@ async def add_security_headers(request: Request, call_next): # class MetricsMiddleware(BaseHTTPMiddleware): # async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: -# method = request.method -# endpoint = request.url.path -# tags = {"method": method, "endpoint": endpoint} - -# try: -# start = time.perf_counter() -# response = await call_next(request) -# elapsed_time = time.perf_counter() - start -# tags.update({"status_code": response.status_code}) -# metric_provider.counter("server.call.counter", tags=tags) -# metric_provider.timer("server.call.elapsed", value=elapsed_time, tags=tags) -# log.debug(f"server.call.elapsed.{endpoint}: {elapsed_time}") -# except Exception as e: -# metric_provider.counter("server.call.exception.counter", tags=tags) + # raise e from None -# return response -# app.add_middleware(ExceptionMiddleware) app.include_router(api_router) diff --git a/src/maximo/service.py b/src/maximo/service.py index 97dc2b6..fe35fdf 100644 --- a/src/maximo/service.py +++ b/src/maximo/service.py @@ -1,10 +1,6 @@ from datetime import datetime from typing import Optional, Union -from sqlalchemy import select, func, cast, Numeric, text -from sqlalchemy.orm import Session -from sqlalchemy import and_ -from sqlalchemy.sql import not_ -from src.maximo.model import WorkOrderData # Assuming this is where your model is +from sqlalchemy import select, text from src.database.core import CollectorDbSession, DbSession from src.overhaul_scope.model import OverhaulScope @@ -107,7 +103,6 @@ ORDER BY avg_cost DESC; # wm.wonum, # SUM( # wm.itemqty * -# COALESCE(wm.inv_avgcost, po.unit_cost, 0) # ) AS parts_total_cost # FROM public.wo_maxim_material wm # LEFT JOIN ( @@ -129,7 +124,6 @@ ORDER BY avg_cost DESC; # COALESCE(w.actservcost, 0) AS service_cost # FROM target_wo w # LEFT JOIN part_costs pc ON w.wonum = pc.wonum -# ) # SELECT # asset_location, # ROUND(SUM(material_cost + service_cost)::numeric / COUNT(wonum), 2) AS avg_cost, @@ -139,19 +133,11 @@ ORDER BY avg_cost DESC; # ORDER BY total_wo_count DESC; # """) -# parent_nums = [] -# result = await collector_db.execute(query, {"parent_nums": parent_nums}) -# data = [] # for row in result: # data.append({ -# "location_tag": row.asset_location, -# "avg_cost": float(row.avg_cost or 0.0), -# "total_wo_count": row.total_wo_count, -# }) -# return {item["location_tag"]: item["avg_cost"] for item in data} async def get_oh_cost_summary(collector_db: CollectorDbSession, last_oh_date:datetime, upcoming_oh_date:datetime): @@ -182,13 +168,11 @@ async def get_oh_cost_summary(collector_db: CollectorDbSession, last_oh_date:dat # LEFT JOIN part_costs pc # ON w.wonum = pc.wonum # WHERE -# w.worktype = 'OH' # AND w.reportdate IS NOT NULL # AND w.actstart IS NOT NULL # AND w.actfinish IS NOT NULL # AND w.asset_unit IN ('3', '00') # AND w.wonum NOT LIKE 'T%' -# ) # SELECT # asset_location, # AVG(total_wo_cost) AS avg_cost diff --git a/src/metrics.py b/src/metrics.py index cd3d1d1..19c0194 100644 --- a/src/metrics.py +++ b/src/metrics.py @@ -1,46 +1,25 @@ -# import logging -# from dispatch.plugins.base import plugins -# from .config import METRIC_PROVIDERS -# log = logging.getLogger(__file__) # class Metrics(object): -# _providers = [] # def __init__(self): # if not METRIC_PROVIDERS: # log.info( # "No metric providers defined via METRIC_PROVIDERS env var. Metrics will not be sent." -# ) -# else: -# self._providers = METRIC_PROVIDERS # def gauge(self, name, value, tags=None): # for provider in self._providers: # log.debug( -# f"Sending gauge metric {name} to provider {provider}. Value: {value} Tags: {tags}" -# ) -# p = plugins.get(provider) -# p.gauge(name, value, tags=tags) # def counter(self, name, value=None, tags=None): # for provider in self._providers: # log.debug( -# f"Sending counter metric {name} to provider {provider}. Value: {value} Tags: {tags}" -# ) -# p = plugins.get(provider) -# p.counter(name, value=value, tags=tags) # def timer(self, name, value, tags=None): # for provider in self._providers: # log.debug( -# f"Sending timer metric {name} to provider {provider}. Value: {value} Tags: {tags}" -# ) -# p = plugins.get(provider) -# p.timer(name, value, tags=tags) -# provider = Metrics() diff --git a/src/middleware.py b/src/middleware.py index 171f417..e4c6fb7 100644 --- a/src/middleware.py +++ b/src/middleware.py @@ -269,34 +269,20 @@ class RequestValidationMiddleware(BaseHTTPMiddleware): # # ------------------------- # # 0. Header validation # # ------------------------- - # header_keys = [key.lower() for key, _ in request.headers.items()] # # Check for duplicate headers - # header_counter = Counter(header_keys) - # duplicate_headers = [key for key, count in header_counter.items() if count > 1] - # ALLOW_DUPLICATE_HEADERS = {'accept', 'accept-encoding', 'accept-language', 'accept-charset', 'cookie'} - # real_duplicates = [h for h in duplicate_headers if h not in ALLOW_DUPLICATE_HEADERS] # if real_duplicates: # raise HTTPException( - # status_code=422, - # detail=f"Duplicate headers detected: {real_duplicates}", - # ) # # Whitelist headers - # unknown_headers = [key for key in header_keys if key not in ALLOWED_HEADERS] # if unknown_headers: - # filtered_unknown = [h for h in unknown_headers if not h.startswith('sec-')] # if filtered_unknown: # raise HTTPException( - # status_code=422, - # detail=f"Unknown headers detected: {filtered_unknown}", - # ) # # Inspect header values # for key, value in request.headers.items(): # if value: - # inspect_value(value, f"header '{key}'") # ------------------------- # 1. Query string limits @@ -400,7 +386,6 @@ class RequestValidationMiddleware(BaseHTTPMiddleware): if content_type.startswith("application/json"): body = await request.body() # if len(body) > MAX_JSON_BODY_SIZE: - # raise HTTPException(status_code=422, detail="JSON body too large") if body: try: @@ -419,7 +404,6 @@ class RequestValidationMiddleware(BaseHTTPMiddleware): return await call_next(request) -import threading import asyncio import json import logging diff --git a/src/models.py b/src/models.py index 34c0118..3b0febf 100644 --- a/src/models.py +++ b/src/models.py @@ -4,12 +4,10 @@ from datetime import datetime from typing import Generic, Optional, TypeVar import pytz -from pydantic import BaseModel, Field, SecretStr -from sqlalchemy import Column, DateTime, String, event, func +from pydantic import BaseModel, SecretStr +from sqlalchemy import Column, DateTime, String, event from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, mapped_column -from src.auth.service import CurrentUser from src.config import TIMEZONE from src.enums import ResponseStatus diff --git a/src/overhaul/router.py b/src/overhaul/router.py index f2575d7..a184cbc 100644 --- a/src/overhaul/router.py +++ b/src/overhaul/router.py @@ -1,6 +1,6 @@ from typing import List -from fastapi import APIRouter, HTTPException, status +from fastapi import APIRouter from src.auth.service import Token from src.database.core import CollectorDbSession, DbSession diff --git a/src/overhaul/schema.py b/src/overhaul/schema.py index 8116f15..0854691 100644 --- a/src/overhaul/schema.py +++ b/src/overhaul/schema.py @@ -1,10 +1,7 @@ -from datetime import datetime -from typing import Any, Dict, List, Optional -from uuid import UUID +from typing import Any, Dict, List from pydantic import BaseModel, Field -from src.models import DefultBase, Pagination from src.overhaul_scope.schema import ScopeRead @@ -33,40 +30,19 @@ class OverhaulRead(OverhaulBase): systemComponents: Dict[str, Any] -# { -# "overview": { -# "totalEquipment": 30, -# "nextSchedule": { -# "date": "2025-01-12", -# "Overhaul": "B", # "equipmentCount": 30 -# } # }, # "criticalParts": [ # "Boiler feed pump", # "Boiler reheater system", -# "Drum Level (Right) Root Valve A", # "BCP A Discharge Valve", # "BFPT A EXH Press HI Root VLV" # ], # "schedules": [ -# { -# "date": "2025-01-12", -# "Overhaul": "B", # "status": "upcoming" -# } # // ... other scheduled overhauls # ], -# "systemComponents": { -# "boiler": { -# "status": "operational", # "lastOverhaul": "2024-06-15" # }, -# "turbine": { -# "hpt": { "status": "operational" }, -# "ipt": { "status": "operational" }, # "lpt": { "status": "operational" } -# } # // ... other major components -# } -# } diff --git a/src/overhaul/service.py b/src/overhaul/service.py index f526cd4..c51b3cc 100644 --- a/src/overhaul/service.py +++ b/src/overhaul/service.py @@ -1,19 +1,15 @@ import asyncio -from typing import Optional import httpx -from sqlalchemy import Delete, Select +from sqlalchemy import Select -from src.auth.service import CurrentUser from src.calculation_target_reliability.service import RBD_SERVICE_API from src.config import TC_RBD_ID from src.database.core import DbSession from src.contribution_util import calculate_contribution from src.overhaul_activity.service import get_standard_scope_by_session_id from src.overhaul_scope.model import OverhaulScope -from src.overhaul_scope.service import get_all as get_all_session from src.overhaul_scope.service import get_overview_overhaul -from src.standard_scope.service import get_by_oh_session_id async def get_overhaul_overview(db_session: DbSession): @@ -30,28 +26,23 @@ async def get_simulation_results(*, simulation_id: str, token: str): } calc_result_url = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}?nodetype=RegularNode" - # plot_result_url = f"{RBD_SERVICE_API}/aeros/simulation/result/plot/{simulation_id}?nodetype=RegularNode" calc_plant_result = f"{RBD_SERVICE_API}/aeros/simulation/result/calc/{simulation_id}/plant" async with httpx.AsyncClient(timeout=300.0) as client: calc_task = client.get(calc_result_url, headers=headers) - # plot_task = client.get(plot_result_url, headers=headers) plant_task = client.get(calc_plant_result, headers=headers) # Run all three requests concurrently calc_response, plant_response = await asyncio.gather(calc_task, plant_task) calc_response.raise_for_status() - # plot_response.raise_for_status() plant_response.raise_for_status() calc_data = calc_response.json()["data"] - # plot_data = plot_response.json()["data"] plant_data = plant_response.json()["data"] return { "calc_result": calc_data, - # "plot_result": plot_data, "plant_result": plant_data } @@ -313,43 +304,25 @@ def get_overhaul_system_components(): # async def get(*, db_session: DbSession, scope_id: str) -> Optional[Scope]: # """Returns a document based on the given document id.""" -# query = Select(Scope).filter(Scope.id == scope_id) -# result = await db_session.execute(query) -# return result.scalars().one_or_none() # async def get_all(*, db_session: DbSession): # """Returns all documents.""" -# query = Select(Scope) -# result = await db_session.execute(query) -# return result.scalars().all() # async def create(*, db_session: DbSession, scope_id: ScopeCreate): # """Creates a new document.""" -# scope = Scope(**scope_id.model_dump()) -# db_session.add(scope) -# await db_session.commit() -# return scope # async def update(*, db_session: DbSession, scope: Scope, scope_id: ScopeUpdate): # """Updates a document.""" -# data = scope_id.model_dump() -# update_data = scope_id.model_dump(exclude_defaults=True) # for field in data: # if field in update_data: -# setattr(scope, field, update_data[field]) -# await db_session.commit() -# return scope # async def delete(*, db_session: DbSession, scope_id: str): # """Deletes a document.""" -# query = Delete(Scope).where(Scope.id == scope_id) -# await db_session.execute(query) -# await db_session.commit() diff --git a/src/overhaul_activity/model.py b/src/overhaul_activity/model.py index 2ff860d..39493dd 100644 --- a/src/overhaul_activity/model.py +++ b/src/overhaul_activity/model.py @@ -1,10 +1,7 @@ -from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String -from sqlalchemy.ext.hybrid import hybrid_property -from sqlalchemy.orm import relationship +from sqlalchemy import UUID, Column, Float, ForeignKey, String from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin -from src.workorder.model import MasterWorkOrder +from src.models import DefaultMixin class OverhaulActivity(Base, DefaultMixin): @@ -18,26 +15,11 @@ class OverhaulActivity(Base, DefaultMixin): service_cost = Column(Float, nullable=False, default=0) status = Column(String, nullable=False, default="pending") -# equipment = relationship( # "MasterEquipment", -# lazy="raise", -# primaryjoin="and_(OverhaulActivity.assetnum == foreign(MasterEquipment.assetnum))", -# uselist=False, # Add this if it's a one-to-one relationship -# ) -# # sparepart_equipments = relationship( # # "ScopeEquipmentPart", -# # lazy="select", # or "joined", "subquery", "dynamic" depending on your needs -# # primaryjoin="OverhaulActivity.assetnum == foreign(ScopeEquipmentPart.assetnum)", -# # uselist=True -# # ) -# overhaul_scope = relationship( # "OverhaulScope", -# lazy="raise", -# ) -# overhaul_jobs = relationship( # "OverhaulJob", back_populates="overhaul_activity", lazy="raise" -# ) diff --git a/src/overhaul_activity/router.py b/src/overhaul_activity/router.py index d7c6ffb..8689e65 100644 --- a/src/overhaul_activity/router.py +++ b/src/overhaul_activity/router.py @@ -1,18 +1,16 @@ -from typing import List, Optional +from typing import Optional from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Query, status from src.csrf_protect import csrf_protect from src.database.core import CollectorDbSession -from src.database.service import (CommonParameters, DbSession, - search_filter_sort_paginate) +from src.database.service import (CommonParameters, DbSession) from src.models import StandardResponse from src.auth.access_control import require_any_role, ALLOWED_ROLES -from .schema import (OverhaulActivityCreate, OverhaulActivityPagination, - OverhaulActivityRead, OverhaulActivityUpdate) -from .service import add_multiple_equipment_to_session, get, get_all, remove_equipment_from_session, update +from .schema import (OverhaulActivityCreate, OverhaulActivityRead) +from .service import add_multiple_equipment_to_session, get, get_all, remove_equipment_from_session router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))]) @@ -28,7 +26,6 @@ async def get_scope_equipments( scope_name: Optional[str] = Query(None), ): """Get all scope activity pagination.""" - # return data = await get_all( common=common, location_tag=location_tag, @@ -52,7 +49,7 @@ async def create_overhaul_equipment( overhaul_session_id: UUID, ): - activity = await add_multiple_equipment_to_session( + await add_multiple_equipment_to_session( db_session=db_session, collector_db=collector_db_session, overhaul_session_id=overhaul_session_id, @@ -82,30 +79,17 @@ async def get_overhaul_equipment( # @router.put( -# "/{overhaul_session}/{assetnum}", -# response_model=StandardResponse[OverhaulActivityRead], -# ) # async def update_scope( # db_session: DbSession, # scope_equipment_activity_in: OverhaulActivityUpdate, # assetnum: str, # ): -# activity = await get(db_session=db_session, assetnum=assetnum) # if not activity: # raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail="A data with this id does not exist.", -# ) # return StandardResponse( -# data=await update( -# db_session=db_session, -# activity=activity, -# scope_equipment_activity_in=scope_equipment_activity_in, # ), -# message="Data updated successfully", -# ) @router.post( diff --git a/src/overhaul_activity/schema.py b/src/overhaul_activity/schema.py index 7c872b6..46147e4 100644 --- a/src/overhaul_activity/schema.py +++ b/src/overhaul_activity/schema.py @@ -1,11 +1,10 @@ from datetime import datetime -from typing import Any, Dict, List, Optional +from typing import List, Optional from uuid import UUID from pydantic import Field from src.models import DefultBase, Pagination -from src.standard_scope.schema import MasterEquipmentTree from src.workscope_group.schema import ActivityMasterRead class OverhaulActivityBase(DefultBase): @@ -41,9 +40,6 @@ class OverhaulActivityRead(OverhaulActivityBase): equipment_name: Optional[str] oh_scope: str overhaul_cost: Optional[float] = Field(0, ge=0, le=1000000000000000) - # equipment: MasterEquipmentTree - # overhaul_scope: OverhaulScope - # overhaul_jobs: Optional[List[OverhaulJob]] = Field([]) class OverhaulActivityPagination(Pagination): diff --git a/src/overhaul_activity/service.py b/src/overhaul_activity/service.py index dac2569..d924cac 100644 --- a/src/overhaul_activity/service.py +++ b/src/overhaul_activity/service.py @@ -1,23 +1,16 @@ -import asyncio import datetime from typing import List, Optional from uuid import UUID, uuid4 from fastapi import HTTPException, status -from sqlalchemy import Delete, Select, and_, func, select -from sqlalchemy import update as sqlUpdate -from sqlalchemy.dialects.postgresql import insert +from sqlalchemy import Select, and_, select from sqlalchemy.orm import joinedload, selectinload -from src.auth.service import CurrentUser from src.database.core import DbSession from src.database.service import CommonParameters, search_filter_sort_paginate -from src.overhaul_activity.utils import get_material_cost, get_service_cost from src.utils import update_model -from src.overhaul_scope.model import OverhaulScope from src.overhaul_scope.service import get as get_session, get_prev_oh from src.standard_scope.model import MasterEquipment, StandardScope -from src.standard_scope.service import get_by_oh_session_id from src.workscope_group.model import MasterActivity from src.equipment_workscope_group.model import EquipmentWorkscopeGroup from src.overhaul_scope.model import MaintenanceType @@ -25,7 +18,7 @@ from src.workscope_group_maintenance_type.model import WorkscopeOHType from src.overhaul_scope.service import get as get_overhaul from src.standard_scope.model import EquipmentOHHistory from .model import OverhaulActivity -from .schema import (OverhaulActivityCreate, OverhaulActivityRead, +from .schema import (OverhaulActivityRead, OverhaulActivityUpdate) import json @@ -65,31 +58,17 @@ async def get_all( all: bool = False, collector_db: CollectorDbSession ): - # query = ( - # Select(OverhaulActivity) # .where(OverhaulActivity.overhaul_scope_id == overhaul_session_id) # .options(joinedload(OverhaulActivity.equipment).options(joinedload(MasterEquipment.parent).options(joinedload(MasterEquipment.parent)))) # .options(selectinload(OverhaulActivity.overhaul_scope)) # .options(selectinload(OverhaulActivity.overhaul_jobs).options(joinedload(OverhaulJob.scope_equipment_job).options(joinedload(ScopeEquipmentJob.job)))) - # ) # if assetnum: - # query = query.filter(OverhaulActivity.assetnum == assetnum).options( - # joinedload(OverhaulActivity.overhaul_scope) - # ) # if scope_name: - # query = query.filter(OverhaulActivity.scope_name == scope_name).options( - # joinedload(OverhaulActivity.overhaul_scope) - # ) - # results = await search_filter_sort_paginate(model=query, **common) - ##raise Exception(results['items'][0].equipment.parent.__dict__) - # equipments, overhaul = await get_by_oh_session_id( - # db_session=db_session, oh_session_id=overhaul_session_id - # ) overhaul = await get_overhaul(db_session=common['db_session'], overhaul_session_id=overhaul_session_id) prev_oh_scope = await get_prev_oh(db_session=common['db_session'], overhaul_session=overhaul) @@ -191,7 +170,6 @@ async def get_standard_scope_by_session_id(*, db_session: DbSession, overhaul_se results = [] material_cost = await get_cm_cost_summary(collector_db=collector_db, last_oh_date=prev_oh_scope.end_date, upcoming_oh_date=overhaul.start_date) - #service_cost = get_service_cost(scope=overhaul.maintenance_type.name, total_equipment=len(eqs)) overhaul_cost = await get_oh_cost_summary(collector_db=collector_db, last_oh_date=prev_oh_scope.end_date, upcoming_oh_date=overhaul.start_date) for equipment in eqs: diff --git a/src/overhaul_gantt/router.py b/src/overhaul_gantt/router.py index 28b9f0f..6c95807 100644 --- a/src/overhaul_gantt/router.py +++ b/src/overhaul_gantt/router.py @@ -1,18 +1,15 @@ import re -from typing import List, Optional -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, Depends from sqlalchemy import select -from src.auth.service import CurrentUser, Admin +from src.auth.service import Admin from src.csrf_protect import csrf_protect from src.database.core import DbSession -from src.database.service import CommonParameters from src.models import StandardResponse from src.overhaul_gantt.model import OverhaulGantt from src.overhaul_gantt.schema import OverhaulGanttIn -# from .schema import (OverhaulScheduleCreate, OverhaulSchedulePagination, OverhaulScheduleUpdate) from .service import get_gantt_performance_chart router = APIRouter() @@ -23,7 +20,6 @@ router = APIRouter() ) async def get_gantt_performance(db_session: DbSession): """Get all scope pagination.""" - # return query = select(OverhaulGantt).limit(1) data = (await db_session.execute(query)).scalar_one_or_none() @@ -43,7 +39,6 @@ async def get_gantt_performance(db_session: DbSession): ) async def get_gantt_spreadsheet(db_session: DbSession): """Get all scope pagination.""" - # return query = select(OverhaulGantt).limit(1) data = (await db_session.execute(query)).scalar_one_or_none() @@ -69,7 +64,6 @@ async def get_gantt_spreadsheet(db_session: DbSession): ) async def update_gantt_spreadsheet(db_session: DbSession, spreadsheet_in: OverhaulGanttIn, admin: Admin): """Get all scope pagination.""" - # return match = re.search(r"/d/([a-zA-Z0-9-_]+)", spreadsheet_in.spreadsheet_link) if not match: @@ -113,31 +107,17 @@ async def update_gantt_spreadsheet(db_session: DbSession, spreadsheet_in: Overha # db_session: DbSession, overhaul_job_in: OverhaulScheduleCreate # ): # await create( -# db_session=db_session, -# overhaul_job_in=overhaul_job_in, -# ) # return StandardResponse( -# data=None, -# message="Data created successfully", -# ) # @router.put("/{overhaul_job_id}", response_model=StandardResponse[None]) # async def update_overhaul_schedule( # db_session: DbSession, overhaul_job_id: str, overhaul_job_in: OverhaulScheduleUpdate # ): -# await update(db_session=db_session, overhaul_schedule_id=overhaul_job_id, overhaul_job_in=overhaul_job_in) # return StandardResponse( -# data=None, -# message="Data updated successfully", -# ) # @router.delete("/{overhaul_job_id}", response_model=StandardResponse[None]) # async def delete_overhaul_equipment_job(db_session: DbSession, overhaul_job_id): -# await delete(db_session=db_session, overhaul_schedule_id=overhaul_job_id) # return StandardResponse( -# data=None, -# message="Data deleted successfully", -# ) diff --git a/src/overhaul_gantt/schema.py b/src/overhaul_gantt/schema.py index 178e239..1dfa5db 100644 --- a/src/overhaul_gantt/schema.py +++ b/src/overhaul_gantt/schema.py @@ -1,13 +1,5 @@ -# from datetime import datetime -# from typing import List, Optional -# from uuid import UUID -# from pydantic import Field -# from src.models import DefultBase, Pagination -# from src.overhaul_scope.schema import ScopeRead -# from src.scope_equipment_job.schema import ScopeEquipmentJobRead -# from src.job.schema import ActivityMasterRead from pydantic import Field from src.models import DefultBase @@ -18,31 +10,13 @@ class OverhaulGanttIn(DefultBase): # class OverhaulScheduleCreate(OverhaulScheduleBase): -# year: int -# plan_duration: Optional[int] = Field(None) -# planned_outage: Optional[int] = Field(None) -# actual_shutdown: Optional[int] = Field(None) -# start: datetime -# finish: datetime -# remark: Optional[str] = Field(None) # class OverhaulScheduleUpdate(OverhaulScheduleBase): -# start: datetime -# finish: datetime # class OverhaulScheduleRead(OverhaulScheduleBase): -# id: UUID -# year: int -# plan_duration: Optional[int] -# planned_outage: Optional[int] -# actual_shutdown: Optional[int] -# start: datetime -# finish: datetime -# remark: Optional[str] # class OverhaulSchedulePagination(Pagination): -# items: List[OverhaulScheduleRead] = [] diff --git a/src/overhaul_gantt/service.py b/src/overhaul_gantt/service.py index b9ac9e0..3a0b293 100644 --- a/src/overhaul_gantt/service.py +++ b/src/overhaul_gantt/service.py @@ -1,19 +1,11 @@ -from typing import Optional from fastapi import HTTPException, status -from sqlalchemy import Delete, Select, func -from sqlalchemy.orm import selectinload -# from .model import OverhaulSchedule -# from .schema import OverhaulScheduleCreate, OverhaulScheduleUpdate from .utils import fetch_all_sections, get_google_creds, get_spreatsheed_service, process_spreadsheet_data # async def get_all(*, common): # """Returns all documents.""" -# query = Select(OverhaulSchedule).order_by(OverhaulSchedule.start.desc()) -# results = await search_filter_sort_paginate(model=query, **common) -# return results # async def create( @@ -21,34 +13,21 @@ from .utils import fetch_all_sections, get_google_creds, get_spreatsheed_service # ): -# schedule = OverhaulSchedule(**overhaul_job_in.model_dump()) -# db_session.add(schedule) -# await db_session.commit() -# return schedule # async def update(*, db_session: DbSession, overhaul_schedule_id: str, overhaul_job_in: OverhaulScheduleUpdate): # """Updates a document.""" -# data = overhaul_job_in.model_dump() -# overhaul_schedule = await db_session.get(OverhaulSchedule, overhaul_schedule_id) -# update_data = overhaul_job_in.model_dump(exclude_defaults=True) # for field in data: # if field in update_data: -# setattr(overhaul_schedule, field, update_data[field]) -# await db_session.commit() -# return overhaul_schedule # async def delete(*, db_session: DbSession, overhaul_schedule_id: str): # """Deletes a document.""" -# query = Delete(OverhaulSchedule).where(OverhaulSchedule.id == overhaul_schedule_id) -# await db_session.execute(query) -# await db_session.commit() async def get_gantt_performance_chart(*, spreadsheet_id = "1gZXuwA97zU1v4QBv56wKeiqadc6skHUucGKYG8qVFRk"): diff --git a/src/overhaul_gantt/utils.py b/src/overhaul_gantt/utils.py index ae8367b..9bead2d 100644 --- a/src/overhaul_gantt/utils.py +++ b/src/overhaul_gantt/utils.py @@ -1,8 +1,5 @@ -import urllib from google.oauth2.service_account import Credentials -from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build -from googleapiclient.errors import HttpError SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"] diff --git a/src/overhaul_job/model.py b/src/overhaul_job/model.py index bcfb7da..1386a8c 100644 --- a/src/overhaul_job/model.py +++ b/src/overhaul_job/model.py @@ -1,43 +1,16 @@ -from sqlalchemy import (UUID, Column, DateTime, Float, ForeignKey, Integer, - String) -from sqlalchemy.orm import relationship -from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin # class EquipmentWorkscopeGroup(Base, DefaultMixin): -# __tablename__ = "oh_tr_equipment_workscope_group" -# # overhaul_activity_id = Column( # # UUID(as_uuid=True), ForeignKey("oh_tr_overhaul_activity.id"), nullable=False -# # ) -# # scope_equipment_job_id = Column( -# # UUID(as_uuid=True), -# # ForeignKey("oh_ms_scope_equipment_job.id", ondelete="cascade"), -# # nullable=False, -# # ) -# # notes = Column(String, nullable=True) -# # status = Column(String, nullable=True, default="pending") -# workscope_group_id = Column( # UUID(as_uuid=True), ForeignKey("oh_ms_workscope_group.id"), nullable=False -# ) -# location_tag = Column(String, nullable=False) -# equipment = relationship( # "StandardScope", -# lazy="selectin", -# primaryjoin="and_(OverhaulJob.location_tag == foreign(StandardScope.location_tag))", -# uselist=False, # Add this if it's a one-to-one relationship -# ) -# workscope_group = relationship("MasterActivity", lazy="selectin", back_populates="equipment_workscope_groups") -# # scope_equipment_job = relationship( # # "ScopeEquipmentJob", lazy="raise", back_populates="overhaul_jobs" -# # ) -# # overhaul_activity = relationship("OverhaulActivity", lazy="raise", back_populates="overhaul_jobs") diff --git a/src/overhaul_job/router.py b/src/overhaul_job/router.py index f92b15a..957c5bc 100644 --- a/src/overhaul_job/router.py +++ b/src/overhaul_job/router.py @@ -1,14 +1,12 @@ -from typing import List, Optional +from typing import Optional -from fastapi import APIRouter, HTTPException, status, Query +from fastapi import APIRouter, Query -from src.auth.service import CurrentUser from src.database.core import DbSession from src.database.service import CommonParameters from src.models import StandardResponse -from .schema import (OverhaulJobBase, OverhaulJobCreate, OverhaulJobPagination, - OverhaulJobRead) +from .schema import (OverhaulJobCreate, OverhaulJobPagination) from .service import create, delete, get_all router = APIRouter() @@ -19,7 +17,6 @@ router = APIRouter() ) async def get_jobs(common: CommonParameters, location_tag: str, scope: Optional[str] = Query(None)): """Get all scope pagination.""" - # return results = await get_all(common=common, location_tag=location_tag, scope=scope) return StandardResponse( @@ -33,7 +30,6 @@ async def create_overhaul_equipment_jobs( db_session: DbSession, overhaul_equipment_id, overhaul_job_in: OverhaulJobCreate ): """Get all scope activity pagination.""" - # return await create( db_session=db_session, overhaul_equipment_id=overhaul_equipment_id, @@ -58,34 +54,21 @@ async def delete_overhaul_equipment_job(db_session: DbSession, overhaul_job_id): # @router.post("", response_model=StandardResponse[List[str]]) # async def create_scope(db_session: DbSession, scope_in: OverhaulJobCreate): -# overhaul_job = await create(db_session=db_session, scope_in=scope_in) -# return StandardResponse(data=overhaul_job, message="Data created successfully") # @router.put("/{scope_id}", response_model=StandardResponse[ScopeRead]) # async def update_scope(db_session: DbSession, scope_id: str, scope_in: ScopeUpdate, current_user: CurrentUser): -# scope = await get(db_session=db_session, scope_id=scope_id) # if not scope: # raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail="A data with this id does not exist.", -# ) -# return StandardResponse(data=await update(db_session=db_session, scope=scope, scope_in=scope_in), message="Data updated successfully") # @router.delete("/{scope_id}", response_model=StandardResponse[ScopeRead]) # async def delete_scope(db_session: DbSession, scope_id: str): -# scope = await get(db_session=db_session, scope_id=scope_id) # if not scope: # raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail=[{"msg": "A data with this id does not exist."}], -# ) -# await delete(db_session=db_session, scope_id=scope_id) -# return StandardResponse(message="Data deleted successfully", data=scope) diff --git a/src/overhaul_job/schema.py b/src/overhaul_job/schema.py index ddbcc8c..c8ba9b5 100644 --- a/src/overhaul_job/schema.py +++ b/src/overhaul_job/schema.py @@ -1,12 +1,9 @@ -from datetime import datetime from typing import List, Optional from uuid import UUID -from pydantic import Field from src.models import DefultBase, Pagination from src.overhaul_scope.schema import ScopeRead -from src.scope_equipment_job.schema import ScopeEquipmentJobRead from src.job.schema import ActivityMasterRead class OverhaulJobBase(DefultBase): diff --git a/src/overhaul_job/service.py b/src/overhaul_job/service.py index dc739ba..df63f5f 100644 --- a/src/overhaul_job/service.py +++ b/src/overhaul_job/service.py @@ -1,14 +1,10 @@ from typing import Optional from fastapi import HTTPException, status -from sqlalchemy import Delete, Select, func -from sqlalchemy.orm import selectinload +from sqlalchemy import Select -from src.auth.service import CurrentUser from src.database.core import DbSession from src.database.service import search_filter_sort_paginate -# from src.scope_equipment_job.model import ScopeEquipmentJob -# from src.overhaul_activity.model import OverhaulActivity from src.workscope_group.model import MasterActivity from src.workscope_group_maintenance_type.model import WorkscopeOHType @@ -51,7 +47,7 @@ async def create( OverhaulJob.overhaul_activity_id == overhaul_equipment_id ) - equipment = await db_session.scalar(equipment_stmt) + await db_session.scalar(equipment_stmt) for job_id in overhaul_job_in.job_ids: overhaul_equipment_job = OverhaulJob( @@ -96,28 +92,20 @@ async def delete( return True - except Exception as e: + except Exception: await db_session.rollback() raise # async def update(*, db_session: DbSession, scope: OverhaulScope, scope_in: ScopeUpdate): # """Updates a document.""" -# data = scope_in.model_dump() -# update_data = scope_in.model_dump(exclude_defaults=True) # for field in data: # if field in update_data: -# setattr(scope, field, update_data[field]) -# await db_session.commit() -# return scope # async def delete(*, db_session: DbSession, scope_id: str): # """Deletes a document.""" -# query = Delete(OverhaulScope).where(OverhaulScope.id == scope_id) -# await db_session.execute(query) -# await db_session.commit() diff --git a/src/overhaul_schedule/model.py b/src/overhaul_schedule/model.py index 707b009..e2d71ab 100644 --- a/src/overhaul_schedule/model.py +++ b/src/overhaul_schedule/model.py @@ -1,9 +1,8 @@ -from sqlalchemy import (UUID, Column, DateTime, Float, ForeignKey, Integer, +from sqlalchemy import (Column, DateTime, Integer, String) -from sqlalchemy.orm import relationship from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin +from src.models import DefaultMixin class OverhaulSchedule(Base, DefaultMixin): diff --git a/src/overhaul_schedule/router.py b/src/overhaul_schedule/router.py index edc084b..e3c6ef5 100644 --- a/src/overhaul_schedule/router.py +++ b/src/overhaul_schedule/router.py @@ -1,8 +1,6 @@ -from typing import List, Optional -from fastapi import APIRouter, HTTPException, status +from fastapi import APIRouter -from src.auth.service import CurrentUser from src.database.core import DbSession from src.database.service import CommonParameters from src.models import StandardResponse @@ -18,7 +16,6 @@ router = APIRouter() ) async def get_schedules(common: CommonParameters): """Get all scope pagination.""" - # return results = await get_all(common=common) diff --git a/src/overhaul_schedule/schema.py b/src/overhaul_schedule/schema.py index e14e866..71f9160 100644 --- a/src/overhaul_schedule/schema.py +++ b/src/overhaul_schedule/schema.py @@ -5,9 +5,6 @@ from uuid import UUID from pydantic import Field from src.models import DefultBase, Pagination -from src.overhaul_scope.schema import ScopeRead -from src.scope_equipment_job.schema import ScopeEquipmentJobRead -from src.job.schema import ActivityMasterRead class OverhaulScheduleBase(DefultBase): pass diff --git a/src/overhaul_schedule/service.py b/src/overhaul_schedule/service.py index 086fff5..1aec55f 100644 --- a/src/overhaul_schedule/service.py +++ b/src/overhaul_schedule/service.py @@ -1,15 +1,9 @@ -from typing import Optional -from fastapi import HTTPException, status -from sqlalchemy import Delete, Select, func -from sqlalchemy.orm import selectinload +from sqlalchemy import Select -from src.auth.service import CurrentUser from src.database.core import DbSession from src.database.service import search_filter_sort_paginate from src.utils import update_model -from src.scope_equipment_job.model import ScopeEquipmentJob -from src.overhaul_activity.model import OverhaulActivity from src.soft_delete import apply_not_deleted_filter, soft_delete_record from .model import OverhaulSchedule @@ -39,7 +33,7 @@ async def create( async def update(*, db_session: DbSession, overhaul_schedule_id: str, overhaul_job_in: OverhaulScheduleUpdate): """Updates a document.""" - data = overhaul_job_in.model_dump() + overhaul_job_in.model_dump() query = Select(OverhaulSchedule).where(OverhaulSchedule.id == overhaul_schedule_id) query = apply_not_deleted_filter(query, OverhaulSchedule) query = query.with_for_update() diff --git a/src/overhaul_scope/model.py b/src/overhaul_scope/model.py index 280227b..59ae3a8 100644 --- a/src/overhaul_scope/model.py +++ b/src/overhaul_scope/model.py @@ -3,7 +3,7 @@ from sqlalchemy import Column, DateTime , Integer, String, ForeignKey, UUID from sqlalchemy.orm import relationship from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin +from src.models import DefaultMixin from src.auth.access_control import Allow, RolePrincipal, OHPermission @@ -39,7 +39,6 @@ class OverhaulScope(Base, DefaultMixin): maintenance_type = relationship("MaintenanceType", lazy="selectin", backref="overhaul_scopes") - # activity_equipments = relationship("OverhaulActivity", lazy="selectin") class MaintenanceType(Base, DefaultMixin): diff --git a/src/overhaul_scope/router.py b/src/overhaul_scope/router.py index 93a69be..70fef11 100644 --- a/src/overhaul_scope/router.py +++ b/src/overhaul_scope/router.py @@ -5,7 +5,7 @@ from fastapi import APIRouter, HTTPException, status from src.auth.service import CurrentUser from src.database.core import DbSession -from src.database.service import CommonParameters, search_filter_sort_paginate +from src.database.service import CommonParameters from src.models import StandardResponse from .model import OverhaulScope @@ -21,7 +21,6 @@ router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))]) @router.get("", response_model=StandardResponse[ScopePagination], dependencies=[Depends(required_permission(OHPermission.READ, OverhaulScope))]) async def get_scopes(common: CommonParameters, scope_name: Optional[str] = None): """Get all scope pagination.""" - # return results = await get_all(common=common, scope_name=scope_name) return StandardResponse( diff --git a/src/overhaul_scope/service.py b/src/overhaul_scope/service.py index 4bfe017..8fcf7c6 100644 --- a/src/overhaul_scope/service.py +++ b/src/overhaul_scope/service.py @@ -1,9 +1,8 @@ from typing import Optional -from sqlalchemy import Delete, Select, func +from sqlalchemy import Select from sqlalchemy.orm import selectinload -from src.auth.service import CurrentUser from src.database.core import DbSession from src.database.service import search_filter_sort_paginate from src.overhaul_activity.model import OverhaulActivity @@ -102,7 +101,6 @@ async def create(*, db_session: DbSession, scope_in: ScopeCreate): status=scope_in.status # Hardcoded status ) - # raise Exception(overhaul_session.start_date) db_session.add(overhaul_session) # Need to flush to get the id diff --git a/src/rate_limiter.py b/src/rate_limiter.py index 18a403c..8b13789 100644 --- a/src/rate_limiter.py +++ b/src/rate_limiter.py @@ -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, -# ) \ No newline at end of file diff --git a/src/sparepart/model.py b/src/sparepart/model.py index 522a3fc..ab1c3e8 100644 --- a/src/sparepart/model.py +++ b/src/sparepart/model.py @@ -1,10 +1,8 @@ from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String, Date -from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import relationship from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin -from src.workorder.model import MasterWorkOrder +from src.models import DefaultMixin class MasterSparePart(Base, DefaultMixin): diff --git a/src/sparepart/router.py b/src/sparepart/router.py index 4c05653..01111e7 100644 --- a/src/sparepart/router.py +++ b/src/sparepart/router.py @@ -1,7 +1,7 @@ -from fastapi import APIRouter, Depends, HTTPException, Query, status +from fastapi import APIRouter, Depends from src.csrf_protect import csrf_protect from src.database.core import CollectorDbSession -from src.database.service import (CommonParameters, DbSession, search_filter_sort_paginate) +from src.database.service import (DbSession) from src.models import StandardResponse from src.sparepart.schema import SparepartRemark from src.auth.access_control import require_any_role, ALLOWED_ROLES @@ -13,7 +13,6 @@ router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))]) @router.get("", response_model=StandardResponse[list]) async def get_sparepart(collector_db_session:CollectorDbSession, db_session: DbSession): """Get all scope activity pagination.""" - # return data = await get_spareparts_paginated(db_session=db_session, collector_db_session=collector_db_session) return StandardResponse( @@ -36,59 +35,35 @@ async def create_remark_route(collector_db_session:CollectorDbSession, db_sessio # @router.post("", response_model=StandardResponse[ActivityMasterCreate]) # async def create_activity(db_session: DbSession, activity_in: ActivityMasterCreate): -# activity = await create(db_session=db_session, activty_in=activity_in) -# return StandardResponse(data=activity, message="Data created successfully") # @router.get( # "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster] -# ) # async def get_activity(db_session: DbSession, activity_id: str): -# activity = await get(db_session=db_session, activity_id=activity_id) # if not activity: # raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail="A data with this id does not exist.", -# ) -# return StandardResponse(data=activity, message="Data retrieved successfully") # @router.put( # "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster] -# ) # async def update_scope( # db_session: DbSession, activity_in: ActivityMasterCreate, activity_id # ): -# activity = await get(db_session=db_session, activity_id=activity_id) # if not activity: # raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail="A data with this id does not exist.", -# ) # return StandardResponse( -# data=await update( -# db_session=db_session, activity=activity, activity_in=activity_in # ), -# message="Data updated successfully", -# ) # @router.delete( # "/{scope_equipment_activity_id}", response_model=StandardResponse[ActivityMaster] -# ) # async def delete_scope(db_session: DbSession, activity_id: str): -# activity = await get(db_session=db_session, activity_id=activity_id) # if not activity: # raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail=[{"msg": "A data with this id does not exist."}], -# ) -# await delete(db_session=db_session, activity_id=activity_id) -# return StandardResponse(message="Data deleted successfully", data=activity) diff --git a/src/sparepart/schema.py b/src/sparepart/schema.py index 7022d09..7ab0aa6 100644 --- a/src/sparepart/schema.py +++ b/src/sparepart/schema.py @@ -1,10 +1,9 @@ from dataclasses import dataclass -from datetime import date, datetime +from datetime import date from enum import Enum -from typing import Any, Dict, List, Optional +from typing import List from uuid import UUID -from pydantic import BaseModel, Field from src.models import DefultBase, Pagination diff --git a/src/sparepart/service.py b/src/sparepart/service.py index 23f0fbe..2dbe826 100644 --- a/src/sparepart/service.py +++ b/src/sparepart/service.py @@ -1,23 +1,12 @@ -from typing import Optional -import asyncio import logging from datetime import datetime, timedelta, date -from typing import List, Dict, Optional, Tuple +from typing import List, Dict, Tuple from collections import defaultdict -from uuid import UUID -import numpy as np -from dataclasses import dataclass -from enum import Enum -from sqlalchemy import Delete, Select, select, text -from sqlalchemy.orm import joinedload, selectinload +from sqlalchemy import select, text -from src.auth.service import CurrentUser -from src.database.core import DbSession -from src.database.service import CommonParameters, search_filter_sort_paginate from src.overhaul_activity.service import get_standard_scope_by_session_id -from src.overhaul_scope.service import get as get_scope, get_overview_overhaul -from src.overhaul_scope.service import get_prev_oh +from src.overhaul_scope.service import get_overview_overhaul from src.sparepart.model import SparepartRemark from src.sparepart.schema import ProcurementRecord, ProcurementStatus, SparepartRequirement, SparepartStock from src.soft_delete import apply_not_deleted_filter @@ -26,7 +15,6 @@ from src.soft_delete import apply_not_deleted_filter log = logging.getLogger(__name__) from sqlalchemy import text -import math from sqlalchemy import text @@ -41,8 +29,6 @@ from sqlalchemy import text # """ # # Normalize parent_num to array for SQL ANY() -# # parent_nums = parent_num if isinstance(parent_num, (list, tuple)) else [parent_num] -# parent_nums = [] # data_query = text(""" # WITH selected_wo AS ( @@ -154,12 +140,10 @@ from sqlalchemy import text # 'po_vendeliverydate', po_vendeliverydate, # 'po_receipt', po_receipt, # 'po_status', po_status -# ) # ORDER BY pr_issue_date DESC # ) AS pr_po_details # FROM pr_po_unified # GROUP BY item_num -# ) # SELECT # wm.itemnum, @@ -181,22 +165,10 @@ from sqlalchemy import text # ORDER BY wm.itemnum; # """) -# rows = await collector_db_session.execute(data_query, {"parent_nums": parent_nums}) -# spare_parts = [] # for row in rows: # spare_parts.append({ -# "item_num": row.itemnum, -# "description": row.item_description, -# "current_balance_total": float(row.current_balance_total or 0.0), -# "total_required_for_oh": float(row.total_required_for_oh or 0.0), -# "total_pr_qty": row.total_pr_qty, -# "total_po_qty": row.total_po_qty, -# "total_po_received": row.total_po_received, -# "pr_po_details": row.pr_po_details, -# }) -# return spare_parts async def get_spareparts_paginated(*, db_session, collector_db_session): @@ -210,8 +182,6 @@ async def get_spareparts_paginated(*, db_session, collector_db_session): items_per_page (int): Number of items per page """ # calculate limit/offset - # limit = items_per_page - # offset = (page - 1) * items_per_page # wo_materials AS ( # SELECT @@ -918,15 +888,11 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s """Load sparepart data from database""" # You'll need to implement these queries based on your database schema # Get scope dates for analysis window - # scope = await get_scope(db_session=db_session, overhaul_session_id=overhaul_session_id) - # prev_oh_scope = await get_prev_oh(db_session=db_session, overhaul_session=scope) analysis_start_date = prev_oh_scope.end_date analysis_window_months = int(((scope.start_date - prev_oh_scope.end_date).days / 30) * 1.2) if not analysis_window_months else analysis_window_months sparepart_manager = SparepartManager(analysis_start_date, analysis_window_months) - start_date = prev_oh_scope.end_date - end_date = scope.start_date # Load sparepart stocks # Example query - adjust based on your schema @@ -963,7 +929,6 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s ) sparepart_manager.add_sparepart_stock(stock) - # parent_nums = [] # query = text(""" # WITH target_wo AS ( @@ -1039,17 +1004,14 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s # item_num, # ROUND(CAST(AVG( # EXTRACT(EPOCH FROM ( - # COALESCE(vendeliverydate, estimated_arrival_date) - issue_date # )) / 86400 / 30.44 # ) AS NUMERIC), 1) as avg_leadtime_months, # ROUND(CAST(MIN( # EXTRACT(EPOCH FROM ( - # COALESCE(vendeliverydate, estimated_arrival_date) - issue_date # )) / 86400 / 30.44 # ) AS NUMERIC), 1) as min_leadtime_months, # ROUND(CAST(MAX( # EXTRACT(EPOCH FROM ( - # COALESCE(vendeliverydate, estimated_arrival_date) - issue_date # )) / 86400 / 30.44 # ) AS NUMERIC), 1) as max_leadtime_months, # COUNT(*) as leadtime_sample_size, @@ -1083,7 +1045,6 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s # ) as description # FROM public.maximo_sparepart_pr_po_line # WHERE description IS NOT NULL - # ) # SELECT # tr.asset_location, # tr.itemnum, @@ -1100,22 +1061,10 @@ async def load_sparepart_data_from_db(scope, prev_oh_scope, db_session, app_db_s # ORDER BY tr.asset_location, tr.itemnum; # """) - # equipment_requirements_query = await db_session.execute(query, {"parent_nums": parent_nums}) - # equipment_requirements = defaultdict(list) # for req_record in equipment_requirements_query: - # requirement = SparepartRequirement( - # sparepart_id=req_record.itemnum, - # quantity_required=float(req_record.total_required_for_oh or 0.0), - # lead_time=float(req_record.avg_leadtime_months or 0.0), - # sparepart_name=req_record.item_description, - # unit_cost=float(req_record.avg_unit_cost or 0.0), - # avg_cost=float(req_record.inv_avgcost or 0.0), - # ) - # equipment_requirements[req_record.asset_location].append(requirement) # for equipment_tag, requirements in equipment_requirements.items(): - # sparepart_manager.add_equipment_requirements(equipment_tag, requirements) # Load equipment sparepart requirements # You'll need to create this table/relationship diff --git a/src/standard_scope/model.py b/src/standard_scope/model.py index c963fd9..358eb77 100644 --- a/src/standard_scope/model.py +++ b/src/standard_scope/model.py @@ -1,10 +1,8 @@ from sqlalchemy import UUID, Column, Date, Float, ForeignKey, Integer, String, Boolean -from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import relationship from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin -from src.workorder.model import MasterWorkOrder +from src.models import DefaultMixin class StandardScope(Base, DefaultMixin): diff --git a/src/standard_scope/router.py b/src/standard_scope/router.py index dbf0f40..2cc9a47 100644 --- a/src/standard_scope/router.py +++ b/src/standard_scope/router.py @@ -1,18 +1,16 @@ -from typing import List, Optional +from typing import List -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, Depends from fastapi.params import Query -from src.auth.service import CurrentUser from src.database.core import DbSession, CollectorDbSession -from src.database.service import CommonParameters, search_filter_sort_paginate +from src.database.service import CommonParameters from src.models import StandardResponse from src.auth.access_control import require_any_role, ALLOWED_ROLES from .schema import (MasterEquipmentPagination, ScopeEquipmentCreate, - ScopeEquipmentPagination, ScopeEquipmentRead, - ScopeEquipmentUpdate) -from .service import (create, delete, get_all, get_all_master_equipment, update, get_history_standard_scope_wo_service) + ScopeEquipmentPagination) +from .service import (create, get_all, get_all_master_equipment, get_history_standard_scope_wo_service) from uuid import UUID router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))]) @@ -20,7 +18,6 @@ router = APIRouter(dependencies=[Depends(require_any_role(*ALLOWED_ROLES))]) @router.get("", response_model=StandardResponse[ScopeEquipmentPagination]) async def get_scope_equipments(common: CommonParameters, scope_name: str = Query(None)): """Get all scope pagination.""" - # return data = await get_all(common=common, oh_scope=scope_name) return StandardResponse( @@ -59,34 +56,18 @@ async def get_history_standard_scope_wo( # async def update_scope_equipment( # db_session: DbSession, assetnum: str, scope__equipment_in: ScopeEquipmentUpdate # ): -# scope_equipment = await get_by_assetnum(db_session=db_session, assetnum=assetnum) # if not scope_equipment: # raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail="A data with this id does not exist.", -# ) # return StandardResponse( -# data=await update( -# db_session=db_session, -# scope_equipment=scope_equipment, -# scope__equipment_in=scope__equipment_in, # ), -# message="Data updated successfully", -# ) # @router.delete("/{assetnum}", response_model=StandardResponse[None]) # async def delete_scope_equipment(db_session: DbSession, assetnum: str): -# scope_equipment = await get_by_assetnum(db_session=db_session, assetnum=assetnum) # if not scope_equipment: # raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail=[{"msg": "A data with this id does not exist."}], -# ) -# await delete(db_session=db_session, assetnum=assetnum) -# return StandardResponse(message="Data deleted successfully", data=None) diff --git a/src/standard_scope/schema.py b/src/standard_scope/schema.py index 7eb0ab5..4955958 100644 --- a/src/standard_scope/schema.py +++ b/src/standard_scope/schema.py @@ -2,10 +2,9 @@ from datetime import datetime from typing import List, Optional, ForwardRef from uuid import UUID -from pydantic import Field, computed_field, field_validator, validator +from pydantic import Field from src.models import DefultBase, Pagination -from src.overhaul_scope.schema import ScopeRead from .enum import ScopeEquipmentType diff --git a/src/standard_scope/service.py b/src/standard_scope/service.py index 8261307..3030eb3 100644 --- a/src/standard_scope/service.py +++ b/src/standard_scope/service.py @@ -1,20 +1,18 @@ from datetime import datetime, timedelta -from typing import Optional, Union +from typing import Optional from fastapi import HTTPException, status -from sqlalchemy import Delete, Select, and_, desc, func, not_, or_ +from sqlalchemy import Select, desc from sqlalchemy.dialects.postgresql import insert from sqlalchemy.orm import selectinload -from src.auth.service import CurrentUser from src.database.core import DbSession, CollectorDbSession from src.utils import update_model from src.database.service import CommonParameters, search_filter_sort_paginate from src.overhaul_scope.model import OverhaulScope from src.standard_scope.enum import ScopeEquipmentType from src.standard_scope.model import EquipmentOHHistory -from src.soft_delete import apply_not_deleted_filter, soft_delete_record -from src.workorder.model import MasterWorkOrder +from src.soft_delete import apply_not_deleted_filter from src.equipment_workscope_group.model import EquipmentWorkscopeGroup from src.workscope_group.model import MasterActivity from src.workscope_group_maintenance_type.model import WorkscopeOHType @@ -61,7 +59,6 @@ async def get_all(*, common, oh_scope: Optional[str] = None): # .filter( # (StandardScope.is_alternating_oh == False) | # (StandardScope.oh_history == None) | - # (StandardScope.oh_history.has(EquipmentOHHistory.last_oh_type != selected_overhaul.maintenance_type.name)) # ).distinct() ) @@ -100,7 +97,6 @@ async def get_by_oh_session_id(*, db_session: DbSession, oh_session_id: UUID): async def create(*, db_session: DbSession, scope_equipment_in: ScopeEquipmentCreate): """Creates a new document.""" - # scope_equipment = StandardScope(**scope_equipment_in.model_dump()) assetnums = scope_equipment_in.assetnums results = [] removal_date = scope_equipment_in.removal_date @@ -204,7 +200,6 @@ async def get_by_oh_scope( # .filter( # (StandardScope.is_alternating_oh == False) | # (StandardScope.oh_history == None) | - # (StandardScope.oh_history.has(EquipmentOHHistory.last_oh_type != selected_overhaul.maintenance_type.name)) # ).distinct() ) diff --git a/src/testing/integration_test.py b/src/testing/integration_test.py index e9dd15e..d545425 100644 --- a/src/testing/integration_test.py +++ b/src/testing/integration_test.py @@ -6,7 +6,6 @@ Run with: pytest src/testing/integration_test.py -v """ -import pytest import requests BASE_URL = "http://100.125.115.116:8000/optimumoh" @@ -22,17 +21,13 @@ HEADERS = { # Placeholder IDs — replace with real values from your database before running OVERHAUL_SESSION_ID = "3704c82c-cfc5-47f7-813a-1661f89b0738" -# OVERHAUL_EQUIPMENT_ID = 1 ## Source code got commented SCOPE_JOB_ID = "db590764-72cb-4eb1-a30f-014116df0472" CALCULATION_ID = "44f483f3-bfe4-4094-a59f-b97a10f2fea6" BUDGET_THRESHOLD = 100000 RISK_COST = 1 LOCATION_TAG = "3AL-F501A" SCOPE_NAME = "A" -# OVERHAUL_SESSION_HISTORY_ID = 1 ASSET_NUM = "A19825" -# SCOPE_EQUIPMENT_JOB_ID = 1 ## Source code got commented -# EQUIPMENT_ACTIVITY_ID = 1 ## Source code got commented EAF_INPUT = 0.85 DURATION = 17520 SCOPE_EQUIPMENT_ACTIVITY_ID = "4527eb2b-0bc4-46b3-80a1-e1d2f67882f8" @@ -102,8 +97,6 @@ def get_csrf_token(path: str, auth_token: str) -> str: # def test_get_overhaul_gantt(): # """GET /overhaul-gantt — no role guard; all roles allowed""" # for user in USER_LIST: -# res = requests.get(f"{BASE_URL}/overhaul-gantt", headers=user_auth_headers(user["authorization_token"])) -# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}" @@ -113,30 +106,13 @@ def get_csrf_token(path: str, auth_token: str) -> str: # # [[Source code got commented]] # def test_get_overhaul_session(): -# """GET /overhaul-session?all=1 — require_any_role blocks Management""" # for user in USER_LIST: -# res = requests.get(f"{BASE_URL}/overhaul-session?all=1", headers=user_auth_headers(user["authorization_token"])) -# expected = 200 if user["name"] in NON_MANAGEMENT else 403 -# print(f"[{user['name']}] expected {expected}, got {res.status_code}, response: {res.text}") -# assert res.status_code == expected, f"[{user['name']}] expected {expected}, got {res.status_code}" # # [[Source code got commented]] # def test_post_overhaul_session(): # """POST /overhaul-session — require_any_role + CREATE blocks Management""" -# payload = { -# "type": "Major", -# "start_date": "2025-01-01", -# "end_date": "2025-12-31", -# } # for user in USER_LIST: -# csrf_token = get_csrf_token("/overhaul-session", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post(f"{BASE_URL}/overhaul-session", json=payload, headers=headers) -# print(f"[{user['name']}], got {res.status_code}, response: {res.text}") # if user["name"] in NON_MANAGEMENT: -# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}" -# else: -# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}" # # ───────────────────────────────────────────── # # Overhaul Schedules @@ -145,39 +121,15 @@ def get_csrf_token(path: str, auth_token: str) -> str: # # [[Attribute error]] # def test_post_overhaul_schedules(): # """POST /overhaul-schedules — require_any_role + CREATE blocks Management""" -# payload = { -# "start_date": "2024-06-01T00:00:00Z", -# "end_date": "2024-06-30T23:59:59Z", -# "duration_oh": 720, -# "crew_number": 10, # "status": "Upcoming" -# } # for user in USER_LIST: -# print(f"Testing payload: {payload}, headers: {user_auth_headers(user['authorization_token'])}") - # csrf_token = get_csrf_token("/overhaul-schedules", user["authorization_token"]) - # headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} - # res = requests.post(f"{BASE_URL}/overhaul-schedules", json=payload, headers=headers) -# print(f"[{user['name']}], got {res.status_code}, response: {res.text}") # if user["name"] in NON_MANAGEMENT: -# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}" -# else: -# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}" # # [[Source code got commented]] # def test_delete_overhaul_schedule(): -# """POST /overhaul-schedules/delete/{scope_id} — require_any_role + DELETE blocks Management. CSRF required.""" # for user in USER_LIST: -# csrf_token = get_csrf_token("/overhaul-schedules/delete", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post( -# f"{BASE_URL}/overhaul-schedules/delete/{OVERHAUL_SCHEDULE_ID}", -# headers=headers, -# ) # if user["name"] in NON_MANAGEMENT: -# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}" -# else: -# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}" @@ -188,19 +140,7 @@ def get_csrf_token(path: str, auth_token: str) -> str: # # [[Source code got commented]] # def test_post_overhaul_jobs(): -# """POST /overhaul-jobs/{overhaul_equipment_id} — no role guard; all roles allowed""" -# payload = { -# "job_ids": [], -# } # for user in USER_LIST: -# csrf_token = get_csrf_token("/overhaul-jobs", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post( -# f"{BASE_URL}/overhaul-jobs/{SCOPE_JOB_ID}", -# json=payload, -# headers=headers, -# ) -# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}" # ───────────────────────────────────────────── @@ -210,32 +150,14 @@ def get_csrf_token(path: str, auth_token: str) -> str: # # [[Database error: Unconsumed column names: scope_overhaul, assetnum, type, removal_date]] # def test_post_scope_equipments(): # """POST /scope-equipments — require_any_role blocks Management""" -# payload = { -# "assetnums": ["A26312", "A26313", "A26314"], # "scope_name": "A" -# } # for user in USER_LIST: -# csrf_token = get_csrf_token("/scope-equipments", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post(f"{BASE_URL}/scope-equipments", json=payload, headers=headers) -# print(f"[{user['name']}], got {res.status_code}, response: {res.text}") # if user["name"] in NON_MANAGEMENT: -# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}" -# else: -# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}" # # [[No endpoint exists]] # def test_delete_scope_equipment(): -# """DELETE /scope-equipments/delete/{id} — require_any_role blocks Management""" # for user in USER_LIST: -# res = requests.delete( -# f"{BASE_URL}/scope-equipments/delete/{OVERHAUL_EQUIPMENT_ID}", -# headers=user_auth_headers(user["authorization_token"]), -# ) # if user["name"] in NON_MANAGEMENT: -# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}" -# else: -# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}" # # ───────────────────────────────────────────── @@ -244,31 +166,11 @@ def get_csrf_token(path: str, auth_token: str) -> str: # # [[Source code got commented]] # def test_post_scope_equipment_jobs(): -# """POST /scope-equipment-jobs/{assetnum} — no role guard; all roles allowed""" -# payload = { -# "job_ids": [], -# } # for user in USER_LIST: -# csrf_token = get_csrf_token("/scope-equipment-jobs", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post( -# f"{BASE_URL}/scope-equipment-jobs/{ASSET_NUM}", -# json=payload, -# headers=headers, -# ) -# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}" # # [[Source code got commented]] # def test_delete_scope_equipment_job(): -# """POST /equipment-workscopes/delete/{scope_job_id} — no role guard; all roles allowed""" # for user in USER_LIST: -# csrf_token = get_csrf_token("/equipment-workscopes/delete", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post( -# f"{BASE_URL}/equipment-workscopes/delete/{SCOPE_EQUIPMENT_JOB_ID}", -# headers=headers, -# ) -# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}" # # ───────────────────────────────────────────── # # Overhaul Jobs @@ -276,43 +178,16 @@ def get_csrf_token(path: str, auth_token: str) -> str: # # [[Source code got commented]] # def test_get_overhaul_jobs(): -# """GET /overhaul-jobs/{overhaul_equipment_id} — no role guard; all roles allowed""" # for user in USER_LIST: -# res = requests.get( -# f"{BASE_URL}/overhaul-jobs/{OVERHAUL_EQUIPMENT_ID}", -# headers=user_auth_headers(user["authorization_token"]), -# ) -# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}" # # ───────────────────────────────────────────── # # Calculation — Budget Constraint # # ───────────────────────────────────────────── -# # [[AttributeError]] # def test_post_calculation_time_constraint(): -# """POST /calculation/time-constraint — required_permission(CREATE); Management has no CREATE""" -# payload = { -# "ohSessionId": OVERHAUL_SESSION_ID, -# "costPerFailure": 0, -# "overhaulCost": 0, -# } -# params = { -# "with_results": 0, -# } # for user in USER_LIST: -# csrf_token = get_csrf_token("/calculation/time-constraint", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post( -# f"{BASE_URL}/calculation/time-constraint", -# json=payload, -# params=params, -# headers=headers, -# ) # if user["name"] in NON_MANAGEMENT: -# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}" -# else: -# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}" # # ───────────────────────────────────────────── @@ -324,18 +199,7 @@ def get_csrf_token(path: str, auth_token: str) -> str: # """POST /equipment-activities — no role guard; all roles allowed # Note: route commented out in api.py; active equivalent is POST /overhaul-activity/{session_id} # """ -# payload = { -# "location_tags": [LOCATION_TAG], -# } # for user in USER_LIST: -# csrf_token = get_csrf_token("/equipment-activities", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post( -# f"{BASE_URL}/equipment-activities", -# json=payload, -# headers=headers, -# ) -# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}" # # [[Source code got commented]] @@ -344,13 +208,6 @@ def get_csrf_token(path: str, auth_token: str) -> str: # Note: route commented out in api.py; active equivalent is POST /overhaul-activity/delete/{session}/{location_tag} # """ # for user in USER_LIST: -# csrf_token = get_csrf_token("/equipment-activities/delete", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post( -# f"{BASE_URL}/equipment-activities/delete/{EQUIPMENT_ACTIVITY_ID}", -# headers=headers, -# ) -# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}" # # ───────────────────────────────────────────── @@ -361,29 +218,17 @@ def get_csrf_token(path: str, auth_token: str) -> str: # def test_get_jobs(): # """GET /jobs — no role guard; all roles allowed""" # for user in USER_LIST: -# res = requests.get(f"{BASE_URL}/jobs", headers=user_auth_headers(user["authorization_token"])) -# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}" # ───────────────────────────────────────────── # Overhauls sub-routes # ───────────────────────────────────────────── -# # [[TypeError]] # def test_get_overhauls_schedules(): -# """GET /overhauls/schedules — required_permission(READ); Management has READ""" # for user in USER_LIST: -# res = requests.get(f"{BASE_URL}/overhauls/schedules", headers=user_auth_headers(user["authorization_token"])) -# print(f"[{user['name']}], got {res.status_code}, response: {res.text}") -# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}: {res.text}" -# # [[TypeError]] # def test_get_overhauls_critical_parts(): -# """GET /overhauls/critical-parts — required_permission(READ); Management has READ""" # for user in USER_LIST: -# res = requests.get(f"{BASE_URL}/overhauls/critical-parts", headers=user_auth_headers(user["authorization_token"])) -# print(f"[{user['name']}], got {res.status_code}, response: {res.text}") -# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}: {res.text}" # # ───────────────────────────────────────────── @@ -392,113 +237,50 @@ def get_csrf_token(path: str, auth_token: str) -> str: # # [[Attribute Error]] # def test_get_overhaul_activity_by_assetnum(): -# """GET /overhaul-activity/{overhaul_session}/{assetnum} — require_any_role blocks Management""" # for user in USER_LIST: -# res = requests.get( -# f"{BASE_URL}/overhaul-activity/{OVERHAUL_SESSION_ID}/{ASSET_NUM}", -# headers=user_auth_headers(user["authorization_token"]), -# ) -# expected = 200 if user["name"] in NON_MANAGEMENT else 403 -# assert res.status_code == expected, f"[{user['name']}] expected {expected}, got {res.status_code}: {res.text}" # # ───────────────────────────────────────────── # # Workscopes # # ───────────────────────────────────────────── -# # [[TypeError]] # def test_post_workscopes(): # """POST /workscopes — no role guard; all roles allowed -# Payload: workscope (str) # """ -# payload = { -# # "workscope": "Test workscope description", -# # "system": "Test system", -# # "subsystem": "Test subsystem", -# # "tasks": "Test task", -# # "equipment_workscope_groups": "Test group", -# # "oh_types": "Test OH type", -# "description": "Test description", -# # "job_id": SCOPE_JOB_ID, -# } # for user in USER_LIST: -# csrf_token = get_csrf_token("/workscopes", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post(f"{BASE_URL}/workscopes", json=payload, headers=headers) -# print(f"[{user['name']}], got {res.status_code}, response: {res.text}") -# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}" # ───────────────────────────────────────────── # Equipment Workscopes # ───────────────────────────────────────────── -# # [[NameError]] # def test_post_equipment_workscopes_delete(): # """POST /equipment-workscopes/delete/{scope_job_id} — no role guard; all roles allowed # No body required. # """ # for user in USER_LIST: -# csrf_token = get_csrf_token("/equipment-workscopes/delete", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post( -# f"{BASE_URL}/equipment-workscopes/delete/{SCOPE_JOB_ID}", -# headers=headers, -# ) -# print(f"[{user['name']}], got {res.status_code}, response: {res.text}") -# assert res.status_code in (200, 204, 404), f"[{user['name']}] expected 200/204/404, got {res.status_code}" # # ───────────────────────────────────────────── # # Calculation — Time Constraint # # ───────────────────────────────────────────── -# # [[AttributeError]] # def test_post_calculation_time_constraint(): # """POST /calculation/time-constraint — required_permission(CREATE); Management has no CREATE # Payload: overhaulCost (float), ohSessionId (UUID), costPerFailure (float) # Query params: scope_calculation_id (optional), with_results (optional int), simulation_id (optional) # """ -# payload = { -# "overhaulCost": 0, -# "ohSessionId": OVERHAUL_SESSION_ID, -# "costPerFailure": 0, -# } # for user in USER_LIST: -# csrf_token = get_csrf_token("/calculation/time-constraint", user["authorization_token"]) -# headers = {**user_auth_headers(user["authorization_token"]), "X-CSRF-Token": csrf_token} -# res = requests.post( -# f"{BASE_URL}/calculation/time-constraint", -# json=payload, -# headers=headers, -# ) -# print(f"[{user['name']}], got {res.status_code}, response: {res.text}") # if user["name"] in NON_MANAGEMENT: -# assert res.status_code in (200, 201), f"[{user['name']}] expected 200/201, got {res.status_code}" -# else: -# assert res.status_code == 403, f"[{user['name']}] expected 403, got {res.status_code}" -# # [[AttributeError]] # def test_get_calculation_time_constraint_parameters(): # """GET /calculation/time-constraint/parameters — required_permission(READ); Management has READ # Query params: calculation_id (optional) # """ # for user in USER_LIST: -# res = requests.get( -# f"{BASE_URL}/calculation/time-constraint/parameters", -# headers=user_auth_headers(user["authorization_token"]), -# ) -# assert res.status_code == 200, f"[{user['name']}] expected 200, got {res.status_code}: {res.text}" -# # [[AttributeError]] # def test_get_calculation_time_constraint_by_id_and_assetnum(): -# """GET /calculation/time-constraint/{calculation_id}/{assetnum} — required_permission(READ); Management has READ""" # for user in USER_LIST: -# res = requests.get( -# f"{BASE_URL}/calculation/time-constraint/{CALCULATION_ID}/{ASSET_NUM}", -# headers=user_auth_headers(user["authorization_token"]), -# ) -# assert res.status_code in (200, 404), f"[{user['name']}] expected 200/404, got {res.status_code}: {res.text}" @@ -507,15 +289,8 @@ def get_csrf_token(path: str, auth_token: str) -> str: # # Calculation — Budget Constraint # # ───────────────────────────────────────────── -# # [[AttributeError]] # def test_get_calculation_budget_constraint(): # """GET /calculation/budget-constraint/{session_id} — require_any_role blocks Management # Query params: cost_threshold (float, default 100) # """ # for user in USER_LIST: -# res = requests.get( -# f"{BASE_URL}/calculation/budget-constraint/{OVERHAUL_SESSION_ID}?cost_threshold={BUDGET_THRESHOLD}", -# headers=user_auth_headers(user["authorization_token"]), -# ) -# expected = 200 if user["name"] in NON_MANAGEMENT else 403 -# assert res.status_code == expected, f"[{user['name']}] expected {expected}, got {res.status_code}: {res.text}" diff --git a/src/testing/prod_integration_test.py b/src/testing/prod_integration_test.py index 421bec8..fa00bce 100644 --- a/src/testing/prod_integration_test.py +++ b/src/testing/prod_integration_test.py @@ -6,7 +6,6 @@ Run with: pytest src/testing/integration_test.py -v """ -import pytest import requests BASE_URL = "http://100.125.115.116:8000/optimumoh" diff --git a/src/utils.py b/src/utils.py index 4c75778..e0010fa 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,5 +1,5 @@ import re -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta from typing import Optional import pytz diff --git a/src/workorder/model.py b/src/workorder/model.py index d30a62b..600cad3 100644 --- a/src/workorder/model.py +++ b/src/workorder/model.py @@ -1,8 +1,7 @@ -from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String -from sqlalchemy.orm import relationship +from sqlalchemy import Column, Float, String from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin +from src.models import DefaultMixin class MasterWorkOrder(Base, DefaultMixin): @@ -13,8 +12,4 @@ class MasterWorkOrder(Base, DefaultMixin): workgroup = Column(String, nullable=True) total_cost_max = Column(Float, nullable=True) - # scope_equipments = relationship( # "ScopeEquipment", - # lazy="raise", - # primaryjoin="and_(MasterWorkOrder.assetnum == foreign(ScopeEquipment.assetnum))", - # ) diff --git a/src/workscope_group/model.py b/src/workscope_group/model.py index a726f5b..f025784 100644 --- a/src/workscope_group/model.py +++ b/src/workscope_group/model.py @@ -1,10 +1,8 @@ -from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String -from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy import UUID, Column, ForeignKey, String from sqlalchemy.orm import relationship from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin -from src.workorder.model import MasterWorkOrder +from src.models import DefaultMixin class MasterActivitytask(Base, DefaultMixin): @@ -45,15 +43,8 @@ class MasterActivity(Base, DefaultMixin): back_populates="workscope_group", ) - # details = relationship( # "MasterActivityDetail", - # lazy="raise", - # primaryjoin="and_(MasterActivity.id == foreign(MasterActivityDetail.activity_id))", - # ) # class MasterActivityDetail(Base, DefaultMixin): -# __tablename__ = "oh_ms_activity_detail" -# name = Column(String, nullable=False) -# activity_id = Column(UUID(as_uuid=True)) diff --git a/src/workscope_group/router.py b/src/workscope_group/router.py index ff0e1ff..f291f6c 100644 --- a/src/workscope_group/router.py +++ b/src/workscope_group/router.py @@ -1,7 +1,6 @@ -from fastapi import APIRouter, HTTPException, Query, status +from fastapi import APIRouter, HTTPException, status -from src.database.service import (CommonParameters, DbSession, - search_filter_sort_paginate) +from src.database.service import (CommonParameters, DbSession) from src.models import StandardResponse from .schema import (ActivityMaster, ActivityMasterCreate, @@ -14,7 +13,6 @@ router = APIRouter() @router.get("", response_model=StandardResponse[ActivityMasterPagination]) async def get_activities(common: CommonParameters): """Get all scope activity pagination.""" - # return data = await get_all(common=common) diff --git a/src/workscope_group/schema.py b/src/workscope_group/schema.py index 7463cd7..9ad8ea0 100644 --- a/src/workscope_group/schema.py +++ b/src/workscope_group/schema.py @@ -1,8 +1,6 @@ -from datetime import datetime -from typing import Any, Dict, List, Optional +from typing import List from uuid import UUID -from pydantic import BaseModel, Field from src.models import DefultBase, Pagination @@ -44,40 +42,19 @@ class ActivityMasterPagination(Pagination): items: List[ActivityMasterRead] = [] -# { -# "overview": { -# "totalEquipment": 30, -# "nextSchedule": { -# "date": "2025-01-12", -# "Overhaul": "B", # "equipmentCount": 30 -# } # }, # "criticalParts": [ # "Boiler feed pump", # "Boiler reheater system", -# "Drum Level (Right) Root Valve A", # "BCP A Discharge Valve", # "BFPT A EXH Press HI Root VLV" # ], # "schedules": [ -# { -# "date": "2025-01-12", -# "Overhaul": "B", # "status": "upcoming" -# } # // ... other scheduled overhauls # ], -# "systemComponents": { -# "boiler": { -# "status": "operational", # "lastOverhaul": "2024-06-15" # }, -# "turbine": { -# "hpt": { "status": "operational" }, -# "ipt": { "status": "operational" }, # "lpt": { "status": "operational" } -# } # // ... other major components -# } -# } diff --git a/src/workscope_group/service.py b/src/workscope_group/service.py index 15ec94b..85f04f0 100644 --- a/src/workscope_group/service.py +++ b/src/workscope_group/service.py @@ -1,10 +1,8 @@ from typing import Optional from fastapi import HTTPException, status -from sqlalchemy import Delete, Select -from sqlalchemy.orm import joinedload, selectinload +from sqlalchemy import Select -from src.auth.service import CurrentUser from src.database.core import DbSession from src.database.service import CommonParameters, search_filter_sort_paginate from src.utils import update_model diff --git a/src/workscope_group_maintenance_type/model.py b/src/workscope_group_maintenance_type/model.py index 450ed1f..295f117 100644 --- a/src/workscope_group_maintenance_type/model.py +++ b/src/workscope_group_maintenance_type/model.py @@ -1,10 +1,7 @@ -from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String -from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy import UUID, Column, ForeignKey from sqlalchemy.orm import relationship from src.database.core import Base -from src.models import DefaultMixin, IdentityMixin, TimeStampMixin -from src.workorder.model import MasterWorkOrder class WorkscopeOHType(Base): diff --git a/src/workscope_group_maintenance_type/service.py b/src/workscope_group_maintenance_type/service.py index 15ec94b..85f04f0 100644 --- a/src/workscope_group_maintenance_type/service.py +++ b/src/workscope_group_maintenance_type/service.py @@ -1,10 +1,8 @@ from typing import Optional from fastapi import HTTPException, status -from sqlalchemy import Delete, Select -from sqlalchemy.orm import joinedload, selectinload +from sqlalchemy import Select -from src.auth.service import CurrentUser from src.database.core import DbSession from src.database.service import CommonParameters, search_filter_sort_paginate from src.utils import update_model