"""study_reminders ORM — 알람 재료 append-only (공부 암기노트 Phase 1). study_reminder cron(09/13/19 KST)이 focus 토픽 due 요약을 1행 INSERT, GET /reminders/latest 가 읽는다. UPDATE/DELETE 없음. fired_at 은 시간 슬롯으로 truncate 해서 UNIQUE(user, fired_at) 멱등(on_conflict_do_nothing)을 성립시킨다(raw now() 마이크로초면 멱등 무효). study_topic_id 는 nullable(전체 집계 행은 NULL) + ON DELETE SET NULL(이력 보존). """ from __future__ import annotations from datetime import datetime from sqlalchemy import BigInteger, DateTime, ForeignKey, Integer from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.orm import Mapped, mapped_column from core.database import Base class StudyReminder(Base): __tablename__ = "study_reminders" 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="SET NULL") ) due_count: Mapped[int | None] = mapped_column(Integer) focus_topic_names: Mapped[list | None] = mapped_column(JSONB) fired_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=datetime.now, nullable=False ) # active partial unique 없음 — UNIQUE(user_id, fired_at) 는 migration 298 inline constraint.