第一次提交
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
"""作答记录与 AI 功能的 schema。"""
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class AttemptCreate(BaseModel):
|
||||
user_answer: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class AttemptOut(BaseModel):
|
||||
"""一次作答记录。is_correct 为 null 表示"已记录,尚未判分"。"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
question_id: str
|
||||
user_answer: list[str] | None
|
||||
is_correct: bool | None
|
||||
judged_by: str
|
||||
ai_feedback_markdown: str | None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class SelfJudge(BaseModel):
|
||||
"""自评:用户自己判定对错,不花 AI 额度。"""
|
||||
|
||||
is_correct: bool
|
||||
|
||||
|
||||
class JudgeResult(BaseModel):
|
||||
"""按需判分的结果。"""
|
||||
|
||||
attempt_id: str
|
||||
is_correct: bool | None
|
||||
judged_by: str
|
||||
feedback_markdown: str | None = None
|
||||
key_points_missed: list[str] = Field(default_factory=list)
|
||||
correct_answer: list[str] | None = None
|
||||
|
||||
|
||||
class ExplainOut(BaseModel):
|
||||
explanation_markdown: str
|
||||
|
||||
|
||||
class TagsOut(BaseModel):
|
||||
tags: list[str]
|
||||
@@ -0,0 +1,22 @@
|
||||
"""认证相关的请求/响应 schema。"""
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class RegisterRequest(BaseModel):
|
||||
username: str = Field(min_length=3, max_length=64)
|
||||
password: str = Field(min_length=6, max_length=128)
|
||||
email: str | None = Field(default=None, max_length=255)
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
|
||||
|
||||
class UserOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
username: str
|
||||
email: str | None
|
||||
is_active: bool
|
||||
@@ -0,0 +1,30 @@
|
||||
"""图片与任务的 schema。"""
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
from app.models.job import JobStatus
|
||||
|
||||
|
||||
class ImageOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
object_key: str
|
||||
mime: str
|
||||
size_bytes: int
|
||||
|
||||
|
||||
class JobOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
status: JobStatus
|
||||
image_id: str | None
|
||||
# 一张图可能拆成多条笔记。任务未完成时库里是 NULL,对前端统一成空数组
|
||||
result_question_ids: list[str] = Field(default_factory=list)
|
||||
engine_used: str | None
|
||||
error_message: str | None
|
||||
|
||||
@field_validator("result_question_ids", mode="before")
|
||||
@classmethod
|
||||
def _none_to_empty(cls, v):
|
||||
return v or []
|
||||
@@ -0,0 +1,29 @@
|
||||
"""首页概览与复习清单的 schema。"""
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.schemas.question import QuestionOut
|
||||
|
||||
|
||||
class TagStat(BaseModel):
|
||||
name: str
|
||||
count: int
|
||||
needs_review_count: int
|
||||
|
||||
|
||||
class NotebookOverview(BaseModel):
|
||||
"""首页一次拿全:知识点导航 + 待复习计数 + 最近记录。"""
|
||||
|
||||
total_notes: int
|
||||
review_count: int
|
||||
untagged_count: int
|
||||
tags: list[TagStat]
|
||||
recent: list[QuestionOut]
|
||||
|
||||
|
||||
class ReviewItem(BaseModel):
|
||||
question: QuestionOut
|
||||
# 为什么在复习清单里:手动标记 / 最近一次答错
|
||||
marked: bool
|
||||
last_wrong: bool
|
||||
wrong_count: int
|
||||
last_wrong_at: str | None
|
||||
@@ -0,0 +1,86 @@
|
||||
"""笔记相关的请求/响应 schema。"""
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.models.question import QuestionType
|
||||
|
||||
|
||||
class OptionIn(BaseModel):
|
||||
key: str = Field(max_length=8) # A/B/C/D 等
|
||||
text_markdown: str
|
||||
|
||||
|
||||
class QuestionBase(BaseModel):
|
||||
# 默认未分类:拍照记下来就能存,不强制先想清楚题型
|
||||
type: QuestionType = QuestionType.unclassified
|
||||
stem_markdown: str
|
||||
options: list[OptionIn] | None = None
|
||||
correct_answer: list[str] | None = None
|
||||
explanation_markdown: str | None = None
|
||||
my_note_markdown: str | None = None
|
||||
needs_review: bool = False
|
||||
difficulty: int | None = Field(default=None, ge=1, le=5)
|
||||
|
||||
|
||||
class QuestionCreate(QuestionBase):
|
||||
tags: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class QuestionUpdate(BaseModel):
|
||||
"""全部可选,只更新提供的字段。"""
|
||||
|
||||
type: QuestionType | None = None
|
||||
stem_markdown: str | None = None
|
||||
options: list[OptionIn] | None = None
|
||||
correct_answer: list[str] | None = None
|
||||
explanation_markdown: str | None = None
|
||||
my_note_markdown: str | None = None
|
||||
needs_review: bool | None = None
|
||||
difficulty: int | None = Field(default=None, ge=1, le=5)
|
||||
tags: list[str] | None = None
|
||||
|
||||
|
||||
class QuestionOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
type: QuestionType
|
||||
stem_markdown: str
|
||||
options: list[dict] | None
|
||||
correct_answer: list[str] | None
|
||||
explanation_markdown: str | None
|
||||
my_note_markdown: str | None
|
||||
needs_review: bool
|
||||
difficulty: int | None
|
||||
source_image_id: str | None
|
||||
ocr_engine: str | None
|
||||
tags: list[str] = Field(default_factory=list)
|
||||
created_at: datetime | None = None
|
||||
updated_at: datetime | None = None
|
||||
|
||||
@classmethod
|
||||
def from_model(cls, q) -> "QuestionOut":
|
||||
return cls(
|
||||
id=q.id,
|
||||
type=q.type,
|
||||
stem_markdown=q.stem_markdown,
|
||||
options=q.options,
|
||||
correct_answer=q.correct_answer,
|
||||
explanation_markdown=q.explanation_markdown,
|
||||
my_note_markdown=q.my_note_markdown,
|
||||
needs_review=q.needs_review,
|
||||
difficulty=q.difficulty,
|
||||
source_image_id=q.source_image_id,
|
||||
ocr_engine=q.ocr_engine,
|
||||
tags=[t.name for t in q.tags],
|
||||
created_at=q.created_at,
|
||||
updated_at=q.updated_at,
|
||||
)
|
||||
|
||||
|
||||
class QuestionList(BaseModel):
|
||||
items: list[QuestionOut]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
@@ -0,0 +1,17 @@
|
||||
"""用户 AI 配置 schema。api_key 只写不回显明文。"""
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SettingsOut(BaseModel):
|
||||
llm_base_url: str | None = None
|
||||
llm_text_model: str | None = None
|
||||
llm_vision_model: str | None = None
|
||||
has_api_key: bool = False # 是否已配置 key(不回显明文)
|
||||
|
||||
|
||||
class SettingsUpdate(BaseModel):
|
||||
llm_base_url: str | None = None
|
||||
llm_text_model: str | None = None
|
||||
llm_vision_model: str | None = None
|
||||
# 留空表示不修改现有 key;显式传值则更新
|
||||
api_key: str | None = None
|
||||
Reference in New Issue
Block a user