add features: add simulation model & result into db
digital-twin/RBD-multibranch/pipeline/head This commit looks good Details

ferdi_feature
akumadoferudi 2 months ago
parent 14ad828e3e
commit 599a0963d5

1
.gitignore vendored

@ -2,3 +2,4 @@ env/
__pycache__/ __pycache__/
venv/ venv/
.env .env
.venv

@ -1,12 +1,534 @@
from sqlalchemy import JSON, UUID, Column, DateTime, Float, ForeignKey, Integer, String from sqlalchemy import (
JSON,
UUID,
TIMESTAMP,
Column,
DateTime,
Float,
ForeignKey,
Numeric,
Integer,
String,
Text
)
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.sql import func
from src.database.core import Base from src.database.core import Base
from src.models import DefaultMixin from src.models import DefaultMixin
import uuid
class AerosProject(Base, DefaultMixin): class AerosProject(Base, DefaultMixin):
__tablename__ = "rbd_ms_aeros_project" __tablename__ = "rbd_ms_aeros_project"
project_name = Column(String, nullable=False) project_name = Column(String, nullable=False)
aro_file_path = Column(String, nullable=False) aro_file_path = Column(String, nullable=False)
# AF
class SimulationModel(Base):
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
# server_default=text("uuid_generate_v4()")
)
name = Column(
String,
# unique=True,
index=True,
nullable=True
)
payload = Column(JSON, nullable=True)
gui_payload = Column(JSON, nullable=True)
# realtionship
schemas = relationship("SimulationModelSchema", back_populates="model") # 1-to-many
assets = relationship("SimulationModelAsset", back_populates="model") # 1-to-many
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
class SimulationTask(Base):
id = Column(String, primary_key=True, index=True)
model_id = Column(Integer, nullable=False)
status = Column(String, default="pending") # pending, running, completed, failed
progress = Column(Integer, default=0)
params = Column(JSON, nullable=True)
result = Column(JSON, nullable=True)
error = Column(String, nullable=True)
created_at = Column(String, nullable=False)
updated_at = Column(String, nullable=False)
# AF
class SimulationMasterType(Base):
__tablename__ = "simulation_master_types"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
# server_default=text("uuid_generate_v4()")
)
name = Column(Text, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
model_asset_distributions = relationship("SimulationModelAssetDistribution", back_populates="type") # 1-to-many
model_schemas = relationship("SimulationModelSchema", back_populates="type") # 1-to-many
model_asset_repair_durations = relationship("SimulationModelAssetRepairDuration", back_populates="type") # many-to-1
result_events = relationship("SimulationResultEvent", back_populates="type") # 1-to-many
class SimulationModelSchema(Base):
__tablename__ = "simulation_model_schemas"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
# server_default=text("uuid_generate_v4()")
)
simulation_model_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model.id"),
nullable=True
)
# self reference
simulation_model_schemas_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model_schemas.id"),
nullable=True
)
type_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_master_types.id"),
nullable=True
)
schemaId = Column(Text, nullable=True) # not a foreign key
name = Column(Text, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
model = relationship("SimulationModel", back_populates="schemas") # many-to-1
type = relationship("SimulationMasterType", back_populates="model_schemas") # many-to-1
assets = relationship("SimulationModelAsset", back_populates="model_schema") # 1-to-many
schema_results = relationship("SimulationResultSchema", back_populates="schema_model") # 1-to-many
# SELF RELATION
parent = relationship(
"SimulationModelSchema",
remote_side=[id],
back_populates="children"
)
children = relationship(
"SimulationModelSchema",
back_populates="parent",
# cascade="all, delete"
)
class SimulationModelAsset(Base):
__tablename__ = "simulation_model_assets"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
# server_default=text("uuid_generate_v4()")
)
assetId = Column(Text, nullable=True) # not a foreign key
name = Column(Text, nullable=True)
simulation_model_schemas_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model_schemas.id"),
nullable=True
)
simulation_model_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model.id"),
nullable=True
)
max_capacity = Column(Numeric, nullable=True)
design_capacity = Column(Numeric, nullable=True)
buffer_duration = Column(Numeric, nullable=True)
transition_delay = Column(Numeric, nullable=True)
initial_age = Column(Numeric, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
model = relationship("SimulationModel", back_populates="assets") # many-to-1
asset_distribution = relationship(
"SimulationModelAssetDistribution",
back_populates="asset",
uselist=False # 1-to-1
)
asset_repair_duration = relationship(
"SimulationModelAssetRepairDuration",
back_populates="asset",
uselist=False # 1-to-1
)
asset_overhaul = relationship(
"SimulationModelAssetOverhaul",
back_populates="asset",
uselist=False # 1-to-1
)
asset_pm = relationship(
"SimulationModelAssetPm",
back_populates="asset",
uselist=False # 1-to-1
)
model_schema = relationship(
"SimulationModelSchema",
back_populates="assets" # many-to-1
)
asset_results = relationship(
"SimulationResultAsset",
back_populates="asset_model" # 1-to-many
)
class SimulationModelAssetDistribution(Base):
__tablename__ = "simulation_model_assets_distributions"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
# server_default=text("uuid_generate_v4()")
)
simulation_model_assets_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model_assets.id"),
nullable=True
)
type_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_master_types.id"),
nullable=True
)
shape = Column(Numeric, nullable=True)
scale = Column(Numeric, nullable=True)
mean = Column(Numeric, nullable=True)
std_dev = Column(Numeric, nullable=True)
eta = Column(Numeric, nullable=True)
beta = Column(Numeric, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
asset = relationship("SimulationModelAsset", back_populates="asset_distribution") # 1-to-1
type = relationship("SimulationMasterType", back_populates="model_asset_distributions") # many-to-1
class SimulationModelAssetRepairDuration(Base):
__tablename__ = "simulation_model_assets_repair_durations"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
# server_default=text("uuid_generate_v4()")
)
simulation_model_assets_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model_assets.id"),
nullable=True
)
type_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_master_types.id"),
nullable=True
)
mean = Column(Numeric, nullable=True)
std_dev = Column(Numeric, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
asset = relationship("SimulationModelAsset", back_populates="asset_repair_duration") # 1-to-1
type = relationship("SimulationMasterType", back_populates="model_asset_repair_durations") # many-to-1
class SimulationModelAssetOverhaul(Base):
__tablename__ = "simulation_model_assets_overhauls"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
# server_default=text("uuid_generate_v4()")
)
simulation_model_assets_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model_assets.id"),
nullable=True
)
interval = Column(Numeric, nullable=True)
duration = Column(Numeric, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
asset = relationship("SimulationModelAsset", back_populates="asset_overhaul") # 1-to-1
class SimulationModelAssetPm(Base):
__tablename__ = "simulation_model_assets_pms"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
# server_default=text("uuid_generate_v4()")
)
simulation_model_assets_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model_assets.id"),
nullable=True
)
interval = Column(Numeric, nullable=True)
duration = Column(Numeric, nullable=True)
restoration_factor = Column(Numeric, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
asset = relationship("SimulationModelAsset", back_populates="asset_pm") # 1-to-1
class SimulationResult(Base):
__tablename__ = "simulation_results"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
)
production_availability = Column(Numeric, nullable=True)
simulation_duration = Column(Numeric, nullable=True)
simulation_replications = Column(Numeric, nullable=True)
system_availability = Column(Numeric, nullable=True)
system_downtime = Column(Numeric, nullable=True)
system_failures = Column(Numeric, nullable=True)
system_uptime = Column(Numeric, nullable=True)
total_demand = Column(Numeric, nullable=True)
total_productions = Column(Numeric, nullable=True)
system_down_causes = Column(JSONB, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
schemas = relationship("SimulationResultSchema", back_populates="result") # 1-to-many
assets = relationship("SimulationResultAsset", back_populates="result") # 1-to-many
events = relationship("SimulationResultEvent", back_populates="result") # 1-to-many
# down_causes = relationship("SimulationResultDownCauses", back_populates="result") # 1-to-many
class SimulationResultAsset(Base):
__tablename__ = "simulation_result_assets"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
)
simulation_results_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_results.id"),
nullable=True
)
simulation_model_assets_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model_assets.id"),
nullable=True
)
assetId = Column(Text, nullable=True) # "node_blablabla"
name = Column(Text, nullable=True)
availability = Column(Numeric, nullable=True)
contribution_factor = Column(Numeric, nullable=True)
downtime = Column(Numeric, nullable=True)
uptime = Column(Numeric, nullable=True)
failures = Column(Numeric, nullable=True)
total_downtime_concurrent_with_system = Column(Numeric, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
result = relationship("SimulationResult", back_populates="assets") # many-to-1
events = relationship("SimulationResultEvent", back_populates="asset") # 1-to-many
asset_model = relationship("SimulationModelAsset", back_populates="asset_results") # many-to-1
class SimulationResultSchema(Base):
__tablename__ = "simulation_result_schemas"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
)
simulation_results_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_results.id"),
nullable=True
)
simulation_model_schemas_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_model_schemas.id"),
nullable=True
)
schemaId = Column(Text, nullable=True)
name = Column(Text, nullable=True)
availability = Column(Numeric, nullable=True)
downtime = Column(Numeric, nullable=True)
uptime = Column(Numeric, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
result = relationship("SimulationResult", back_populates="schemas") # many-to-1
events = relationship("SimulationResultEvent", back_populates="schema") # 1-to-many
schema_model = relationship("SimulationModelSchema", back_populates="schema_results") # many-to-1
class SimulationResultEvent(Base):
__tablename__ = "simulation_result_events"
id = Column(
UUID(as_uuid=True),
primary_key=True,
index=True,
default=uuid.uuid4,
)
simulation_result_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_results.id"),
nullable=True
)
simulation_result_assets_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_result_assets.id"),
nullable=True
)
simulation_result_schemas_id = Column(
UUID(as_uuid=True),
ForeignKey("simulation_result_schemas.id"),
nullable=True
)
type_id = Column(
UUID(as_uuid=True),
# asumsi ada tabel ini
ForeignKey("simulation_master_types.id"),
nullable=True
)
cause = Column(Text, nullable=True)
state = Column(Text, nullable=True)
load_factor = Column(Numeric, nullable=True)
start_time = Column(Numeric, nullable=True)
end_time = Column(Numeric, nullable=True)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)
# relationships
result = relationship("SimulationResult", back_populates="events")
asset = relationship("SimulationResultAsset", back_populates="events")
schema = relationship("SimulationResultSchema", back_populates="events")
type = relationship("SimulationMasterType", back_populates="result_events")
# class SimulationResultDownCause(Base):
# __tablename__ = "simulation_result_down_causes"
# id = Column(
# UUID(as_uuid=True),
# primary_key=True,
# index=True,
# default=uuid.uuid4,
# )
# simulation_results_id = Column(
# UUID(as_uuid=True),
# ForeignKey("simulation_results.id"),
# nullable=True
# )
# availability = Column(Numeric, nullable=True)
# downtime = Column(Numeric, nullable=True)
# uptime = Column(Numeric, nullable=True)
# failures = Column(Numeric, nullable=True)
# created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
# updated_at = Column(TIMESTAMP(timezone=True), server_default=func.now(), onupdate=func.now())
# deleted_at = Column(TIMESTAMP(timezone=True), nullable=True)

