
- Set up project structure with FastAPI and SQLite - Create models for users, questions, and quizzes - Implement Alembic migrations with seed data - Add user authentication with JWT - Implement question management endpoints - Implement quiz creation and management - Add quiz-taking and scoring functionality - Set up API documentation and health check endpoint - Update README with comprehensive documentation
158 lines
3.4 KiB
Python
158 lines
3.4 KiB
Python
from datetime import datetime
|
|
from enum import Enum
|
|
from typing import List, Optional
|
|
|
|
from pydantic import BaseModel, Field, validator
|
|
|
|
from app.schemas.question import Question
|
|
from app.schemas.user import User
|
|
|
|
|
|
class QuizQuestionBase(BaseModel):
|
|
question_id: int
|
|
position: int = 0
|
|
|
|
|
|
class QuizQuestionCreate(QuizQuestionBase):
|
|
pass
|
|
|
|
|
|
class QuizQuestionUpdate(QuizQuestionBase):
|
|
question_id: Optional[int] = None
|
|
position: Optional[int] = None
|
|
|
|
|
|
class QuizQuestionInDBBase(QuizQuestionBase):
|
|
id: int
|
|
quiz_id: int
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
|
|
class QuizQuestion(QuizQuestionInDBBase):
|
|
question: Optional[Question] = None
|
|
|
|
|
|
class QuizBase(BaseModel):
|
|
title: str
|
|
description: Optional[str] = None
|
|
is_public: bool = False
|
|
time_limit_minutes: int = 10
|
|
pass_percentage: float = 70.0
|
|
|
|
|
|
class QuizCreate(QuizBase):
|
|
questions: List[QuizQuestionCreate] = Field(..., min_items=1)
|
|
|
|
@validator("questions")
|
|
def validate_unique_questions(cls, v):
|
|
question_ids = [q.question_id for q in v]
|
|
if len(question_ids) != len(set(question_ids)):
|
|
raise ValueError("Quiz cannot contain duplicate questions")
|
|
return v
|
|
|
|
|
|
class QuizUpdate(QuizBase):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
is_public: Optional[bool] = None
|
|
time_limit_minutes: Optional[int] = None
|
|
pass_percentage: Optional[float] = None
|
|
questions: Optional[List[QuizQuestionCreate]] = None
|
|
|
|
@validator("questions")
|
|
def validate_unique_questions(cls, v):
|
|
if v is not None:
|
|
question_ids = [q.question_id for q in v]
|
|
if len(question_ids) != len(set(question_ids)):
|
|
raise ValueError("Quiz cannot contain duplicate questions")
|
|
return v
|
|
|
|
|
|
class QuizInDBBase(QuizBase):
|
|
id: int
|
|
user_id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
|
|
class Quiz(QuizInDBBase):
|
|
user: Optional[User] = None
|
|
questions: List[QuizQuestion] = []
|
|
|
|
|
|
class QuizWithoutDetails(QuizInDBBase):
|
|
user: Optional[User] = None
|
|
question_count: int
|
|
|
|
|
|
class AttemptStatus(str, Enum):
|
|
started = "started"
|
|
completed = "completed"
|
|
abandoned = "abandoned"
|
|
|
|
|
|
class QuizAnswerBase(BaseModel):
|
|
quiz_question_id: int
|
|
selected_option_id: Optional[int] = None
|
|
|
|
|
|
class QuizAnswerCreate(QuizAnswerBase):
|
|
pass
|
|
|
|
|
|
class QuizAnswerUpdate(QuizAnswerBase):
|
|
selected_option_id: Optional[int] = None
|
|
|
|
|
|
class QuizAnswerInDBBase(QuizAnswerBase):
|
|
id: int
|
|
quiz_attempt_id: int
|
|
is_correct: Optional[bool] = None
|
|
answered_at: datetime
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
|
|
class QuizAnswer(QuizAnswerInDBBase):
|
|
pass
|
|
|
|
|
|
class QuizAttemptBase(BaseModel):
|
|
quiz_id: int
|
|
status: AttemptStatus = AttemptStatus.started
|
|
score: Optional[float] = None
|
|
started_at: datetime = Field(default_factory=datetime.utcnow)
|
|
completed_at: Optional[datetime] = None
|
|
|
|
|
|
class QuizAttemptCreate(QuizAttemptBase):
|
|
pass
|
|
|
|
|
|
class QuizAttemptUpdate(QuizAttemptBase):
|
|
status: Optional[AttemptStatus] = None
|
|
score: Optional[float] = None
|
|
completed_at: Optional[datetime] = None
|
|
|
|
|
|
class QuizAttemptInDBBase(QuizAttemptBase):
|
|
id: int
|
|
user_id: int
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
|
|
class QuizAttempt(QuizAttemptInDBBase):
|
|
quiz: Optional[Quiz] = None
|
|
answers: List[QuizAnswer] = []
|
|
|
|
|
|
class QuizSubmitAnswer(BaseModel):
|
|
selected_option_id: int |