from datetime import date, datetime from pydantic import BaseModel, ConfigDict, Field class ResourceBase(BaseModel): title: str = Field(..., max_length=255) resource_type: str = Field(..., pattern="^(link|file)$") url: str | None = None file_name: str | None = None category: str tags: str | None = None class ResourceCreate(ResourceBase): pass class ResourceUpdate(ResourceBase): pass class ResourceOut(ResourceBase): model_config = ConfigDict(from_attributes=True) id: int created_at: datetime class MistakeBase(BaseModel): title: str = Field(..., max_length=255) image_url: str | None = None category: str difficulty: str | None = Field(None, pattern="^(easy|medium|hard)$") question_content: str | None = Field(None, max_length=8000) answer: str | None = Field(None, max_length=4000) explanation: str | None = Field(None, max_length=8000) note: str | None = Field(None, max_length=4000) wrong_count: int = Field(1, ge=1) class MistakeCreate(MistakeBase): pass class MistakeUpdate(MistakeBase): pass class MistakeOut(MistakeBase): model_config = ConfigDict(from_attributes=True) id: int created_at: datetime class ScoreRecordBase(BaseModel): exam_name: str = Field(..., max_length=255) exam_date: date total_score: float = Field(..., ge=0, le=200) module_scores: str | None = None class ScoreRecordCreate(ScoreRecordBase): pass class ScoreRecordUpdate(ScoreRecordBase): pass class ScoreRecordOut(ScoreRecordBase): model_config = ConfigDict(from_attributes=True) id: int created_at: datetime class ScoreStats(BaseModel): highest: float lowest: float average: float improvement: float class ResourceBatchUpdate(BaseModel): ids: list[int] category: str | None = None tags: str | None = None class IdBatchPayload(BaseModel): ids: list[int] class AiMistakeAnalysisOut(BaseModel): analysis: str class AiStudyPlanIn(BaseModel): goal: str = Field(..., max_length=200) days_left: int = Field(30, ge=1, le=365) daily_hours: float = Field(2, ge=0.5, le=16) class AiStudyPlanOut(BaseModel): plan: str class OcrParseIn(BaseModel): image_url: str = Field(..., max_length=1024) prompt: str | None = Field(None, max_length=500) class OcrParseOut(BaseModel): text: str title_suggestion: str | None = None category_suggestion: str | None = None difficulty_suggestion: str | None = None question_content: str | None = None answer: str | None = None explanation: str | None = None