Extraction modes (no restart needed — code ready for next Celery deploy): - New QuizCreate.extraction_mode field: standard|questions_only|two_step|regex|ai_decide - extraction_modes.py: independent implementations that don't touch standard path - questions_only: extract Q+options, correct_answer="PENDING" for manual fill - two_step: separate answer key section scan + phase1/2/3 matching - regex: AI detects answer pattern, generates regex, applies to full doc - ai_decide: AI reads samples from start+end and picks strategy - DocumentDetailPage: Extraction Mode dropdown with description per mode - quiz_tasks.py: routes to correct mode, standard path completely unchanged Database: - Deleted 11 orphaned questions from PREP 2013 extraction (quiz 12 was already deleted) - 268 questions remaining (all PREP 2012) UI fixes: - Nextcloud section in Settings now only shown to moderators/admins (regular users can't upload PDFs so they don't need Nextcloud) - Upload PDF already hidden in navbar for non-moderators (confirmed correct) - Resume quiz: now async — study mode quiz data loaded BEFORE showing quiz so correct_answer is available immediately for feedback - Resume saves and restores voice selection - voice field added to ProgressSave schema and Redis storage - Progress save dependency includes selectedVoice Attempts: - POST /attempts/start: reuses existing incomplete attempt by default (fresh=false) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
66 lines
1.8 KiB
Python
66 lines
1.8 KiB
Python
from datetime import datetime
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
class QuizCreate(BaseModel):
|
|
section_id: int
|
|
title: str
|
|
mode: str = "timed" # timed, learning
|
|
time_limit_minutes: int | None = None
|
|
model_id: str | None = None # override extraction model
|
|
question_category_id: int | None = None # assign extracted questions to this bank category
|
|
extraction_mode: str = "standard"
|
|
# standard — current working mode (inline Correct Answer / Preferred Response)
|
|
# questions_only — extract Q+options only, no answers (admin fills later)
|
|
# two_step — separate answer key section (PREP 2013 style)
|
|
# regex — AI analyses format then extracts answer key with regex
|
|
# ai_decide — AI reads a sample and decides which approach to use
|
|
|
|
|
|
class QuestionResponse(BaseModel):
|
|
id: int
|
|
question_text: str
|
|
question_type: str
|
|
options: list[str] | None
|
|
image_path: str | None = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class QuestionWithAnswer(QuestionResponse):
|
|
correct_answer: str
|
|
explanation: str | None
|
|
page_reference: int | None
|
|
|
|
|
|
class QuizResponse(BaseModel):
|
|
id: int
|
|
section_id: int
|
|
user_id: int
|
|
title: str
|
|
questions_count: int
|
|
mode: str
|
|
time_limit_minutes: int | None
|
|
skipped_questions: str | None = None
|
|
category_id: int | None = None
|
|
created_at: datetime
|
|
deleted_at: datetime | None = None
|
|
is_published: int = 1
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class QuizDetail(QuizResponse):
|
|
questions: list[QuestionResponse] = []
|
|
|
|
|
|
class QuizLearningDetail(QuizResponse):
|
|
"""Learning mode — includes answers and explanations."""
|
|
questions: list[QuestionWithAnswer] = []
|
|
|
|
|
|
class QuizReview(QuizResponse):
|
|
questions: list[QuestionWithAnswer] = []
|