from datetime import datetime, timezone from sqlalchemy import Float, ForeignKey, Integer, String, Text from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship class Base(DeclarativeBase): pass def _utc_now() -> datetime: return datetime.now(timezone.utc) class Analysis(Base): __tablename__ = "analyses" id: Mapped[str] = mapped_column(String(36), primary_key=True) device_id: Mapped[str] = mapped_column(String(36), index=True) created_at: Mapped[datetime] = mapped_column(default=_utc_now, index=True) source: Mapped[str] = mapped_column(String(20)) language: Mapped[str] = mapped_column(String(10)) risk_score: Mapped[float] = mapped_column(Float) risk_level: Mapped[str] = mapped_column(String(10)) duration_ms: Mapped[int] = mapped_column(Integer, default=0) transcript_preview: Mapped[str | None] = mapped_column(Text, nullable=True) text_hash: Mapped[str | None] = mapped_column(String(64), nullable=True) features: Mapped[list["Feature"]] = relationship( back_populates="analysis", cascade="all, delete-orphan" ) class Feature(Base): __tablename__ = "features" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) analysis_id: Mapped[str] = mapped_column( ForeignKey("analyses.id", ondelete="CASCADE"), index=True ) type: Mapped[str] = mapped_column(String(30)) weight: Mapped[float] = mapped_column(Float) evidence: Mapped[str] = mapped_column(Text) analysis: Mapped[Analysis] = relationship(back_populates="features") class Feedback(Base): __tablename__ = "feedback" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) analysis_id: Mapped[str] = mapped_column( ForeignKey("analyses.id", ondelete="CASCADE"), index=True ) device_id: Mapped[str] = mapped_column(String(36), index=True) correct: Mapped[int] = mapped_column(Integer) comment: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(default=_utc_now)