LMS / Course System: - Course → Module → Lesson hierarchy (any user can create) - Lesson types: text, video (Vimeo/YouTube/local), document, quiz, live_session - Video provider auto-detection from URL - BBB API integration (create/join meetings) with env config - Course enrollment with per-lesson progress tracking - AI content generation/refinement for text lessons - Draft/published/archived status workflow - Subscription gate placeholder (requires_subscription flag) - Thumbnail upload support - Module/lesson reorder with up/down controls User Quiz Creation: - Any user can create quizzes from question bank (was moderator-only) - User quizzes: is_published=0, is_shared=0 (private by default) - Fixed section_id fallback: None instead of hardcoded 1 Manual Question Creation: - POST /questions/create endpoint for manual MCQ entry - CreateQuestionModal on QuestionBankPage with options, radio for correct answer - Auto-generates embedding on creation Frontend: - CoursesPage: Browse/My Courses/Created tabs with search and pagination - CourseDetailPage: Student view with module accordion, lesson viewer, progress - CourseEditorPage: Full course builder with AI generate, question bank browser - Courses link in Navbar - Create Question button on Question Bank (available to all users) Backend: - 5 new tables: courses, course_modules, course_lessons, course_enrollments, course_lesson_progress - Course model + schemas + router (22 endpoints) - BBB_SERVER_URL + BBB_SECRET config - Updated CLAUDE.md with LMS documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
135 lines
3.4 KiB
Python
135 lines
3.4 KiB
Python
from datetime import datetime
|
|
from pydantic import BaseModel
|
|
|
|
|
|
class CourseCreate(BaseModel):
|
|
title: str
|
|
description: str | None = None
|
|
|
|
|
|
class CourseUpdate(BaseModel):
|
|
title: str | None = None
|
|
description: str | None = None
|
|
status: str | None = None
|
|
is_featured: int | None = None
|
|
requires_subscription: int | None = None
|
|
|
|
|
|
class ModuleCreate(BaseModel):
|
|
title: str
|
|
description: str | None = None
|
|
|
|
|
|
class ModuleUpdate(BaseModel):
|
|
title: str | None = None
|
|
description: str | None = None
|
|
position: int | None = None
|
|
|
|
|
|
class LessonCreate(BaseModel):
|
|
title: str
|
|
lesson_type: str # text, video, document, quiz, live_session
|
|
description: str | None = None
|
|
content_text: str | None = None
|
|
video_url: str | None = None
|
|
local_file_path: str | None = None
|
|
quiz_id: int | None = None
|
|
live_session_url: str | None = None
|
|
live_session_start: datetime | None = None
|
|
live_session_end: datetime | None = None
|
|
duration_minutes: int | None = None
|
|
is_required: int = 1
|
|
|
|
|
|
class LessonUpdate(BaseModel):
|
|
title: str | None = None
|
|
description: str | None = None
|
|
content_text: str | None = None
|
|
video_url: str | None = None
|
|
local_file_path: str | None = None
|
|
quiz_id: int | None = None
|
|
live_session_url: str | None = None
|
|
live_session_start: datetime | None = None
|
|
live_session_end: datetime | None = None
|
|
duration_minutes: int | None = None
|
|
is_required: int | None = None
|
|
position: int | None = None
|
|
|
|
|
|
class LessonResponse(BaseModel):
|
|
id: int
|
|
module_id: int
|
|
title: str
|
|
description: str | None = None
|
|
position: int
|
|
lesson_type: str
|
|
content_text: str | None = None
|
|
video_url: str | None = None
|
|
video_provider: str | None = None
|
|
local_file_path: str | None = None
|
|
quiz_id: int | None = None
|
|
live_session_url: str | None = None
|
|
live_session_start: datetime | None = None
|
|
live_session_end: datetime | None = None
|
|
bbb_meeting_id: str | None = None
|
|
duration_minutes: int | None = None
|
|
is_required: int = 1
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class ModuleResponse(BaseModel):
|
|
id: int
|
|
course_id: int
|
|
title: str
|
|
description: str | None = None
|
|
position: int
|
|
lessons: list[LessonResponse] = []
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class CourseResponse(BaseModel):
|
|
id: int
|
|
title: str
|
|
description: str | None = None
|
|
thumbnail_path: str | None = None
|
|
user_id: int
|
|
status: str
|
|
is_featured: int = 0
|
|
requires_subscription: int = 0
|
|
created_at: datetime
|
|
updated_at: datetime | None = None
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class CourseDetail(CourseResponse):
|
|
modules: list[ModuleResponse] = []
|
|
creator_name: str | None = None
|
|
enrolled_count: int = 0
|
|
my_enrollment: dict | None = None
|
|
|
|
|
|
class EnrollmentResponse(BaseModel):
|
|
id: int
|
|
course_id: int
|
|
user_id: int
|
|
enrolled_at: datetime
|
|
completed_at: datetime | None = None
|
|
progress_pct: float = 0.0
|
|
status: str
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class LessonProgressUpdate(BaseModel):
|
|
status: str | None = None # in_progress, completed
|
|
time_spent_seconds: int | None = None
|
|
score: float | None = None
|
|
|
|
|
|
class AIContentRequest(BaseModel):
|
|
prompt: str
|
|
action: str = "generate" # generate, refine
|
|
existing_content: str | None = None
|