You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

425 lines
16 KiB

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,
CourseLiveSession,
LiveSessionUser,
LiveSessionRecording
)
from apps.account.models import ProfessorUser
class AdminLessonSerializer(serializers.ModelSerializer):
class Meta:
model = Lesson
fields = '__all__'
class AdminGlossarySerializer(serializers.ModelSerializer):
class Meta:
model = Glossary
fields = '__all__'
class AdminAttachmentSerializer(serializers.ModelSerializer):
class Meta:
model = Attachment
fields = '__all__'
def serialize_file(value, serializer):
if not value:
return None
field = FileFieldSerializer()
field.bind('', serializer)
return field.to_representation(value)
class AdminCourseCategorySerializer(serializers.ModelSerializer):
slug = serializers.SlugField(required=False, allow_blank=True)
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()
lesson = serializers.IntegerField(required=False, write_only=True, allow_null=True)
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, required=False, allow_null=True)
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, required=False, default=0)
class Meta:
ref_name = 'CourseAdminCourseLesson'
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,
'lesson': instance.lesson_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)
lesson_id = validated_data.get('lesson')
if lesson_id:
try:
lesson = Lesson.objects.get(pk=lesson_id)
except Lesson.DoesNotExist:
raise serializers.ValidationError({"lesson": "Lesson does not exist."})
# Check if already linked to this course
if CourseLesson.objects.filter(course=course, lesson=lesson).exists():
raise serializers.ValidationError({"detail": "This lesson is already linked to this course."})
else:
content_type = validated_data.get('content_type')
if not content_type:
raise serializers.ValidationError({"detail": "Content type is required for new lessons."})
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()
attachment = serializers.IntegerField(required=False, write_only=True, allow_null=True)
title = serializers.CharField(max_length=255, required=False, allow_blank=True, allow_null=True)
file = FileFieldSerializer(required=False, allow_null=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,
'attachment': instance.attachment_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)
attachment_id = validated_data.get('attachment')
if attachment_id:
try:
attachment = Attachment.objects.get(pk=attachment_id)
except Attachment.DoesNotExist:
raise serializers.ValidationError({"attachment": "Attachment does not exist."})
# Check if already linked
if CourseAttachment.objects.filter(course=course, attachment=attachment).exists():
raise serializers.ValidationError({"detail": "This attachment is already linked to this course."})
else:
title = validated_data.get('title')
file_data = validated_data.get('file')
if not title or not file_data:
raise serializers.ValidationError({"detail": "Title and file are required for new attachments."})
# 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()
glossary = serializers.IntegerField(required=False, write_only=True, allow_null=True)
title = serializers.CharField(max_length=555, required=False, allow_blank=True, allow_null=True)
description = serializers.CharField(required=False, allow_blank=True, allow_null=True)
def to_representation(self, instance):
glossary_obj = instance.glossary
return {
'id': instance.id,
'course': instance.course_id,
'glossary': instance.glossary_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)
glossary_id = validated_data.get('glossary')
if glossary_id:
try:
glossary = Glossary.objects.get(pk=glossary_id)
except Glossary.DoesNotExist:
raise serializers.ValidationError({"glossary": "Glossary term does not exist."})
# Check if already linked
if CourseGlossary.objects.filter(course=course, glossary=glossary).exists():
raise serializers.ValidationError({"detail": "This glossary term is already linked to this course."})
else:
title = validated_data.get('title')
description = validated_data.get('description')
if not title or not description:
raise serializers.ValidationError({"detail": "Title and description are required for new glossary terms."})
# 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
class AdminLiveSessionUserSerializer(serializers.ModelSerializer):
user_name = serializers.CharField(source='user.fullname', read_only=True)
user_email = serializers.CharField(source='user.email', read_only=True)
class Meta:
model = LiveSessionUser
fields = ['id', 'session', 'user', 'user_name', 'user_email', 'role', 'entered_at', 'exited_at', 'is_online']
class AdminLiveSessionRecordingSerializer(serializers.ModelSerializer):
file = FileFieldSerializer(required=False, allow_null=True)
thumbnail = FileFieldSerializer(required=False, allow_null=True)
class Meta:
model = LiveSessionRecording
fields = ['id', 'session', 'title', 'file', 'file_time', 'recording_type', 'thumbnail', 'is_active', 'created_at']
class AdminLiveSessionListSerializer(serializers.ModelSerializer):
course_title = serializers.CharField(source='course.title', read_only=True)
participant_count = serializers.SerializerMethodField()
class Meta:
model = CourseLiveSession
fields = ['id', 'course', 'course_title', 'room_id', 'subject', 'started_at', 'ended_at', 'participant_count', 'created_at']
def get_participant_count(self, obj):
return obj.user_sessions.count()
class AdminLiveSessionDetailSerializer(serializers.ModelSerializer):
course_title = serializers.CharField(source='course.title', read_only=True)
user_sessions = AdminLiveSessionUserSerializer(many=True, read_only=True)
recordings = AdminLiveSessionRecordingSerializer(many=True, read_only=True)
recorded_file = FileFieldSerializer(required=False, allow_null=True)
class Meta:
model = CourseLiveSession
fields = ['id', 'course', 'course_title', 'room_id', 'subject', 'started_at', 'ended_at', 'recorded_file', 'user_sessions', 'recordings', 'created_at', 'updated_at']