355 lines
9.4 KiB
Python
355 lines
9.4 KiB
Python
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
|