173 lines
6.1 KiB
Python
173 lines
6.1 KiB
Python
from rest_framework import serializers
|
|
|
|
from apps.courses.api.serializers import (
|
|
CategorySerializer,
|
|
CourseFAQSerializer,
|
|
CourseHighlightSerializer,
|
|
LessonReadSerializer,
|
|
SectionReadSerializer,
|
|
)
|
|
from apps.courses.models import Lesson, LessonStatus
|
|
from apps.enrollments.models import Enrollment, LessonProgress
|
|
|
|
|
|
class EnrolledCourseSummarySerializer(serializers.Serializer):
|
|
"""Used by the my-courses dashboard list. Mirrors the design's MY_COURSES shape."""
|
|
|
|
id = serializers.IntegerField()
|
|
slug = serializers.CharField()
|
|
title = serializers.CharField()
|
|
tagline = serializers.CharField()
|
|
color = serializers.CharField()
|
|
cover_image = serializers.SerializerMethodField()
|
|
instructor_id = serializers.IntegerField()
|
|
instructor_name = serializers.SerializerMethodField()
|
|
enrolled_at = serializers.DateTimeField()
|
|
completed_at = serializers.DateTimeField(allow_null=True)
|
|
progress_percent = serializers.IntegerField()
|
|
completed_lessons = serializers.IntegerField()
|
|
total_lessons = serializers.IntegerField()
|
|
last_lesson_id = serializers.IntegerField(allow_null=True)
|
|
last_lesson_title = serializers.CharField(allow_null=True)
|
|
next_lesson_id = serializers.IntegerField(allow_null=True)
|
|
next_lesson_title = serializers.CharField(allow_null=True)
|
|
|
|
def get_cover_image(self, obj):
|
|
url = obj.get("cover_image_url")
|
|
request = self.context.get("request")
|
|
if url and request:
|
|
return request.build_absolute_uri(url)
|
|
return url
|
|
|
|
def get_instructor_name(self, obj):
|
|
return obj.get("instructor_name")
|
|
|
|
|
|
class StudentCourseDetailSerializer(serializers.Serializer):
|
|
"""Full course detail for an enrolled student — all published lesson content unlocked."""
|
|
|
|
id = serializers.IntegerField()
|
|
slug = serializers.CharField()
|
|
title = serializers.CharField()
|
|
tagline = serializers.CharField()
|
|
description = serializers.CharField()
|
|
level = serializers.CharField()
|
|
language = serializers.CharField()
|
|
color = serializers.CharField()
|
|
cover_image = serializers.SerializerMethodField()
|
|
instructor_id = serializers.IntegerField()
|
|
instructor_name = serializers.CharField()
|
|
category = CategorySerializer(allow_null=True)
|
|
highlights = CourseHighlightSerializer(many=True)
|
|
faqs = CourseFAQSerializer(many=True)
|
|
sections = serializers.SerializerMethodField()
|
|
enrollment = serializers.SerializerMethodField()
|
|
|
|
def get_cover_image(self, obj):
|
|
course = obj["course"]
|
|
if not course.cover_image:
|
|
return None
|
|
request = self.context.get("request")
|
|
if request:
|
|
return request.build_absolute_uri(course.cover_image.url)
|
|
return course.cover_image.url
|
|
|
|
def get_sections(self, obj):
|
|
course = obj["course"]
|
|
completed_lesson_ids = obj["completed_lesson_ids"]
|
|
sections = course.sections.all().prefetch_related(
|
|
"lessons"
|
|
)
|
|
out = []
|
|
for sec in sections:
|
|
published = [l for l in sec.lessons.all() if l.status == LessonStatus.PUBLISHED]
|
|
published.sort(key=lambda l: (l.order, l.id))
|
|
out.append({
|
|
"id": sec.id,
|
|
"title": sec.title,
|
|
"order": sec.order,
|
|
"lessons": LessonReadSerializer(
|
|
published, many=True
|
|
).data,
|
|
"lessons_completed": [
|
|
l.id for l in published if l.id in completed_lesson_ids
|
|
],
|
|
})
|
|
return out
|
|
|
|
def get_enrollment(self, obj):
|
|
e = obj["enrollment"]
|
|
return {
|
|
"enrolled_at": e.enrolled_at,
|
|
"completed_at": e.completed_at,
|
|
"progress_percent": e.progress_percent(),
|
|
"completed_lessons": e.completed_lessons_count(),
|
|
"total_lessons": e.total_lessons_count(),
|
|
}
|
|
|
|
|
|
class EnrollResponseSerializer(serializers.Serializer):
|
|
enrolled = serializers.BooleanField()
|
|
course_id = serializers.IntegerField()
|
|
course_slug = serializers.CharField()
|
|
enrolled_at = serializers.DateTimeField()
|
|
|
|
|
|
class LessonProgressResponseSerializer(serializers.ModelSerializer):
|
|
progress_percent = serializers.SerializerMethodField()
|
|
course_completed = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = LessonProgress
|
|
fields = ["id", "lesson", "completed_at", "progress_percent", "course_completed"]
|
|
read_only_fields = fields
|
|
|
|
def get_progress_percent(self, obj):
|
|
return obj.enrollment.progress_percent()
|
|
|
|
def get_course_completed(self, obj):
|
|
return obj.enrollment.is_completed
|
|
|
|
|
|
class InstructorEnrollmentSerializer(serializers.ModelSerializer):
|
|
"""Compact list view for the instructor's CRM-style student panel."""
|
|
|
|
student_id = serializers.IntegerField(source="student.id", read_only=True)
|
|
student_name = serializers.CharField(source="student.full_name", read_only=True)
|
|
student_phone = serializers.CharField(source="student.phone_number", read_only=True)
|
|
student_last_active_at = serializers.DateTimeField(source="student.last_active_at", read_only=True)
|
|
progress_percent = serializers.SerializerMethodField()
|
|
completed_lessons = serializers.SerializerMethodField()
|
|
total_lessons = serializers.SerializerMethodField()
|
|
engagement_status = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Enrollment
|
|
fields = [
|
|
"id",
|
|
"student_id",
|
|
"student_name",
|
|
"student_phone",
|
|
"student_last_active_at",
|
|
"course_id",
|
|
"enrolled_at",
|
|
"completed_at",
|
|
"progress_percent",
|
|
"completed_lessons",
|
|
"total_lessons",
|
|
"engagement_status",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
def get_progress_percent(self, obj):
|
|
return obj.progress_percent()
|
|
|
|
def get_completed_lessons(self, obj):
|
|
return obj.completed_lessons_count()
|
|
|
|
def get_total_lessons(self, obj):
|
|
return obj.total_lessons_count()
|
|
|
|
def get_engagement_status(self, obj):
|
|
return obj.engagement_status()
|