from datetime import datetime from pydantic import BaseModel, Field class QuizCreate(BaseModel): model_config = {"protected_namespaces": ()} section_id: int | None = None 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 (2013-style layout) # regex — AI analyses format then extracts answer key with regex # ai_decide — AI reads a sample and decides which approach to use class QuizUpdate(BaseModel): title: str class QuestionResponse(BaseModel): id: int question_text: str question_type: str options: list[str] | None image_path: str | None = None question_category_id: int | None = None difficulty: str | None = None category_breadcrumbs: list[dict] = Field(default_factory=list) # Filled by the endpoint, not read off the model: a figure is a row in # question_media, not a column on the question. figures: list[dict] = Field(default_factory=list) class Config: from_attributes = True class QuestionWithAnswer(QuestionResponse): correct_answer: str explanation: str | None explanation_image_path: str | None = None page_reference: int | None option_explanations: dict | None = None key_points: list | None = None class QuizResponse(BaseModel): id: int section_id: int | None = None # Nullable: bank content has no owner, and a schema that promises an # int where the column says NULL is a 500 on serialisation. user_id: int | None = None 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 is_shared: int = 0 questions_per_attempt: int | None = None max_attempts: int | None = None attempt_mode: str | None = None share_token: str | None = None 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] = []