Add Sejoli tryout JSON snapshot importer
This commit is contained in:
139
app/models/tryout_snapshot_question.py
Normal file
139
app/models/tryout_snapshot_question.py
Normal file
@@ -0,0 +1,139 @@
|
||||
"""
|
||||
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",
|
||||
),
|
||||
)
|
||||
Reference in New Issue
Block a user