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.
 
 

401 lines
15 KiB

from rest_framework import serializers
# from dj_filer.admin import get_thumbs
from utils import get_thumbs
from apps.course.models import Course, CourseCategory, Attachment, Glossary, LessonCompletion, Participant, Lesson, CourseAttachment, CourseGlossary, CourseLesson
from apps.chat.models import RoomMessage
from apps.account.serializers import UserProfileSerializer
class CourseCategorySerializer(serializers.ModelSerializer):
course_count = serializers.SerializerMethodField()
class Meta:
model = CourseCategory
fields = ['name', 'slug', 'course_count']
def get_course_count(self, obj):
return obj.course_count
class CourseListSerializer(serializers.ModelSerializer):
category = CourseCategorySerializer()
thumbnail = serializers.SerializerMethodField()
participant_count = serializers.SerializerMethodField()
lessons_count = serializers.SerializerMethodField()
price = serializers.SerializerMethodField()
discount_percentage = serializers.SerializerMethodField()
final_price = serializers.SerializerMethodField()
is_free = serializers.SerializerMethodField()
class Meta:
model = Course
fields = [
'id',
'title',
'slug',
'participant_count',
'category',
'thumbnail',
'is_online',
'online_link',
'level',
'duration',
'lessons_count',
'short_description',
'status',
'is_free',
'price',
'discount_percentage',
'final_price',
]
def get_thumbnail(self, obj):
return get_thumbs(obj.thumbnail, self.context.get('request'))
def get_participant_count(self, obj):
return obj.participants.count()
def get_lessons_count(self, obj):
# Use prefetched lessons if available
if hasattr(obj, 'lessons') and obj.lessons.all():
lessons_count = sum(1 for lesson in obj.lessons.all() if lesson.is_active)
return max(lessons_count, obj.lessons_count)
# Fallback to direct query
lessons_count = obj.lessons.filter(is_active=True).count()
return max(lessons_count, obj.lessons_count)
def get_price(self, obj):
if obj.is_free or obj.price == 0:
return "0.00"
return str(obj.price)
def get_discount_percentage(self, obj):
if obj.is_free or obj.price == 0:
return 0
return obj.discount_percentage
def get_final_price(self, obj):
if obj.is_free or obj.price == 0:
return "0.00"
return str(obj.final_price)
def get_is_free(self, obj):
return obj.is_free or obj.price == 0
class CourseDetailSerializer(serializers.ModelSerializer):
category = CourseCategorySerializer()
professor = UserProfileSerializer()
thumbnail = serializers.SerializerMethodField()
participant_count = serializers.SerializerMethodField()
access = serializers.SerializerMethodField()
lessons_complated_count = serializers.SerializerMethodField()
lessons_count = serializers.SerializerMethodField()
last_lesson_id = serializers.SerializerMethodField()
room_id = serializers.SerializerMethodField()
user_transaction_status = serializers.SerializerMethodField()
price = serializers.SerializerMethodField()
discount_percentage = serializers.SerializerMethodField()
final_price = serializers.SerializerMethodField()
is_free = serializers.SerializerMethodField()
class Meta:
model = Course
fields = [
'id',
'title',
'slug',
'category',
'access',
'participant_count',
'professor',
'thumbnail',
'video_type',
'video_file',
'video_link',
'is_online',
'online_link',
'level',
'description',
'duration',
'lessons_count',
'lessons_complated_count',
'short_description',
'status',
'is_free',
'price',
'discount_percentage',
'final_price',
'timing',
'features',
'last_lesson_id',
'room_id',
'user_transaction_status'
]
def get_room_id(self, obj):
# Use prefetched room_messages if available
if hasattr(obj, 'room_messages') and obj.room_messages.all():
return obj.room_messages.first().id
# Fallback to direct query if not prefetched
room_message = RoomMessage.objects.filter(course=obj).first()
if room_message:
return room_message.id
return None
def get_user_transaction_status(self, obj):
from apps.transaction.models import TransactionParticipant
if student := self._get_authenticated_user():
latest_transaction = TransactionParticipant.objects.filter(
user=student,
course=obj,
is_deleted=False
).order_by('-created_at').first()
if latest_transaction:
return latest_transaction.status
return None
def get_last_lesson_id(self, obj):
request = self.context.get('request')
if request and request.user.is_authenticated:
user = request.user
# Use prefetched lessons if available
if hasattr(obj, 'lessons') and obj.lessons.all():
lessons = [lesson for lesson in obj.lessons.all() if lesson.is_active]
completed_lessons = []
# Check which lessons are completed using prefetched data
for lesson in lessons:
if hasattr(lesson, 'completions') and lesson.completions.all():
if any(completion.student_id == user.id for completion in lesson.completions.all()):
completed_lessons.append(lesson)
if completed_lessons:
# Find the last completed lesson by priority
last_completed = max(completed_lessons, key=lambda x: x.priority)
# Find next lesson
next_lessons = [l for l in lessons if l.priority > last_completed.priority]
if next_lessons:
return min(next_lessons, key=lambda x: x.priority).id
# If no completed lessons or no next lesson, return first lesson
if lessons:
return min(lessons, key=lambda x: x.priority).id
# Fallback to direct queries if not prefetched
last_completed_lesson = LessonCompletion.objects.filter(
student=user,
course_lesson__course=obj
).order_by('-completed_at').first()
if last_completed_lesson:
next_lesson = CourseLesson.objects.filter(
course=obj,
priority__gt=last_completed_lesson.course_lesson.priority,
is_active=True
).order_by('priority').first()
if not next_lesson:
next_lesson = CourseLesson.objects.filter(
course=obj,
is_active=True
).order_by('priority').first()
if next_lesson:
return next_lesson.id
return None
def get_access(self, obj):
if student := self._get_authenticated_user():
if not self._is_participant(student, obj):
return False
return True
return False
def get_is_professor(self, obj):
if professor := self._get_authenticated_user():
return obj.professor == professor
return False
def get_lessons_count(self, obj):
# Use prefetched lessons if available
if hasattr(obj, 'lessons') and obj.lessons.all():
lessons_count = sum(1 for lesson in obj.lessons.all() if lesson.is_active)
return max(lessons_count, obj.lessons_count)
# Fallback to direct query
lessons_count = obj.lessons.filter(is_active=True).count()
return max(lessons_count, obj.lessons_count)
def get_lessons_complated_count(self, obj):
if student := self._get_authenticated_user():
if not self._is_participant(student, obj):
return None
completed_count = self._get_completed_lessons_count(student, obj)
# Ensure completed count doesn't exceed total lessons count
total_lessons = self.get_lessons_count(obj)
return min(completed_count, total_lessons)
return None
def _is_participant(self, student, course):
"""Helper method to check if a student is a participant in the given course."""
return Participant.objects.filter(student=student, course=course).exists()
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_completed_lessons_count(self, student, course):
"""Helper method to count completed lessons for the student in the given course."""
# Use prefetched completions if available
if hasattr(course, 'lessons') and course.lessons.all():
completed_count = 0
for lesson in course.lessons.all():
if hasattr(lesson, 'completions') and lesson.completions.all():
if any(completion.student_id == student.id for completion in lesson.completions.all()):
completed_count += 1
return completed_count
# Fallback to direct query if not prefetched
return LessonCompletion.objects.filter(
student=student,
course_lesson__course=course
).count()
def get_thumbnail(self, obj):
return get_thumbs(obj.thumbnail, self.context.get('request'))
def get_participant_count(self, obj):
# Use prefetched participants if available
if hasattr(obj, 'participants') and obj.participants.all():
return len(obj.participants.all())
# Fallback to direct query
return obj.participants.count()
def get_price(self, obj):
if obj.is_free or obj.price == 0:
return "0.00"
return str(obj.price)
def get_discount_percentage(self, obj):
if obj.is_free or obj.price == 0:
return 0
return obj.discount_percentage
def get_final_price(self, obj):
if obj.is_free or obj.price == 0:
return "0.00"
return str(obj.final_price)
def get_is_free(self, obj):
return obj.is_free or obj.price == 0
class MyCourseListSerializer(serializers.ModelSerializer):
category = CourseCategorySerializer()
thumbnail = serializers.SerializerMethodField()
lessons_count = serializers.SerializerMethodField()
lessons_complated_count = serializers.SerializerMethodField()
class Meta:
model = Course
fields = [
'id',
'title',
'slug',
'category',
'thumbnail',
'lessons_count',
'lessons_complated_count',
'short_description',
'status',
]
def get_thumbnail(self, obj):
return get_thumbs(obj.thumbnail, self.context.get('request'))
def get_lessons_count(self, obj):
"""Get the actual count of active lessons"""
# Use prefetched lessons if available
if hasattr(obj, 'lessons') and obj.lessons.all():
lessons_count = sum(1 for lesson in obj.lessons.all() if lesson.is_active)
return max(lessons_count, obj.lessons_count)
# Fallback to direct query
lessons_count = obj.lessons.filter(is_active=True).count()
return max(lessons_count, obj.lessons_count)
def get_lessons_complated_count(self, obj):
if student := self._get_authenticated_user():
if not self._is_participant(student, obj):
return None
completed_count = self._get_completed_lessons_count(student, obj)
# Ensure completed count doesn't exceed total lessons count
total_lessons = self.get_lessons_count(obj)
return min(completed_count, total_lessons)
return None
def _is_participant(self, student, course):
"""Helper method to check if a student is a participant in the given course."""
# اگر کاربر استاد دوره است، دسترسی کامل دارد
if course.professor == student:
return True
# در غیر این صورت چک می‌کنیم که آیا participant است یا خیر
return Participant.objects.filter(student=student, course=course).exists()
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_completed_lessons_count(self, student, course):
"""Helper method to count completed lessons for the student in the given course."""
# Use prefetched completions if available
if hasattr(course, 'lessons') and course.lessons.all():
completed_count = 0
for lesson in course.lessons.all():
if hasattr(lesson, 'completions') and lesson.completions.all():
if any(completion.student_id == student.id for completion in lesson.completions.all()):
completed_count += 1
return completed_count
# Fallback to direct query if not prefetched
return LessonCompletion.objects.filter(
student=student,
course_lesson__course=course
).count()
class AttachmentSerializer(serializers.ModelSerializer):
class Meta:
model = Attachment
fields = ['id', 'title', 'file', 'file_size']
class CourseAttachmentSerializer(serializers.ModelSerializer):
title = serializers.CharField(source='attachment.title', read_only=True)
file = serializers.FileField(source='attachment.file', read_only=True)
file_size = serializers.IntegerField(source='attachment.file_size', read_only=True)
class Meta:
model = CourseAttachment
fields = ['id', 'title', 'file', 'file_size']
class GlossarySerializer(serializers.ModelSerializer):
class Meta:
model = Glossary
fields = ['id', 'title', 'description']
class CourseGlossarySerializer(serializers.ModelSerializer):
title = serializers.CharField(source='glossary.title', read_only=True)
description = serializers.CharField(source='glossary.description', read_only=True)
class Meta:
model = CourseGlossary
fields = ['id', 'title', 'description']