| """ |
| infrastructure/database/models/prediction_model.py |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| PredictionModel β SQLAlchemy ORM model for the ``predictions`` table. |
| |
| Supabase / PostgreSQL optimisations: |
| β’ Native UUID type for the FK ``ppg_signal_id`` (matches ``raw_ppg.id``). |
| β’ Dedicated index on ``created_at DESC`` for efficient date-range queries. |
| β’ Composite index on ``(ppg_signal_id, created_at)`` for JOIN + sort queries. |
| |
| Mapping: PredictionModel (ORM) β BPPrediction (domain entity) |
| """ |
| from __future__ import annotations |
|
|
| from typing import Any |
|
|
| from sqlalchemy import Float, ForeignKey, Index, String, text |
| from sqlalchemy.dialects.postgresql import JSONB, UUID |
| from sqlalchemy.orm import Mapped, mapped_column, relationship |
|
|
| from src.infrastructure.database.models.base import Base |
| from src.shared.constants import PREDICTIONS_TABLE_NAME, RAW_PPG_TABLE_NAME |
|
|
|
|
| class PredictionModel(Base): |
| """ |
| ORM representation of an AI blood pressure prediction result. |
| |
| Table: ``predictions`` |
| """ |
|
|
| __tablename__ = PREDICTIONS_TABLE_NAME |
|
|
| __table_args__ = ( |
| |
| Index( |
| "ix_predictions_created_at", |
| text("created_at DESC"), |
| ), |
| |
| Index( |
| "ix_predictions_signal_created", |
| "ppg_signal_id", |
| text("created_at DESC"), |
| ), |
| ) |
|
|
| |
|
|
| ppg_signal_id: Mapped[str] = mapped_column( |
| UUID(as_uuid=False), |
| ForeignKey(f"{RAW_PPG_TABLE_NAME}.id", ondelete="CASCADE"), |
| nullable=False, |
| index=True, |
| comment="FK to raw_ppg.id β the source signal for this prediction", |
| ) |
|
|
| |
|
|
| predicted_sbp: Mapped[float] = mapped_column( |
| Float, |
| nullable=False, |
| comment="Predicted Systolic Blood Pressure (mmHg)", |
| ) |
| predicted_dbp: Mapped[float] = mapped_column( |
| Float, |
| nullable=False, |
| comment="Predicted Diastolic Blood Pressure (mmHg)", |
| ) |
| predicted_ecg: Mapped[list[Any] | None] = mapped_column( |
| JSONB, |
| nullable=True, |
| comment="Synthetic ECG signal windows produced by CardioGAN (list[list[float]])", |
| ) |
|
|
| |
|
|
| model_version: Mapped[str] = mapped_column( |
| String(64), |
| nullable=False, |
| comment="Version string of the model that produced this prediction", |
| ) |
| inference_time_ms: Mapped[float] = mapped_column( |
| Float, |
| nullable=False, |
| default=0.0, |
| comment="Wall-clock inference duration in milliseconds", |
| ) |
| sa_log: Mapped[dict | None] = mapped_column( |
| JSONB, |
| nullable=True, |
| comment="Logs of the Simulated Annealing optimization process", |
| ) |
|
|
| |
|
|
| ppg_signal: Mapped["PPGModel"] = relationship( |
| "PPGModel", |
| back_populates="predictions", |
| lazy="select", |
| ) |
|
|
| def __repr__(self) -> str: |
| return ( |
| f"PredictionModel(id={self.id!r}, " |
| f"ppg_signal_id={self.ppg_signal_id!r}, " |
| f"SBP={self.predicted_sbp}, DBP={self.predicted_dbp}, " |
| f"ecg_segments={len(self.predicted_ecg) if isinstance(self.predicted_ecg, list) else 0}, " |
| f"model={self.model_version!r})" |
| ) |
|
|