from rest_framework import serializers from apps.course.models import Lesson, CourseLesson, Participant, LessonCompletion from apps.course.access import user_has_course_access from apps.quiz.serializers import QuizListSerializer from apps.course.models.course import get_localized_field class LessonSerializer(serializers.ModelSerializer): title = serializers.SerializerMethodField() content_type = serializers.SerializerMethodField() class Meta: model = Lesson fields = ['id', 'title', 'content_type', 'content_file', 'video_link', 'duration'] def _lang(self): request = self.context.get('request') return getattr(request, 'LANGUAGE_CODE', None) or 'en' def get_title(self, obj): return get_localized_field(self._lang(), obj.title) def get_content_type(self, obj): return get_localized_field(self._lang(), obj.content_type) class CourseLessonSerializer(serializers.ModelSerializer): is_active = serializers.SerializerMethodField() is_complated = serializers.SerializerMethodField() quizs = serializers.SerializerMethodField() permission = serializers.SerializerMethodField() title = serializers.SerializerMethodField() content_type = serializers.SerializerMethodField() content_file = serializers.FileField(source='lesson.content_file', read_only=True) video_link = serializers.CharField(source='lesson.video_link', read_only=True) duration = serializers.IntegerField(source='lesson.duration', read_only=True) class Meta: model = CourseLesson fields = ['id', 'title', 'priority', 'is_active', 'permission', 'duration', 'content_type', 'content_file', 'video_link', 'is_complated', 'quizs'] def _lang(self): request = self.context.get('request') return getattr(request, 'LANGUAGE_CODE', None) or 'en' def get_title(self, obj): return get_localized_field(self._lang(), obj.title) def get_content_type(self, obj): return get_localized_field(self._lang(), obj.lesson.content_type) def get_is_active(self, obj): return obj.is_active and self._has_access_for_object(obj) def get_permission(self, obj): return self._has_access_for_object(obj) def _has_access_for_object(self, obj): if user := self._get_authenticated_user(): return self._has_access(user, obj.course) return False def _has_access(self, user, course): return user_has_course_access(user, course) def _is_participant(self, student, course): """Deprecated: use _has_access instead.""" return self._has_access(student, course) def _get_authenticated_user(self): """Helper method to retrieve the authenticated user from the context.""" request = self.context.get('request') return request.user if request and request.user.is_authenticated else None def get_is_complated(self, obj): request = self.context.get('request') if not request or not request.user.is_authenticated: return False user = request.user if not self._has_access(user, obj.course): return False # Use prefetched completions if available if hasattr(obj, 'completions') and obj.completions.all(): return any(completion.student_id == user.id for completion in obj.completions.all()) # Fallback to direct queries is_participant = Participant.objects.filter( student=user, course=obj.course ).exists() if not is_participant: return False return LessonCompletion.objects.filter( student=user, course_lesson=obj ).exists() def get_quizs(self, obj): # Now quizzes are directly related to CourseLesson # print(f'--> type:{type(obj)} obj:{obj.quizzes.all()}') quizzes = obj.quizzes.all() if hasattr(obj, 'quizzes') else [] if quizzes: return QuizListSerializer(quizzes, many=True, context=self.context).data return None from apps.course.models import CourseChapter # 👇 ADD V2 SERIALIZERS class CourseChapterSerializer(serializers.ModelSerializer): """ V2 API: Returns a Chapter and a nested list of all its active lessons """ is_active = serializers.SerializerMethodField() lessons = serializers.SerializerMethodField() title = serializers.SerializerMethodField() class Meta: model = CourseChapter fields = ['id', 'title', 'priority', 'is_active', 'lessons'] def _lang(self): request = self.context.get('request') return getattr(request, 'LANGUAGE_CODE', None) or 'en' def get_title(self, obj): return get_localized_field(self._lang(), obj.title) def get_is_active(self, obj): if not obj.is_active: return False request = self.context.get('request') if not request or not request.user.is_authenticated: return False return user_has_course_access(request.user, obj.course) def get_lessons(self, obj): # Grab all active lessons tied to this specific chapter chapter_lessons = obj.lessons.filter(is_active=True).order_by('priority') # Re-use the V1 CourseLessonSerializer to serialize the nested items return CourseLessonSerializer(chapter_lessons, many=True, context=self.context).data