- FastAPI backend with JWT auth, roles (admin/moderator/user) - PDF upload (up to 500MB) with streaming, PyMuPDF text extraction - ChromaDB vectorization per page with metadata - LiteLLM AI question extraction from PDF (not generation) - Image extraction from PDF pages, graceful fallback - Quiz modes: timed (countdown timer) + learning (answers shown inline) - Page-by-page question navigation with dot navigator - TTS endpoint using LiteLLM (Google Vertex / OpenAI voices) - Admin dashboard: AI model management per task, user role management - Moderator role: upload PDFs, create sections, generate quizzes - Spaced repetition reminders via SMTP email (SM-2 intervals) - APScheduler daily reminder jobs - Celery + Redis for background PDF processing - React frontend with all pages - Docker Compose deployment (nginx + backend + celery + redis) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
55 lines
1.2 KiB
Python
55 lines
1.2 KiB
Python
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, field_validator
|
|
|
|
|
|
class SectionCreate(BaseModel):
|
|
name: str
|
|
start_page: int
|
|
end_page: int
|
|
|
|
@field_validator("end_page")
|
|
@classmethod
|
|
def end_after_start(cls, v, info):
|
|
if "start_page" in info.data and v <= info.data["start_page"]:
|
|
raise ValueError("end_page must be greater than start_page")
|
|
return v
|
|
|
|
@field_validator("start_page")
|
|
@classmethod
|
|
def start_positive(cls, v):
|
|
if v < 1:
|
|
raise ValueError("start_page must be at least 1")
|
|
return v
|
|
|
|
|
|
class SectionResponse(BaseModel):
|
|
id: int
|
|
document_id: int
|
|
name: str
|
|
start_page: int
|
|
end_page: int
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class DocumentResponse(BaseModel):
|
|
id: int
|
|
user_id: int
|
|
original_filename: str
|
|
total_pages: int | None
|
|
status: str
|
|
error_message: str | None
|
|
uploaded_at: datetime
|
|
sections: list[SectionResponse] = []
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class DocumentStatusResponse(BaseModel):
|
|
id: int
|
|
status: str
|
|
total_pages: int | None
|
|
error_message: str | None
|