""" Read-only normalized reference rows for imported tryout questions. These rows reflect the latest imported source version of each question and are kept separate from operational items and AI-generated variants. """ from datetime import datetime from typing import Optional from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, JSON, String, Text, UniqueConstraint, func from sqlalchemy.orm import Mapped, mapped_column from app.database import Base class TryoutSnapshotQuestion(Base): __tablename__ = "tryout_snapshot_questions" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) website_id: Mapped[int] = mapped_column( ForeignKey("websites.id", ondelete="CASCADE", onupdate="CASCADE"), nullable=False, index=True, comment="Website identifier", ) source_tryout_id: Mapped[str] = mapped_column( String(255), nullable=False, index=True, comment="External source tryout identifier", ) source_question_id: Mapped[str] = mapped_column( String(255), nullable=False, comment="External source question identifier", ) latest_snapshot_id: Mapped[Optional[int]] = mapped_column( ForeignKey("tryout_import_snapshots.id", ondelete="SET NULL", onupdate="CASCADE"), nullable=True, index=True, comment="Latest snapshot containing this question", ) question_title: Mapped[str] = mapped_column( Text, nullable=False, comment="Imported title or short label", ) question_html: Mapped[str] = mapped_column( Text, nullable=False, comment="Imported question body HTML", ) explanation_html: Mapped[Optional[str]] = mapped_column( Text, nullable=True, comment="Imported explanation HTML", ) raw_options: Mapped[list] = mapped_column( JSON, nullable=False, comment="Raw source options payload", ) correct_answer: Mapped[str] = mapped_column( String(10), nullable=False, comment="Imported correct answer key", ) category_id: Mapped[Optional[int]] = mapped_column( Integer, nullable=True, comment="Imported category id", ) category_name: Mapped[Optional[str]] = mapped_column( String(255), nullable=True, comment="Imported category name", ) category_code: Mapped[Optional[str]] = mapped_column( String(255), nullable=True, comment="Imported category code", ) option_count: Mapped[int] = mapped_column( Integer, nullable=False, default=0, comment="Count of source options", ) has_option_labels: Mapped[bool] = mapped_column( Boolean, nullable=False, default=False, comment="Whether source options include visible labels", ) is_active: Mapped[bool] = mapped_column( Boolean, nullable=False, default=True, comment="Whether question is still present in latest source import", ) content_checksum: Mapped[str] = mapped_column( String(64), nullable=False, comment="Checksum of normalized question content", ) raw_payload: Mapped[dict] = mapped_column( JSON, nullable=False, comment="Original source question payload", ) first_seen_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) last_seen_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now(), ) __table_args__ = ( UniqueConstraint( "website_id", "source_tryout_id", "source_question_id", name="uq_snapshot_questions_website_tryout_question", ), )