File size: 4,211 Bytes
e391a84 6e84e40 e391a84 6e84e40 9cabacf e391a84 6e84e40 e391a84 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | """
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__ = (
# Optimise date-range queries: "predictions for user X between start and end"
Index(
"ix_predictions_created_at",
text("created_at DESC"),
),
# Optimise JOIN queries: "prediction(s) for a given PPG signal"
Index(
"ix_predictions_signal_created",
"ppg_signal_id",
text("created_at DESC"),
),
)
# ββ Foreign key β raw_ppg.id ββββββββββββββββββββββββββββββββββββββββββββββ
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 values ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
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 metadata ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
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",
)
# ββ Relationship back to PPGModel βββββββββββββββββββββββββββββββββββββββββ
ppg_signal: Mapped["PPGModel"] = relationship( # type: ignore[name-defined]
"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})"
)
|