hackathon-ENGIN/engin/service.py

68 lines
2.1 KiB
Python

"""Application service coordinating I/O, validation and prediction."""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
import pandas as pd
from .io import FrameReader
from .model import PredictionModel
from .validation import FrameValidator
@dataclass(frozen=True)
class DiagnosisResult:
measurements: pd.DataFrame
submission: pd.DataFrame
diagnostics: pd.DataFrame
warnings: tuple[str, ...] = ()
@property
def engine_ids(self) -> list[str]:
return self.measurements["engine_id"].drop_duplicates().astype(str).tolist()
def engine_measurements(self, engine_id: str) -> pd.DataFrame:
frame = self.measurements[self.measurements["engine_id"].eq(engine_id)]
if frame.empty:
raise KeyError(f"Unknown engine_id={engine_id!r}")
return frame.reset_index(drop=True)
def engine_diagnostics(self, engine_id: str) -> pd.DataFrame:
frame = self.diagnostics[self.diagnostics["engine_id"].eq(engine_id)]
if frame.empty:
raise KeyError(f"Unknown engine_id={engine_id!r}")
return frame.reset_index(drop=True)
class DiagnosticService:
"""Constructor-injected use case; no Streamlit or global state dependencies."""
def __init__(
self,
*,
reader: FrameReader,
validator: FrameValidator,
model: PredictionModel,
) -> None:
self._reader = reader
self._validator = validator
self._model = model
def diagnose_bytes(self, payload: bytes) -> DiagnosisResult:
return self.diagnose_frame(self._reader.read_bytes(payload))
def diagnose_path(self, path: Path) -> DiagnosisResult:
return self.diagnose_frame(self._reader.read_path(path))
def diagnose_frame(self, frame: pd.DataFrame) -> DiagnosisResult:
validated = self._validator.validate(frame)
predicted = self._model.predict(validated.frame)
return DiagnosisResult(
measurements=validated.frame,
submission=predicted.submission,
diagnostics=predicted.diagnostics,
warnings=validated.warnings,
)