"""eid_review_set_draft ORM — 이드 복습세트 초안 (append-only 제안). migration 302. 워커가 약점 스냅샷에서 chronic/relapse 문항을 복습세트 초안으로 '제안'만 INSERT. 실제 편성(study_question_progress.due_at)은 사용자 1클릭 T2 액션 — 이 draft 는 불변 제안 기록. UPDATE/DELETE 는 DB RULE 차단. 스탬프 actor·source_generated_at NOT NULL no-default. """ from __future__ import annotations from datetime import datetime from sqlalchemy import BigInteger, DateTime, ForeignKey, String, func from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.orm import Mapped, mapped_column from core.database import Base class EidReviewSetDraft(Base): __tablename__ = "eid_review_set_draft" id: Mapped[int] = mapped_column(BigInteger, primary_key=True) user_id: Mapped[int] = mapped_column( BigInteger, ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) study_topic_id: Mapped[int | None] = mapped_column( BigInteger, ForeignKey("study_topics.id", ondelete="CASCADE") ) # nullable = cross-topic 세트 question_ids: Mapped[list] = mapped_column(JSONB, nullable=False) # ordered list[int] reason: Mapped[str] = mapped_column(String(40), nullable=False) # chronic|relapse|coverage|overdue actor: Mapped[str] = mapped_column(String(20), nullable=False) # 스탬프 source_weakness_id: Mapped[int | None] = mapped_column( BigInteger, ForeignKey("eid_study_weakness.id", ondelete="SET NULL") ) source_generated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False ) # 스탬프 supersedes_id: Mapped[int | None] = mapped_column( BigInteger, ForeignKey("eid_review_set_draft.id", ondelete="SET NULL") ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() )