from datetime import datetime from typing import Optional from pydantic import BaseModel, Field class Token(BaseModel): access_token: str token_type: str = "bearer" class LoginRequest(BaseModel): username: str password: str class QuestionBase(BaseModel): chapter: str = "" primary_knowledge: str = "" secondary_knowledge: str = "" question_type: str = "" difficulty: str = "" stem: str = Field(..., min_length=1) option_a: str = "" option_b: str = "" option_c: str = "" option_d: str = "" answer: str = "" explanation: str = "" notes: str = "" source_file: str = "" class QuestionCreate(QuestionBase): pass class QuestionUpdate(BaseModel): chapter: Optional[str] = None primary_knowledge: Optional[str] = None secondary_knowledge: Optional[str] = None question_type: Optional[str] = None difficulty: Optional[str] = None stem: Optional[str] = None option_a: Optional[str] = None option_b: Optional[str] = None option_c: Optional[str] = None option_d: Optional[str] = None answer: Optional[str] = None explanation: Optional[str] = None notes: Optional[str] = None source_file: Optional[str] = None class QuestionOut(QuestionBase): id: int created_at: datetime updated_at: datetime class Config: from_attributes = True class BatchDeleteRequest(BaseModel): ids: list[int] class BatchUpdateRequest(BaseModel): ids: list[int] chapter: Optional[str] = None primary_knowledge: Optional[str] = None secondary_knowledge: Optional[str] = None question_type: Optional[str] = None difficulty: Optional[str] = None class CategoryBase(BaseModel): name: str parent_id: Optional[int] = None level: int = 1 class CategoryCreate(CategoryBase): pass class CategoryOut(CategoryBase): id: int class Config: from_attributes = True class ImportHistoryOut(BaseModel): id: int filename: str method: str question_count: int status: str created_at: datetime class Config: from_attributes = True class ImportJobItemOut(BaseModel): id: int job_id: int seq: int filename: str stored_path: str status: str attempt: int error: str question_count: int started_at: datetime | None ended_at: datetime | None class Config: from_attributes = True class ImportJobOut(BaseModel): id: int status: str method: str total: int processed: int success_count: int failed_count: int current_index: int current_file: str error: str attempt: int created_at: datetime started_at: datetime | None ended_at: datetime | None updated_at: datetime items: list[ImportJobItemOut] = [] class Config: from_attributes = True class PracticeStartRequest(BaseModel): chapter: Optional[str] = None secondary_knowledge: Optional[str] = None question_type: Optional[str] = None difficulty: Optional[str] = None random_mode: bool = False limit: int = 20 class PracticeCheckRequest(BaseModel): question_id: int user_answer: str