pdf-quiz-generator/backend/app/models/quiz.py
Daniel 9af63e67b5 Major: categories, question bank, security fixes, mobile layout, UX improvements
Security:
- Nginx: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, CSP, Referrer-Policy, Permissions-Policy headers
- Redis-backed login rate limiting (survives container restarts)
- Admin litellm/models endpoint: api_key moved from GET query param to POST body
- Nextcloud credentials moved from localStorage to sessionStorage (cleared on tab close)

UX / Layout:
- Login: unverified users see inline "Resend verification email" option
- QuizPage mobile: TTS Listen button on its own row below question text
- QuizPage mobile: Voice selector on its own row in header card (not squashed with timer)
- QuizEditPage: scroll position preserved after saving a question edit

Quiz Categories:
- New QuizCategory model + quiz_categories table
- category_id column added to quizzes table
- GET/POST/DELETE /api/categories endpoints
- Quizzes grouped by category in QuizzesPage; moderators can assign via 🏷 menu
- Uncategorized section shown when categories exist

Question Bank:
- GET /api/questions/bank — search all questions across quizzes
- POST /api/questions/from-bank — create new quiz from selected questions (copies, originals untouched)
- QuestionBankPage: search, checkbox select, study modal, create quiz form
- "Question Bank" link added to Navbar

Search:
- "View all N questions →" button expands to full question list
- Each question has a Study button opening in-place modal with study mode
- Summary view shows 2 questions per quiz with Study button

Extraction prompt:
- Stronger emphasis on correct_answer field with step-by-step letter → full text example
- Explicit instruction never to store just the letter

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 20:08:05 +02:00

29 lines
1.5 KiB
Python

from datetime import datetime
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text
from sqlalchemy.orm import relationship
from app.database import Base
from app.models.quiz_category import QuizCategory # noqa — ensures mapper resolves "QuizCategory"
class Quiz(Base):
__tablename__ = "quizzes"
id = Column(Integer, primary_key=True, index=True)
section_id = Column(Integer, ForeignKey("sections.id"), nullable=False)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
category_id = Column(Integer, ForeignKey("quiz_categories.id", ondelete="SET NULL"), nullable=True)
title = Column(String, nullable=False)
questions_count = Column(Integer, default=0)
time_limit_minutes = Column(Integer, nullable=True) # null = no limit
mode = Column(String, default="timed") # timed, learning
skipped_questions = Column(Text, nullable=True) # JSON list of skipped question texts
created_at = Column(DateTime, default=datetime.utcnow)
section = relationship("Section", back_populates="quizzes")
user = relationship("User", back_populates="quizzes")
category = relationship("QuizCategory", back_populates="quizzes", foreign_keys=[category_id])
questions = relationship("Question", back_populates="quiz", cascade="all, delete-orphan")
attempts = relationship("QuizAttempt", back_populates="quiz", cascade="all, delete-orphan")
reminders = relationship("ReminderSchedule", cascade="all, delete-orphan", foreign_keys="ReminderSchedule.quiz_id")