from __future__ import annotations from dataclasses import dataclass, field from typing import List import time VALID_DIFFICULTIES = {"easy", "medium", "hard"} VALID_TYPES = {"mcq", "tf", "fill"} @dataclass class Question: text: str topic: str # subject area — "Determinants" not question text options: List[str] correct_idx: int explanation: str difficulty: str q_type: str is_boss: bool = False source_excerpt: str = "" # snippet from original material for tutor context language: str = "en" def __post_init__(self): if self.difficulty not in VALID_DIFFICULTIES: raise ValueError(f"difficulty must be one of {VALID_DIFFICULTIES}, got '{self.difficulty}'") if self.q_type not in VALID_TYPES: raise ValueError(f"q_type must be one of {VALID_TYPES}, got '{self.q_type}'") if not (0 <= self.correct_idx < len(self.options)): raise ValueError(f"correct_idx {self.correct_idx} out of range for {len(self.options)} options") @property def correct_answer(self) -> str: return self.options[self.correct_idx] @dataclass class QuizSession: quest_name: str questions: List[Question] = field(default_factory=list) current_idx: int = 0 score: int = 0 consecutive_correct: int = 0 xp_earned: int = 0 start_time: float = field(default_factory=time.time) wrong_topics: List[str] = field(default_factory=list) # topic strings, not question text @property def is_finished(self) -> bool: return self.current_idx >= len(self.questions) @property def current_question(self) -> Question | None: if self.is_finished: return None return self.questions[self.current_idx] @dataclass class Quest: name: str topics: List[str] boss_topic: str difficulty: str questions: List[Question] = field(default_factory=list) unlocked: bool = True completed: bool = False # True only after user finishes the quest def has_questions(self) -> bool: return len(self.questions) > 0 def total_questions(self) -> int: return len(self.questions)