2026-03-05 11:50:15 +08:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2026-03-06 15:52:34 +08:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2026-03-05 11:50:15 +08:00
|
|
|
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
|