@ -1,7 +1,19 @@
import json
import os import os
from typing import List, Optional from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, HTTPException, Response, status, File, UploadFile, Form
from fastapi import (
APIRouter,
HTTPException,
Response,
status,
File,
UploadFile,
Form,
Request,
Body
)
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
import httpx import httpx
from sqlalchemy import select from sqlalchemy import select
@ -14,7 +26,7 @@ from src.database.service import CommonParameters
from src.models import StandardResponse from src.models import StandardResponse
from src.aeros_utils import aeros_post from src.aeros_utils import aeros_post
from .schema import AerosProjectInput, AerosMetadata, OverhaulScheduleCreate, OverhaulScheduleUpdate from .schema import AerosProjectInput, AerosMetadata, OverhaulScheduleCreate, OverhaulScheduleUpdate
from .service import import_aro_project, fetch_aro_record, reset_project, create, update, delete from .service import get_simulation_model_by_id_service, get_simulation_result_service, import_aro_project, fetch_aro_record, import_simulation_model_service, import_simulation_result_service, reset_project, create, update, delete
router = APIRouter() router = APIRouter()
@ -157,3 +169,357 @@ async def delete_overhaul_equipment_job(db_session: DbSession, overhaul_job_id:
data=None, data=None,
message="Data deleted successfully", message="Data deleted successfully",
) )
# import & store simulation ramp model into db (use this: input .ramp/.json file)
@router.post("/import-simulation-model/file")
async def import_simulation_model_file(
db: DbSession,
file: UploadFile = File(...)
):
try:
allowed_extensions = (
".ramp",
".json"
)
if not file.filename.endswith(
allowed_extensions
):
raise HTTPException(
400,
"Only .ramp and .json files are supported"
)
contents = await file.read()
try:
data = json.loads(
contents.decode("utf-8")
)
except Exception:
raise HTTPException(
400,
"Invalid JSON inside uploaded file"
)
# =====================================
# VALIDATION
# =====================================
validate_simulation_payload(data)
# =====================================
# IMPORT
# =====================================
result = await import_simulation_model_service(
db=db,
name=None,
data=data
)
return StandardResponse(
data=result,
message="File imported successfully"
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
500,
f"Import failed: {str(e)}"
)
# helper: validate simulation model payload from raw json
def validate_simulation_model_payload(data: dict):
if "rbd_root" not in data:
raise HTTPException(
400,
"Invalid structure: 'rbd_root' missing"
)
if "assets" not in data:
raise HTTPException(
400,
"Invalid structure: 'assets' missing"
)
if "schemas" not in data:
raise HTTPException(
400,
"Invalid structure: 'schemas' missing"
)
# import & store simulation ramp model into db (use this: input raw json)
@router.post("/import-simulation-model/json")
async def import_simulation_model_json(
db: DbSession,
payload: dict = Body(...)
):
try:
# =====================================
# VALIDATION
# =====================================
validate_simulation_model_payload(payload)
# =====================================
# IMPORT
# =====================================
result = await import_simulation_model_service(
db=db,
name=None,
data=payload
)
return StandardResponse(
data=result,
message="JSON imported successfully"
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
500,
f"Import failed: {str(e)}"
)
# get simulation model by id (USE THIS)
@router.get("/simulation-model-by-id/{simulation_model_id}")
async def get_simulation_model_by_id(db: DbSession, simulation_model_id: str):
result = await (
get_simulation_model_by_id_service(db, simulation_model_id)
)
if not result:
raise HTTPException(
status_code=404,
detail="Simulation model not found"
)
return StandardResponse(
data=result,
message="Data showed successfully",
)
# helper: validate simulation result payload from raw json
def validate_simulation_result_payload(data: dict):
required_fields = [
"asset_results",
"schema_results",
"system_events",
]
for field in required_fields:
if field not in data:
raise HTTPException(
status_code=400,
detail=f"Invalid structure: '{field}' missing"
)
# import & store simulation result into db (use this: input .json file)
@router.post("/import-simulation-result/file")
async def import_simulation_result_file(
db: DbSession,
file: UploadFile = File(...)
):
try:
# =====================================
# FILE VALIDATION
# =====================================
if not file.filename.endswith(".json"):
raise HTTPException(
status_code=400,
detail="Only .json files are supported"
)
contents = await file.read()
# =====================================
# PARSE JSON
# =====================================
try:
data = json.loads(
contents.decode("utf-8")
)
except Exception:
raise HTTPException(
status_code=400,
detail="Invalid JSON inside uploaded file"
)
# =====================================
# VALIDATION
# =====================================
validate_simulation_result_payload(
data
)
# =====================================
# IMPORT
# =====================================
# result =
await import_simulation_result_service(
db,
data
)
# =====================================
# RESPONSE
# =====================================
# final_result = {
# "status": "success",
# "simulation_result_id": result[
# "result_id"
# ],
# "asset_imported": result[
# "asset_count"
# ],
# "schema_imported": result[
# "schema_count"
# ],
# "system_event_imported": result[
# "system_event_count"
# ],
# }
return StandardResponse(
data=None,
message="Data stored successfully",
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=500,
detail={
"message": str(e),
"type": type(e).__name__,
}
)
# import & store simulation result into db (use this: input raw json)
@router.post("/import-simulation-result/json")
async def import_simulation_result_json(
db: DbSession,
data: dict
):
try:
# =====================================
# VALIDATION
# =====================================
validate_simulation_result_payload(
data
)
# =====================================
# IMPORT
# =====================================
# result =
await import_simulation_result_service(
db,
data
)
# =====================================
# RESPONSE
# =====================================
# final_result = {
# "status": "success",
# "simulation_result_id": result[
# "result_id"
# ],
# "asset_imported": result[
# "asset_count"
# ],
# "schema_imported": result[
# "schema_count"
# ],
# "system_event_imported": result[
# "system_event_count"
# ],
# }
return StandardResponse(
data=None,
message="Data stored successfully",
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=500,
detail={
"message": str(e),
"type": type(e).__name__,
}
)
# get simulation result by id
@router.get("/simulation-result-by-id/{simulation_result_id}")
async def get_simulation_result_by_id(db: DbSession, simulation_result_id: str):
result = await (
get_simulation_result_service(
db,
simulation_result_id
)
)
if not result:
raise HTTPException(
status_code=404,
detail="Simulation result not found"
)
# return result
return StandardResponse(
data=result,
message="Data showed successfully",
)

File diff suppressed because it is too large Load Diff

@ -15,7 +15,7 @@ from src.aeros_simulation.service import (
from src.auth.service import CurrentUser from src.auth.service import CurrentUser
from src.database.core import DbSession from src.database.core import DbSession
from src.database.service import search_filter_sort_paginate from src.database.service import search_filter_sort_paginate
from tkinter.constants import E # from tkinter.constants import E
from src.dashboard_model.model import RBDMasterData from src.dashboard_model.model import RBDMasterData
from src.dashboard_model.schema_masterdata import RBDMasterDataUpdate from src.dashboard_model.schema_masterdata import RBDMasterDataUpdate

Loading…
Cancel
Save