diff --git a/src/calculation_time_constrains/flows.py b/src/calculation_time_constrains/flows.py index aeb8984..5e5612d 100644 --- a/src/calculation_time_constrains/flows.py +++ b/src/calculation_time_constrains/flows.py @@ -1,3 +1,5 @@ +from typing import Optional +from uuid import UUID import numpy as np from sqlalchemy import Select, func, select from sqlalchemy.orm import joinedload @@ -7,7 +9,7 @@ from src.scope.model import Scope from src.database.core import DbSession from src.scope.service import get_all from .schema import CalculationTimeConstrainsParametersRead, CalculationTimeConstrainsParametersRetrive, CalculationTimeConstrainsParametersCreate -from .service import get_overhaul_cost_by_time_chart, get_corrective_cost_time_chart, create_param_and_data, get_calculation_data_by_id +from .service import get_calculation_by_reference_and_parameter, get_calculation_result, get_overhaul_cost_by_time_chart, get_corrective_cost_time_chart, create_param_and_data, get_calculation_data_by_id, create_calculation_result_service from .model import CalculationResult from .model import CalculationParam @@ -61,38 +63,40 @@ async def get_create_calculation_parameters(*, db_session: DbSession, calculatio ) -async def create_calculation(*, db_session: DbSession, calculation_time_constrains_in: CalculationTimeConstrainsParametersCreate, created_by:str): - days = 60 - ## +async def create_calculation(*, db_session: DbSession, calculation_time_constrains_in: CalculationTimeConstrainsParametersCreate, created_by: str): calculation_data = await create_param_and_data( db_session=db_session, calculation_param_in=calculation_time_constrains_in, created_by=created_by) - overhaul_cost_points = get_overhaul_cost_by_time_chart( - calculation_time_constrains_in.overhaulCost, days) - - corrective_cost_points, dailyNumberOfFailure = get_corrective_cost_time_chart( - calculation_time_constrains_in.costPerFailure, days) + results = await create_calculation_result_service(db_session=db_session, calculation_id=calculation_data.id) - total_cost = overhaul_cost_points + corrective_cost_points + return results - optimumOH = np.argmin(total_cost) - numbersOfFailure = sum(dailyNumberOfFailure[:optimumOH]) - # raise Exception(optimumOH) +async def get_or_create_scope_equipment_calculation(*, db_session: DbSession, scope_calculation_id, calculation_time_constrains_in: Optional[CalculationTimeConstrainsParametersCreate]): + scope_calculation = await get_calculation_data_by_id(db_session=db_session, calculation_id=scope_calculation_id) - calculation_results = [] - for i in range(days): - result = CalculationResult( - parameter_id=calculation_data.parameter_id, - calculation_data_id=calculation_data.id, - day=(i + 1), - corrective_cost=float(corrective_cost_points[i]), - overhaul_cost=float(overhaul_cost_points[i]), - num_failures=int(dailyNumberOfFailure[i]), + if not scope_calculation: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="A data with this id does not exist.", ) - calculation_results.append(result) - db_session.add_all(calculation_results) - await db_session.commit() + # Check if calculation already exist + scope_equipment_calculation = await get_calculation_by_reference_and_parameter( + db_session=db_session, calculation_reference_id=calculation_time_constrains_in.assetnum, parameter_id=scope_calculation.parameter_id) - return calculation_data.id + if not scope_equipment_calculation: + # Update Parameter input from scope calculation + calculation_time_constrains_in.costPerFailure = scope_calculation.parameter.avg_failure_cost + calculation_time_constrains_in.overhaulCost = scope_calculation.parameter.overhaul_cost + + scope_equipment_calculation = await create_param_and_data( + db_session=db_session, calculation_param_in=calculation_time_constrains_in, created_by=scope_calculation.created_by, parameter_id=scope_calculation.parameter_id) + + results = await create_calculation_result_service(db_session=db_session, calculation_id=scope_equipment_calculation.id) + + return results + + results = await get_calculation_result(db_session=db_session, calculation_id=scope_equipment_calculation.id) + + return results diff --git a/src/calculation_time_constrains/model.py b/src/calculation_time_constrains/model.py index f56a35c..dafce96 100644 --- a/src/calculation_time_constrains/model.py +++ b/src/calculation_time_constrains/model.py @@ -1,7 +1,7 @@ from enum import Enum -from typing import List, Union +from typing import List, Optional, Union from sqlalchemy import UUID, Column, Float, ForeignKey, Integer, String from sqlalchemy.orm import relationship from src.database.core import Base, DbSession @@ -63,7 +63,8 @@ class CalculationData(Base, DefaultMixin, IdentityMixin): parameter_id = Column(UUID(as_uuid=True), ForeignKey( 'oh_ms_calculation_param.id'), nullable=True) overhaul_reference_type = Column(String(10), nullable=False) - reference_id = Column(UUID(as_uuid=True), nullable=False) + reference_id = Column(String, nullable=False) + optimum_oh_day = Column(Integer, nullable=True) parameter = relationship( "CalculationParam", back_populates="calculation_data") @@ -78,33 +79,35 @@ class CalculationData(Base, DefaultMixin, IdentityMixin): overhaul_reference_type: str, reference_id: Union[str, UUID], db: DbSession, - avg_failure_cost: float, - overhaul_cost: float, + avg_failure_cost: Optional[float], + overhaul_cost: Optional[float], created_by: str, + params_id: Optional[UUID] ): - # Create Params - params = CalculationParam( - avg_failure_cost=avg_failure_cost, - overhaul_cost=overhaul_cost, - created_by=created_by - ) - - - db.add(params) - await db.flush() - + if not params_id: + # Create Params + params = CalculationParam( + avg_failure_cost=avg_failure_cost, + overhaul_cost=overhaul_cost, + created_by=created_by + ) + + db.add(params) + await db.flush() + params_id = params.id + calculation_data = cls( overhaul_reference_type=overhaul_reference_type, - reference_id=reference_id, + reference_id=str(reference_id), created_by=created_by, - parameter_id=params.id + parameter_id=params_id ) db.add(calculation_data) - + await db.commit() await db.refresh(calculation_data) - + return calculation_data diff --git a/src/calculation_time_constrains/router.py b/src/calculation_time_constrains/router.py index 018b823..b5874ee 100644 --- a/src/calculation_time_constrains/router.py +++ b/src/calculation_time_constrains/router.py @@ -6,18 +6,25 @@ from src.database.core import DbSession from src.models import StandardResponse from src.auth.service import CurrentUser from .service import create_param_and_data, get_calculation_result -from .flows import get_create_calculation_parameters, create_calculation +from .flows import get_create_calculation_parameters, create_calculation, get_or_create_scope_equipment_calculation from fastapi.params import Query router = APIRouter() -@router.post("", response_model=StandardResponse[str]) -async def create_calculation_time_constrains(db_session: DbSession, current_user: CurrentUser, calculation_time_constrains_in: CalculationTimeConstrainsParametersCreate): +@router.post("", response_model=StandardResponse[Union[str, CalculationTimeConstrainsRead]]) +async def create_calculation_time_constrains(db_session: DbSession, current_user: CurrentUser, calculation_time_constrains_in: CalculationTimeConstrainsParametersCreate, scope_calculation_id: Optional[str] = Query(None), with_results: Optional[int] = Query(0)): """Save calculation time constrains Here""" - calculation_data = await create_calculation(db_session=db_session, calculation_time_constrains_in=calculation_time_constrains_in, created_by=current_user.name) - return StandardResponse(data=str(calculation_data), message="Data created successfully") + if scope_calculation_id: + results = await get_or_create_scope_equipment_calculation(db_session=db_session, scope_calculation_id=scope_calculation_id, calculation_time_constrains_in=calculation_time_constrains_in) + else: + results = await create_calculation(db_session=db_session, calculation_time_constrains_in=calculation_time_constrains_in, created_by=current_user.name) + + if not with_results: + results = str(results.id) + + return StandardResponse(data=results, message="Data created successfully") @router.get("/parameters", response_model=StandardResponse[Union[CalculationTimeConstrainsParametersRetrive, CalculationTimeConstrainsParametersRead]]) @@ -37,37 +44,23 @@ async def get_calculation_results(db_session: DbSession, calculation_id): results = await get_calculation_result(db_session=db_session, calculation_id=calculation_id) return StandardResponse( - data=CalculationTimeConstrainsRead( - id=calculation_id, - results=results - ), + data=results, message="Data retrieved successfully", ) - -# @router.post("/parameters", response_model=StandardResponse[str]) -# async def create_calculation_time_constrains_parameter(db_session: DbSession, current_user: CurrentUser, calculation_time_constrains_in: CalculationTimeConstrainsParametersCreate): -# """Save parameter Here""" -# parameters = await create_param(db_session=db_session, calculation_param_in=calculation_time_constrains_in, created_by=current_user.name) - -# return StandardResponse(data=str(parameters.id), message="Parameter Data created successfully") - - -# @router.get("/simulation", response_model=StandardResponse[Dict[str, Any]]) -# async def get_simulation_result(): - -# results = { -# "simulation": { -# "intervalDays": 45, -# "numberOfFailures": 75, -# "totalCost": 550000000, -# }, -# "comparison": { -# "vsOptimal": { -# "failureDifference": 16, -# "costDifference": 50000000 -# }, -# } -# } - -# return StandardResponse(data=results, message="Data retrieved successfully") +@router.post("/simulation", response_model=StandardResponse[Dict[str, Any]]) +async def get_simulation_result(): + results = { + "simulation": { + "intervalDays": 45, + "numberOfFailures": 75, + "totalCost": 550000000, + }, + "comparison": { + "vsOptimal": { + "failureDifference": 16, + "costDifference": 50000000 + }, + } + } + return StandardResponse(data=results, message="Data retrieved successfully") diff --git a/src/calculation_time_constrains/schema.py b/src/calculation_time_constrains/schema.py index 556e79b..ff33e50 100644 --- a/src/calculation_time_constrains/schema.py +++ b/src/calculation_time_constrains/schema.py @@ -36,10 +36,11 @@ class CalculationTimeConstrainsParametersRead(CalculationTimeConstrainsBase): class CalculationTimeConstrainsParametersCreate(CalculationTimeConstrainsBase): - overhaulCost: float = Field(..., description="Overhaul cost") + overhaulCost: Optional[float] = Field(0, description="Overhaul cost") scopeOH: Optional[str] = Field(None, description="Scope OH") assetnum: Optional[str] = Field(None, description="Assetnum") - costPerFailure: float = Field(..., description="Cost per failure") + costPerFailure: Optional[float] = Field(0, + description="Cost per failure") # class CalculationTimeConstrainsCreate(CalculationTimeConstrainsBase): @@ -57,7 +58,9 @@ class CalculationResultsRead(CalculationTimeConstrainsBase): class CalculationTimeConstrainsRead(CalculationTimeConstrainsBase): id: Union[UUID, str] + reference: str results: List[CalculationResultsRead] + optimumOh: Dict[str, Any] class CalculationTimeConstrainsSimulationRead(CalculationTimeConstrainsBase): diff --git a/src/calculation_time_constrains/service.py b/src/calculation_time_constrains/service.py index ab90488..51b50e2 100644 --- a/src/calculation_time_constrains/service.py +++ b/src/calculation_time_constrains/service.py @@ -1,11 +1,13 @@ +from typing import Optional +from uuid import UUID import numpy as np -from sqlalchemy import select +from sqlalchemy import and_, select from sqlalchemy.orm import joinedload from src.database.core import DbSession -from .schema import CalculationTimeConstrainsParametersCreate +from .schema import CalculationTimeConstrainsParametersCreate, CalculationTimeConstrainsRead from .model import CalculationParam, OverhaulReferenceType, CalculationData, CalculationResult from fastapi import HTTPException, status -from src.scope.service import get_by_scope_name +from src.scope.service import get_by_scope_name, get def get_overhaul_cost_by_time_chart(overhaul_cost: float, days: int) -> list: @@ -35,7 +37,7 @@ def get_corrective_cost_time_chart(cost_per_failure: float, days: int) -> list: return corrective_costs, failure_counts -async def create_param_and_data(*, db_session: DbSession, calculation_param_in: CalculationTimeConstrainsParametersCreate, created_by: str): +async def create_param_and_data(*, db_session: DbSession, calculation_param_in: CalculationTimeConstrainsParametersCreate, created_by: str, parameter_id: Optional[UUID] = None): """Creates a new document.""" if calculation_param_in.scopeOH is None and calculation_param_in.assetnum is None: raise HTTPException( @@ -67,18 +69,37 @@ async def create_param_and_data(*, db_session: DbSession, calculation_param_in: avg_failure_cost=calculation_param_in.costPerFailure, overhaul_cost=calculation_param_in.overhaulCost, created_by=created_by, - + params_id=parameter_id ) - + return calculationData async def get_calculation_result(db_session: DbSession, calculation_id: str): + calculation = await get_calculation_data_by_id(db_session=db_session, calculation_id=calculation_id) + reference = calculation.reference_id if calculation.overhaul_reference_type == OverhaulReferenceType.ASSET else await get(db_session=db_session, scope_id=calculation.reference_id) + stmt = select(CalculationResult).filter( CalculationResult.calculation_data_id == calculation_id).order_by(CalculationResult.day) - results = await db_session.execute(stmt) - return results.scalars().all() + optimum = stmt.filter(CalculationResult.day == calculation.optimum_oh_day) + results = await db_session.execute(stmt) + optimumOh = await db_session.scalar(optimum) + + optimumRes = { + "overhaulCost": optimumOh.overhaul_cost, + "correctiveCost": optimumOh.corrective_cost, + "numOfFailures": optimumOh.num_failures, + "days": optimumOh.day + } + + return CalculationTimeConstrainsRead( + id=calculation.id, + reference=reference if isinstance( + reference, str) else reference.scope_name, + results=results.scalars().all(), + optimumOh=optimumRes + ) async def get_calculation_data_by_id(db_session: DbSession, calculation_id) -> CalculationData: @@ -87,3 +108,68 @@ async def get_calculation_data_by_id(db_session: DbSession, calculation_id) -> C result = await db_session.execute(stmt) return result.unique().scalar() + + +async def create_calculation_result_service(db_session: DbSession, calculation_id: UUID): + days = 60 + calculation = await get_calculation_data_by_id(db_session=db_session, calculation_id=calculation_id) + reference = calculation.reference_id if calculation.overhaul_reference_type == OverhaulReferenceType.ASSET else await get(db_session=db_session, scope_id=calculation.reference_id) + + # Parameter + overhaulCost = calculation.parameter.overhaul_cost + costPerFailure = calculation.parameter.avg_failure_cost + + overhaul_cost_points = get_overhaul_cost_by_time_chart( + overhaulCost, days=days) + + corrective_cost_points, dailyNumberOfFailure = get_corrective_cost_time_chart( + costPerFailure, days) + + total_cost = overhaul_cost_points + corrective_cost_points + + optimumOHIndex = np.argmin(total_cost) + numbersOfFailure = sum(dailyNumberOfFailure[:optimumOHIndex]) + + optimum = { + "overhaulCost": float(overhaul_cost_points[optimumOHIndex]), + "correctiveCost": float(corrective_cost_points[optimumOHIndex]), + "numOfFailures": int(numbersOfFailure), + "days": int(optimumOHIndex+1) + } + + calculation_results = [] + for i in range(days): + result = CalculationResult( + parameter_id=calculation.parameter_id, + calculation_data_id=calculation.id, + day=(i + 1), + corrective_cost=float(corrective_cost_points[i]), + overhaul_cost=float(overhaul_cost_points[i]), + num_failures=int(dailyNumberOfFailure[i]), + ) + calculation_results.append(result) + + calculation.optimum_oh_day = int(optimumOHIndex+1) + + db_session.add_all(calculation_results) + await db_session.commit() + + return CalculationTimeConstrainsRead( + id=calculation.id, + reference=reference if isinstance( + reference, str) else reference.scope_name, + results=calculation_results, + optimumOh=optimum + ) + + +async def get_calculation_by_reference_and_parameter(*, db_session: DbSession, calculation_reference_id, parameter_id): + stmt = select(CalculationData).filter(and_( + CalculationData.reference_id == calculation_reference_id, + CalculationData.parameter_id == parameter_id, + )) + + result = await db_session.execute(stmt) + + return result.scalar() +