Fix course router: remove nonexistent model fields
- Remove category, thumbnail_url references (not on Course model) - Fix sort_order → position (matching actual column name) - Fix CourseCreate schema: only title + description - Fix tab labels: "My Created" → "Created by Me" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a2a2da7da0
commit
594bbfaf26
2 changed files with 37 additions and 44 deletions
|
|
@ -28,9 +28,6 @@ router = APIRouter()
|
|||
class CourseCreate(BaseModel):
|
||||
title: str
|
||||
description: str | None = None
|
||||
category: str | None = None
|
||||
thumbnail_url: str | None = None
|
||||
requires_subscription: int = 0
|
||||
|
||||
|
||||
class CourseUpdate(BaseModel):
|
||||
|
|
@ -44,13 +41,13 @@ class CourseUpdate(BaseModel):
|
|||
class ModuleCreate(BaseModel):
|
||||
title: str
|
||||
description: str | None = None
|
||||
sort_order: int | None = None
|
||||
position: int | None = None
|
||||
|
||||
|
||||
class ModuleUpdate(BaseModel):
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
sort_order: int | None = None
|
||||
position: int | None = None
|
||||
|
||||
|
||||
class ModuleReorder(BaseModel):
|
||||
|
|
@ -64,7 +61,7 @@ class LessonCreate(BaseModel):
|
|||
video_url: str | None = None
|
||||
duration_minutes: int | None = None
|
||||
is_required: int = 1
|
||||
sort_order: int | None = None
|
||||
position: int | None = None
|
||||
|
||||
|
||||
class LessonUpdate(BaseModel):
|
||||
|
|
@ -75,7 +72,7 @@ class LessonUpdate(BaseModel):
|
|||
video_provider: str | None = None
|
||||
duration_minutes: int | None = None
|
||||
is_required: int | None = None
|
||||
sort_order: int | None = None
|
||||
position: int | None = None
|
||||
|
||||
|
||||
class ProgressUpdate(BaseModel):
|
||||
|
|
@ -128,9 +125,6 @@ def create_course(
|
|||
course = Course(
|
||||
title=data.title,
|
||||
description=data.description,
|
||||
category=data.category,
|
||||
thumbnail_url=data.thumbnail_url,
|
||||
requires_subscription=data.requires_subscription,
|
||||
user_id=current_user.id,
|
||||
status="draft",
|
||||
)
|
||||
|
|
@ -186,9 +180,9 @@ def list_courses(
|
|||
"id": course.id,
|
||||
"title": course.title,
|
||||
"description": course.description,
|
||||
"category": course.category,
|
||||
|
||||
"status": course.status,
|
||||
"thumbnail_url": course.thumbnail_url,
|
||||
"thumbnail_path": course.thumbnail_path,
|
||||
"requires_subscription": course.requires_subscription,
|
||||
"user_id": course.user_id,
|
||||
"creator_name": user_cache[course.user_id],
|
||||
|
|
@ -242,8 +236,8 @@ def list_published_courses(
|
|||
"id": course.id,
|
||||
"title": course.title,
|
||||
"description": course.description,
|
||||
"category": course.category,
|
||||
"thumbnail_url": course.thumbnail_url,
|
||||
|
||||
"thumbnail_path": course.thumbnail_path,
|
||||
"requires_subscription": course.requires_subscription,
|
||||
"user_id": course.user_id,
|
||||
"creator_name": user_cache[course.user_id],
|
||||
|
|
@ -275,8 +269,8 @@ def my_courses(
|
|||
"id": course.id,
|
||||
"title": course.title,
|
||||
"description": course.description,
|
||||
"category": course.category,
|
||||
"thumbnail_url": course.thumbnail_url,
|
||||
|
||||
"thumbnail_path": course.thumbnail_path,
|
||||
"status": course.status,
|
||||
"enrollment_id": enrollment.id,
|
||||
"progress_pct": enrollment.progress_pct,
|
||||
|
|
@ -318,7 +312,7 @@ def get_course(
|
|||
modules = (
|
||||
db.query(CourseModule)
|
||||
.filter(CourseModule.course_id == course_id)
|
||||
.order_by(CourseModule.sort_order, CourseModule.id)
|
||||
.order_by(CourseModule.position, CourseModule.id)
|
||||
.all()
|
||||
)
|
||||
modules_out = []
|
||||
|
|
@ -326,14 +320,14 @@ def get_course(
|
|||
lessons = (
|
||||
db.query(CourseLesson)
|
||||
.filter(CourseLesson.module_id == mod.id)
|
||||
.order_by(CourseLesson.sort_order, CourseLesson.id)
|
||||
.order_by(CourseLesson.position, CourseLesson.id)
|
||||
.all()
|
||||
)
|
||||
modules_out.append({
|
||||
"id": mod.id,
|
||||
"title": mod.title,
|
||||
"description": mod.description,
|
||||
"sort_order": mod.sort_order,
|
||||
"position": mod.position,
|
||||
"lessons": [
|
||||
{
|
||||
"id": les.id,
|
||||
|
|
@ -345,7 +339,7 @@ def get_course(
|
|||
"file_path": les.file_path,
|
||||
"duration_minutes": les.duration_minutes,
|
||||
"is_required": les.is_required,
|
||||
"sort_order": les.sort_order,
|
||||
"position": les.position,
|
||||
"bbb_meeting_id": les.bbb_meeting_id,
|
||||
}
|
||||
for les in lessons
|
||||
|
|
@ -388,9 +382,8 @@ def get_course(
|
|||
"id": course.id,
|
||||
"title": course.title,
|
||||
"description": course.description,
|
||||
"category": course.category,
|
||||
"status": course.status,
|
||||
"thumbnail_url": course.thumbnail_url,
|
||||
"thumbnail_path": course.thumbnail_path,
|
||||
"requires_subscription": course.requires_subscription,
|
||||
"user_id": course.user_id,
|
||||
"creator_name": creator_name,
|
||||
|
|
@ -476,11 +469,11 @@ async def upload_thumbnail(
|
|||
with open(file_path, "wb") as f:
|
||||
f.write(contents)
|
||||
|
||||
course.thumbnail_url = f"/uploads/course_thumbnails/{filename}"
|
||||
course.thumbnail_path = f"/uploads/course_thumbnails/{filename}"
|
||||
course.updated_at = datetime.utcnow()
|
||||
db.commit()
|
||||
|
||||
return {"thumbnail_url": course.thumbnail_url}
|
||||
return {"thumbnail_path": course.thumbnail_path}
|
||||
|
||||
|
||||
# ── Module CRUD ──────────────────────────────────────────────────────
|
||||
|
|
@ -495,21 +488,21 @@ def create_module(
|
|||
"""Add a module to a course (creator or admin)."""
|
||||
_course_owner_or_admin(course_id, current_user, db)
|
||||
|
||||
# Auto sort_order if not provided
|
||||
sort_order = data.sort_order
|
||||
if sort_order is None:
|
||||
# Auto position if not provided
|
||||
position = data.position
|
||||
if position is None:
|
||||
max_order = (
|
||||
db.query(func.max(CourseModule.sort_order))
|
||||
db.query(func.max(CourseModule.position))
|
||||
.filter(CourseModule.course_id == course_id)
|
||||
.scalar()
|
||||
)
|
||||
sort_order = (max_order or 0) + 1
|
||||
position = (max_order or 0) + 1
|
||||
|
||||
module = CourseModule(
|
||||
course_id=course_id,
|
||||
title=data.title,
|
||||
description=data.description,
|
||||
sort_order=sort_order,
|
||||
position=position,
|
||||
)
|
||||
db.add(module)
|
||||
db.commit()
|
||||
|
|
@ -519,7 +512,7 @@ def create_module(
|
|||
"course_id": module.course_id,
|
||||
"title": module.title,
|
||||
"description": module.description,
|
||||
"sort_order": module.sort_order,
|
||||
"position": module.position,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -548,7 +541,7 @@ def update_module(
|
|||
"id": module.id,
|
||||
"title": module.title,
|
||||
"description": module.description,
|
||||
"sort_order": module.sort_order,
|
||||
"position": module.position,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -588,7 +581,7 @@ def reorder_modules(
|
|||
.first()
|
||||
)
|
||||
if module:
|
||||
module.sort_order = idx + 1
|
||||
module.position = idx + 1
|
||||
db.commit()
|
||||
return {"reordered": True, "count": len(data.module_ids)}
|
||||
|
||||
|
|
@ -614,15 +607,15 @@ def create_lesson(
|
|||
if not module:
|
||||
raise HTTPException(status_code=404, detail="Module not found")
|
||||
|
||||
# Auto sort_order if not provided
|
||||
sort_order = data.sort_order
|
||||
if sort_order is None:
|
||||
# Auto position if not provided
|
||||
position = data.position
|
||||
if position is None:
|
||||
max_order = (
|
||||
db.query(func.max(CourseLesson.sort_order))
|
||||
db.query(func.max(CourseLesson.position))
|
||||
.filter(CourseLesson.module_id == module_id)
|
||||
.scalar()
|
||||
)
|
||||
sort_order = (max_order or 0) + 1
|
||||
position = (max_order or 0) + 1
|
||||
|
||||
video_provider = _detect_video_provider(data.video_url)
|
||||
|
||||
|
|
@ -635,7 +628,7 @@ def create_lesson(
|
|||
video_provider=video_provider,
|
||||
duration_minutes=data.duration_minutes,
|
||||
is_required=data.is_required,
|
||||
sort_order=sort_order,
|
||||
position=position,
|
||||
)
|
||||
db.add(lesson)
|
||||
db.commit()
|
||||
|
|
@ -647,7 +640,7 @@ def create_lesson(
|
|||
"lesson_type": lesson.lesson_type,
|
||||
"video_url": lesson.video_url,
|
||||
"video_provider": lesson.video_provider,
|
||||
"sort_order": lesson.sort_order,
|
||||
"position": lesson.position,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -685,7 +678,7 @@ def update_lesson(
|
|||
"lesson_type": lesson.lesson_type,
|
||||
"video_url": lesson.video_url,
|
||||
"video_provider": lesson.video_provider,
|
||||
"sort_order": lesson.sort_order,
|
||||
"position": lesson.position,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -111,9 +111,9 @@ export default function CoursesPage() {
|
|||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
|
||||
<h2 style={{ marginBottom: 0 }}>Courses</h2>
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
<button className={`btn btn-sm ${tab === 'browse' ? 'btn-primary' : 'btn-secondary'}`} onClick={() => setTab('browse')}>Browse Courses</button>
|
||||
<button className={`btn btn-sm ${tab === 'enrolled' ? 'btn-primary' : 'btn-secondary'}`} onClick={() => setTab('enrolled')}>My Courses</button>
|
||||
<button className={`btn btn-sm ${tab === 'created' ? 'btn-primary' : 'btn-secondary'}`} onClick={() => setTab('created')}>My Created</button>
|
||||
<button className={`btn btn-sm ${tab === 'browse' ? 'btn-primary' : 'btn-secondary'}`} onClick={() => setTab('browse')}>Browse</button>
|
||||
<button className={`btn btn-sm ${tab === 'enrolled' ? 'btn-primary' : 'btn-secondary'}`} onClick={() => setTab('enrolled')}>Enrolled</button>
|
||||
<button className={`btn btn-sm ${tab === 'created' ? 'btn-primary' : 'btn-secondary'}`} onClick={() => setTab('created')}>Created by Me</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue