Files
pyisu/backend/app/schemas.py
2026-03-13 14:39:43 +08:00

355 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from pydantic import BaseModel, EmailStr, Field
from typing import Optional, List, Dict, Any
from datetime import datetime
# User schemas
class UserBase(BaseModel):
email: EmailStr
class UserCreate(UserBase):
password: str = Field(..., min_length=8, max_length=100)
class UserLogin(BaseModel):
email: EmailStr
password: str
class UserResponse(UserBase):
id: int
created_at: datetime
is_active: bool
is_developer: bool
class Config:
from_attributes = True
# Token schemas
class Token(BaseModel):
access_token: str
refresh_token: str
token_type: str = "bearer"
class TokenData(BaseModel):
email: Optional[str] = None
# Course schemas
class CourseBase(BaseModel):
title: str = Field(..., max_length=255)
description: str
level: str = Field(..., pattern="^(beginner|intermediate|advanced)$")
duration: int = Field(0, ge=0)
status: str = Field('draft', pattern="^(draft|published|archived)$")
class CourseCreate(CourseBase):
pass
# Course builder schemas
class CourseCreateRequest(BaseModel):
title: str = Field(..., max_length=255)
description: str
level: str = Field(..., pattern="^(beginner|intermediate|advanced)$")
duration: int = Field(..., ge=0)
content: str = Field(..., min_length=1)
structure: Optional[dict] = None
class CourseUpdate(BaseModel):
title: Optional[str] = Field(None, max_length=255)
description: Optional[str] = None
level: Optional[str] = Field(None, pattern="^(beginner|intermediate|advanced)$")
duration: Optional[int] = Field(None, ge=0)
content: Optional[str] = None
status: Optional[str] = Field(None, pattern="^(draft|published|archived)$")
class CourseResponse(CourseBase):
id: int
author_id: int
content: Optional[str] = None
status: str
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
# Course response with full structure
class CourseStructureResponse(CourseBase):
id: int
author_id: int
content: Optional[str] = None
status: str
created_at: datetime
updated_at: datetime
modules: list['ModuleResponse'] = []
class Config:
from_attributes = True
# Module schemas
class ModuleBase(BaseModel):
title: str = Field(..., max_length=255)
description: Optional[str] = None
duration: Optional[str] = Field(None, max_length=100)
order_index: int = 0
class ModuleCreate(ModuleBase):
course_id: int
class ModuleUpdate(BaseModel):
title: Optional[str] = Field(None, max_length=255)
description: Optional[str] = None
duration: Optional[str] = Field(None, max_length=100)
order_index: Optional[int] = None
class ModuleResponse(ModuleBase):
id: int
course_id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
# Lesson schemas
class LessonBase(BaseModel):
title: str = Field(..., max_length=255)
description: Optional[str] = None
order_index: int = 0
class LessonCreate(LessonBase):
module_id: int
class LessonUpdate(BaseModel):
title: Optional[str] = Field(None, max_length=255)
description: Optional[str] = None
order_index: Optional[int] = None
class LessonResponse(LessonBase):
id: int
module_id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
# Lesson Step schemas
class StepBase(BaseModel):
step_type: str = Field(..., pattern="^(theory|practice|lab|test|presentation)$")
title: Optional[str] = Field(None, max_length=255)
content: Optional[str] = None
content_html: Optional[str] = None
file_url: Optional[str] = Field(None, max_length=500)
order_index: int = 0
class StepCreate(StepBase):
lesson_id: int
class StepUpdate(BaseModel):
step_type: Optional[str] = Field(None, pattern="^(theory|practice|lab|test|presentation)$")
title: Optional[str] = Field(None, max_length=255)
content: Optional[str] = None
content_html: Optional[str] = None
file_url: Optional[str] = Field(None, max_length=500)
order_index: Optional[int] = None
class StepResponse(StepBase):
id: int
lesson_id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
# Test schemas
class TestSettings(BaseModel):
passing_score: int = Field(70, ge=0, le=100)
max_attempts: int = Field(0, ge=0)
time_limit: int = Field(0, ge=0)
show_answers: bool = False
class TestAnswerBase(BaseModel):
answer_text: str
is_correct: bool = False
order_index: int = 0
class TestAnswerCreate(TestAnswerBase):
pass
class TestAnswerResponse(TestAnswerBase):
id: int
question_id: int
created_at: datetime
class Config:
from_attributes = True
class TestMatchItemBase(BaseModel):
left_text: str
right_text: str
order_index: int = 0
class TestMatchItemCreate(TestMatchItemBase):
pass
class TestMatchItemResponse(TestMatchItemBase):
id: int
question_id: int
created_at: datetime
class Config:
from_attributes = True
class TestQuestionBase(BaseModel):
question_text: str
question_type: str = Field(..., pattern="^(single_choice|multiple_choice|text_answer|match)$")
points: int = Field(1, ge=1)
order_index: int = 0
class TestQuestionCreate(TestQuestionBase):
answers: List[TestAnswerCreate] = []
match_items: List[TestMatchItemCreate] = []
correct_answers: List[str] = [] # Для text_answer
class TestQuestionUpdate(BaseModel):
question_text: Optional[str] = None
question_type: Optional[str] = Field(None, pattern="^(single_choice|multiple_choice|text_answer|match)$")
points: Optional[int] = Field(None, ge=1)
order_index: Optional[int] = None
answers: Optional[List[TestAnswerCreate]] = None
match_items: Optional[List[TestMatchItemCreate]] = None
correct_answers: Optional[List[str]] = None
class TestQuestionResponse(TestQuestionBase):
id: int
step_id: int
created_at: datetime
updated_at: datetime
answers: List[TestAnswerResponse] = []
match_items: List[TestMatchItemResponse] = []
class Config:
from_attributes = True
class TestQuestionForViewer(TestQuestionResponse):
"""Вопрос без правильных ответов для отображения студенту"""
answers: List[dict] = [] # {id, answer_text, order_index} без is_correct
match_items: List[TestMatchItemResponse] = []
class TestAttemptCreate(BaseModel):
step_id: int
answers: Dict[str, Any] # {question_id: answer_id или [answer_ids] или text или {left_id: right_id}}
class TestAttemptResponse(BaseModel):
id: int
score: float
max_score: float
passed: bool
completed_at: datetime
class Config:
from_attributes = True
class TestAttemptWithDetailsResponse(TestAttemptResponse):
"""Попытка с деталями для просмотра результатов"""
answers: Dict[str, Any]
# Full course structure schemas
class CourseStructureResponse(CourseResponse):
modules: list["ModuleWithLessonsResponse"] = []
class ModuleWithLessonsResponse(ModuleResponse):
lessons: list["LessonWithStepsResponse"] = []
class LessonWithStepsResponse(LessonResponse):
steps: list[StepResponse] = []
# Update forward references
CourseStructureResponse.update_forward_refs()
ModuleWithLessonsResponse.update_forward_refs()
LessonWithStepsResponse.update_forward_refs()
# Favorite schemas
class FavoriteBase(BaseModel):
course_id: int
class FavoriteCreate(FavoriteBase):
pass
class FavoriteResponse(FavoriteBase):
user_id: int
added_at: datetime
class Config:
from_attributes = True
# Progress schemas
class ProgressBase(BaseModel):
course_id: int
completed_lessons: int = Field(..., ge=0)
total_lessons: int = Field(..., gt=0)
class ProgressCreate(ProgressBase):
pass
class ProgressUpdate(BaseModel):
completed_lessons: int = Field(..., ge=0)
total_lessons: int = Field(..., gt=0)
class ProgressResponse(ProgressBase):
user_id: int
percentage: float
updated_at: datetime
class Config:
from_attributes = True
# Stats schemas
class StatsResponse(BaseModel):
total_users: int
total_courses: int
most_popular_course: Optional[str] = None
# Response schemas
class SuccessResponse(BaseModel):
success: bool = True
data: Optional[dict] = None
message: Optional[str] = None
# ErrorResponse
class ErrorResponse(BaseModel):
success: bool = False
error: str
details: Optional[dict] = None
# Course builder schemas
class CourseCreateRequest(BaseModel):
title: str = Field(..., max_length=255)
description: str
level: str = Field(..., pattern="^(beginner|intermediate|advanced)$")
duration: int = Field(..., ge=0)
content: str = Field(..., min_length=1)
structure: Optional[dict] = None
# Course draft schemas
class CourseDraftBase(BaseModel):
title: str = Field(..., max_length=255)
description: str
level: str = Field(..., pattern="^(beginner|intermediate|advanced)$")
content: str
class CourseDraftCreate(CourseDraftBase):
structure: Optional[str] = None # JSON string
class CourseDraftUpdate(CourseDraftCreate):
pass
class CourseDraftResponse(CourseDraftBase):
id: int
user_id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True