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:
Daniel 2026-04-05 02:26:41 +02:00
parent a2a2da7da0
commit 594bbfaf26
2 changed files with 37 additions and 44 deletions

View file

@ -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,
}

View file

@ -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>