from datetime import datetime from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint from app.database import Base class QuestionFolder(Base): """A hand-picked set of questions an educator assembles for someone else. Deliberately not a `user_collections` row with a flag on it. The two look alike — both are a named list of questions — but the access arrow runs the other way. A library is a *consequence* of access: you can only save what the bank predicate already lets you see, and the API says `private: True` about every row. A folder is a *source* of access: a grant points at one, and holding that grant is how an educator comes to reach the questions inside. Putting both in one table would mean thousands of private rows sitting beside a handful that confer permission, told apart by a flag — and a mistake reading that flag is either a privacy leak or a privilege escalation, in a table where the common case is somebody's private list. Membership is the owner's and a moderator's to change, never a grantee's: otherwise the holder of a folder grant could add any question to the folder and so widen their own grant. """ __tablename__ = "question_folders" id = Column(Integer, primary_key=True, index=True) name = Column(String(200), nullable=False) description = Column(Text, nullable=True) # Who assembled it. SET NULL rather than CASCADE: a folder someone was # granted must not disappear because the educator who built it left. user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class QuestionFolderQuestion(Base): __tablename__ = "question_folder_questions" __table_args__ = (UniqueConstraint("folder_id", "question_id", name="uq_folder_question"),) id = Column(Integer, primary_key=True, index=True) folder_id = Column(Integer, ForeignKey("question_folders.id", ondelete="CASCADE"), nullable=False, index=True) question_id = Column(Integer, ForeignKey("questions.id", ondelete="CASCADE"), nullable=False, index=True) added_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True) added_at = Column(DateTime, default=datetime.utcnow)