from datetime import datetime from sqlalchemy import Column, Integer, Boolean, String, DateTime, ForeignKey, JSON from sqlalchemy.orm import relationship from app.database import Base class QuizAttempt(Base): __tablename__ = "quiz_attempts" id = Column(Integer, primary_key=True, index=True) quiz_id = Column(Integer, ForeignKey("quizzes.id", ondelete="CASCADE"), nullable=False) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) mode = Column(String(10), nullable=True) # legacy NULL resumes without exposing answers score = Column(Integer, default=0) total_questions = Column(Integer, default=0) started_at = Column(DateTime, default=datetime.utcnow) completed_at = Column(DateTime, nullable=True) selected_question_ids = Column(JSON, nullable=True) # for question pool: which questions this attempt uses expired = Column(Integer, default=0) # 1 = auto-submitted due to timer expiry (abandoned), excluded from history quiz = relationship("Quiz", back_populates="attempts") user = relationship("User", back_populates="attempts") answers = relationship("AttemptAnswer", back_populates="attempt", cascade="all, delete-orphan") class AttemptAnswer(Base): __tablename__ = "attempt_answers" id = Column(Integer, primary_key=True, index=True) attempt_id = Column(Integer, ForeignKey("quiz_attempts.id", ondelete="CASCADE"), nullable=False) question_id = Column(Integer, ForeignKey("questions.id", ondelete="CASCADE"), nullable=False) user_answer = Column(String, nullable=False) # Seconds on this question. Null means an answer from before this was # measured, which is not the same claim as zero. seconds_spent = Column(Integer, nullable=True) is_correct = Column(Boolean, default=False) # Whether a tip was opened on this question before it was answered. Rows # written before tips existed carry 0, which is not a guess: there was # nothing to open. used_hint = Column(Boolean, nullable=False, default=False, server_default="0") attempt = relationship("QuizAttempt", back_populates="answers") question = relationship("Question")