""" application/dto/prediction_dto.py ─────────────────────────────────── Pydantic DTO for BP prediction responses. """ from __future__ import annotations from datetime import datetime from typing import Optional from pydantic import BaseModel, Field class PredictionResponse(BaseModel): """ Outgoing payload returned to the frontend for a blood pressure prediction. Derived from the BPPrediction domain entity, but formatted for API consumers. """ id: str = Field(description="UUID of the prediction record.") ppg_signal_id: str = Field(description="UUID of the source PPG signal.") predicted_sbp: float = Field(description="Predicted Systolic Blood Pressure (mmHg).") predicted_dbp: float = Field(description="Predicted Diastolic Blood Pressure (mmHg).") predicted_ecg: Optional[list] = Field( default=None, description=( "Synthetic ECG signal windows produced by CardioGAN. " "Format: list of segments, each segment is a list of float samples (224 samples @ 125 Hz)." ), ) mean_arterial_pressure: float = Field(description="Computed MAP: DBP + (SBP-DBP)/3 (mmHg).") pulse_pressure: float = Field(description="SBP - DBP (mmHg).") hypertension_stage: str = Field( description="Classification: Normal | Elevated | Stage 1 | Stage 2 | Crisis" ) model_version: str = Field(description="Model version that produced this prediction.") inference_time_ms: float = Field(description="Wall-clock inference duration (ms).") sa_log: Optional[dict] = Field( default=None, description="Logs of the Simulated Annealing optimization process.", ) created_at: datetime = Field(description="UTC timestamp of when the prediction was made.") model_config = { "json_schema_extra": { "example": { "id": "7ba85f64-5717-4562-b3fc-2c963f66afa9", "ppg_signal_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "predicted_sbp": 118.5, "predicted_dbp": 76.2, "predicted_ecg": [[0.12, -0.05, 0.33], [0.09, -0.11, 0.28]], "mean_arterial_pressure": 90.3, "pulse_pressure": 42.3, "hypertension_stage": "Normal", "model_version": "gan-vgtlnet-v1.0", "inference_time_ms": 142.7, "created_at": "2026-05-30T12:00:00Z", } } } class PredictionHistoryResponse(BaseModel): """Paginated list of predictions for a user.""" user_id: str total: int predictions: list[PredictionResponse] @classmethod def from_predictions( cls, user_id: str, predictions: list[PredictionResponse], ) -> "PredictionHistoryResponse": return cls(user_id=user_id, total=len(predictions), predictions=predictions) class DateRangeRequest(BaseModel): """Optional query parameters for date-range filtering.""" start: Optional[datetime] = Field(default=None, description="Start of range (UTC).") end: Optional[datetime] = Field(default=None, description="End of range (UTC).") limit: int = Field(default=50, ge=1, le=500, description="Max records to return.") offset: int = Field(default=0, ge=0, description="Records to skip.")