from rest_framework import serializers from utils import FileFieldSerializer, get_thumbs from apps.course.models import ( Course, CourseCategory, CourseChapter, CourseLesson, Lesson, CourseAttachment, Attachment, CourseGlossary, Glossary ) from apps.account.models import ProfessorUser def serialize_file(value, serializer): if not value: return None field = FileFieldSerializer() field.bind('', serializer) return field.to_representation(value) class AdminCourseCategorySerializer(serializers.ModelSerializer): class Meta: model = CourseCategory fields = ['id', 'name', 'slug'] class AdminCourseListSerializer(serializers.ModelSerializer): category_name = serializers.CharField(source='category.name', read_only=True) professor_name = serializers.CharField(source='professor.fullname', read_only=True) participant_count = serializers.SerializerMethodField() thumbnail = serializers.SerializerMethodField() class Meta: model = Course fields = [ 'id', 'title', 'slug', 'category', 'category_name', 'professor', 'professor_name', 'thumbnail', 'is_online', 'level', 'duration', 'lessons_count', 'status', 'is_free', 'price', 'discount_percentage', 'final_price', 'participant_count', 'created_at' ] def get_participant_count(self, obj): return obj.participants.count() def get_thumbnail(self, obj): return get_thumbs(obj.thumbnail, self.context.get('request')) class AdminCourseDetailSerializer(serializers.ModelSerializer): category_name = serializers.CharField(source='category.name', read_only=True) professor_name = serializers.CharField(source='professor.fullname', read_only=True) thumbnail = FileFieldSerializer(required=False, allow_null=True) video_file = FileFieldSerializer(required=False, allow_null=True) participant_count = serializers.SerializerMethodField() class Meta: model = Course fields = [ 'id', 'title', 'slug', 'category', 'category_name', 'professor', 'professor_name', 'thumbnail', 'video_type', 'video_file', 'video_link', 'is_online', 'online_link', 'level', 'duration', 'lessons_count', 'description', 'short_description', 'status', 'is_free', 'price', 'discount_percentage', 'final_price', 'is_group_chat_locked', 'is_professor_chat_locked', 'timing', 'features', 'participant_count', 'created_at', 'updated_at' ] read_only_fields = ['id', 'slug', 'final_price', 'created_at', 'updated_at'] def get_participant_count(self, obj): return obj.participants.count() class AdminCourseChapterSerializer(serializers.ModelSerializer): class Meta: model = CourseChapter fields = ['id', 'course', 'title', 'priority', 'is_active', 'created_at'] read_only_fields = ['id', 'created_at'] class AdminCourseLessonSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) course = serializers.IntegerField(required=False, read_only=True) chapter = serializers.IntegerField() title = serializers.CharField(max_length=255, required=False, allow_blank=True, allow_null=True) priority = serializers.IntegerField(required=False, allow_null=True) is_active = serializers.BooleanField(default=True) # Lesson (Base Content) fields content_type = serializers.ChoiceField(choices=Lesson.ContentTypeChoices.choices) content_file = FileFieldSerializer(required=False, allow_null=True) video_link = serializers.CharField(max_length=500, required=False, allow_blank=True, allow_null=True) duration = serializers.IntegerField(min_value=0) def to_representation(self, instance): """ Map a CourseLesson instance to representation fields. """ lesson_obj = instance.lesson return { 'id': instance.id, 'course': instance.course_id, 'chapter': instance.chapter_id, 'title': instance.title or (lesson_obj.title if lesson_obj else ""), 'priority': instance.priority, 'is_active': instance.is_active, 'content_type': lesson_obj.content_type if lesson_obj else None, 'content_file': serialize_file(lesson_obj.content_file, self) if lesson_obj and lesson_obj.content_file else None, 'video_link': lesson_obj.video_link if lesson_obj else "", 'duration': lesson_obj.duration if lesson_obj else 0, } def create(self, validated_data): chapter_id = validated_data.pop('chapter') chapter = CourseChapter.objects.get(pk=chapter_id) course = chapter.course title = validated_data.get('title') priority = validated_data.get('priority') is_active = validated_data.get('is_active', True) content_type = validated_data.get('content_type') content_file = validated_data.get('content_file') video_link = validated_data.get('video_link') duration = validated_data.get('duration', 0) # 1. Create Lesson Base lesson = Lesson.objects.create( title=title or "Base Lesson", content_type=content_type, content_file=content_file, video_link=video_link, duration=duration ) # 2. Create CourseLesson junction course_lesson = CourseLesson.objects.create( course=course, chapter=chapter, lesson=lesson, title=title, priority=priority, is_active=is_active ) return course_lesson def update(self, instance, validated_data): chapter_id = validated_data.get('chapter') if chapter_id: chapter = CourseChapter.objects.get(pk=chapter_id) instance.chapter = chapter instance.course = chapter.course if 'title' in validated_data: instance.title = validated_data['title'] if 'priority' in validated_data: instance.priority = validated_data['priority'] if 'is_active' in validated_data: instance.is_active = validated_data['is_active'] instance.save() # Update the Base Lesson as well lesson = instance.lesson if lesson: if 'title' in validated_data and validated_data['title']: lesson.title = validated_data['title'] if 'content_type' in validated_data: lesson.content_type = validated_data['content_type'] if 'content_file' in validated_data: lesson.content_file = validated_data['content_file'] if 'video_link' in validated_data: lesson.video_link = validated_data['video_link'] if 'duration' in validated_data: lesson.duration = validated_data['duration'] lesson.save() return instance class AdminCourseAttachmentSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) course = serializers.IntegerField() title = serializers.CharField(max_length=255) file = FileFieldSerializer(required=True) file_size = serializers.IntegerField(read_only=True) def to_representation(self, instance): attachment_obj = instance.attachment return { 'id': instance.id, 'course': instance.course_id, 'title': attachment_obj.title if attachment_obj else "", 'file': serialize_file(attachment_obj.file, self) if attachment_obj and attachment_obj.file else None, 'file_size': attachment_obj.file_size if attachment_obj else 0 } def create(self, validated_data): course_id = validated_data.pop('course') course = Course.objects.get(pk=course_id) title = validated_data.get('title') file_data = validated_data.get('file') # 1. Create Base Attachment attachment = Attachment.objects.create( title=title, file=file_data ) # 2. Link to Course course_attachment = CourseAttachment.objects.create( course=course, attachment=attachment ) return course_attachment def update(self, instance, validated_data): # We don't change the course in updates, just details of attachment attachment = instance.attachment if attachment: if 'title' in validated_data: attachment.title = validated_data['title'] if 'file' in validated_data: attachment.file = validated_data['file'] # Reset file size to trigger recalculation in save() attachment.file_size = None attachment.save() return instance class AdminCourseGlossarySerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) course = serializers.IntegerField() title = serializers.CharField(max_length=555) description = serializers.CharField() def to_representation(self, instance): glossary_obj = instance.glossary return { 'id': instance.id, 'course': instance.course_id, 'title': glossary_obj.title if glossary_obj else "", 'description': glossary_obj.description if glossary_obj else "" } def create(self, validated_data): course_id = validated_data.pop('course') course = Course.objects.get(pk=course_id) title = validated_data.get('title') description = validated_data.get('description') # 1. Create Base Glossary glossary = Glossary.objects.create( title=title, description=description ) # 2. Link to Course course_glossary = CourseGlossary.objects.create( course=course, glossary=glossary ) return course_glossary def update(self, instance, validated_data): glossary = instance.glossary if glossary: if 'title' in validated_data: glossary.title = validated_data['title'] if 'description' in validated_data: glossary.description = validated_data['description'] glossary.save() return instance