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.
199 lines
7.5 KiB
199 lines
7.5 KiB
import logging
|
|
from django.utils import timezone
|
|
from apps.course.models import Course
|
|
from apps.chat.models import RoomMessage
|
|
from apps.course.models.course import Course, extract_text_from_json
|
|
from apps.course.models.lesson import CourseLesson
|
|
from apps.course.models.live_session import LiveSessionRecording
|
|
from django.db.models import Q
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
from django.db.models.signals import post_save, post_delete, pre_save
|
|
from django.dispatch import receiver
|
|
from django.core.cache import cache
|
|
from django.contrib.auth import get_user_model
|
|
|
|
UserModel = get_user_model()
|
|
|
|
|
|
@receiver(post_save, sender=Course)
|
|
def handle_room_message_for_course(sender, instance, created, **kwargs):
|
|
title_str = extract_text_from_json(instance.title)
|
|
if created: # فقط برای موارد جدید اجرا شود
|
|
RoomMessage.objects.create(
|
|
name=f"{title_str} - Group",
|
|
description=f"Group chat for course: {title_str}",
|
|
initiator=instance.professor, # استاد بهعنوان سازنده اتاق
|
|
course=instance,
|
|
room_type=RoomMessage.RoomTypeChoices.GROUP
|
|
)
|
|
else: # این بخش در زمان آپدیت دوره اجرا میشود
|
|
# Find the existing group room for this course and update its details
|
|
RoomMessage.objects.filter(
|
|
course=instance,
|
|
room_type=RoomMessage.RoomTypeChoices.GROUP
|
|
).update(
|
|
name=f"{title_str} - Group",
|
|
description=f"Group chat for course: {title_str}",
|
|
initiator=instance.professor
|
|
)
|
|
|
|
|
|
@receiver(post_save, sender=Course)
|
|
def ensure_professor_role(sender, instance, **kwargs):
|
|
professor = getattr(instance, 'professor', None)
|
|
if professor:
|
|
professor.ensure_professor_profile()
|
|
|
|
@receiver([post_save, post_delete], sender=Course)
|
|
def invalidate_professor_course_cache(sender, instance, **kwargs):
|
|
"""
|
|
Clears the cached professor detail page AND their course list
|
|
whenever a course assigned to them is created, updated, or deleted.
|
|
"""
|
|
if instance.professor:
|
|
detail_cache_key = f"professor_detail_{instance.professor.slug}"
|
|
|
|
cache.delete(detail_cache_key)
|
|
|
|
# Optional: If you update a Professor's profile in the admin directly
|
|
@receiver([post_save, post_delete], sender=UserModel)
|
|
def invalidate_professor_profile_cache(sender, instance, **kwargs):
|
|
if instance.user_type == UserModel.UserType.PROFESSOR:
|
|
cache_key = f"professor_detail_{instance.slug}"
|
|
cache.delete(cache_key)
|
|
|
|
@receiver(post_save, sender=Course)
|
|
def sync_course_chat_locks(sender, instance, **kwargs):
|
|
"""
|
|
Automatically locks/unlocks the related chat rooms when the admin
|
|
toggles the chat locks on the Course page.
|
|
"""
|
|
# 1. Update the Group Chat
|
|
RoomMessage.objects.filter(
|
|
course=instance,
|
|
room_type=RoomMessage.RoomTypeChoices.GROUP
|
|
).update(is_locked=instance.is_group_chat_locked)
|
|
|
|
# 2. Update the Private Chats between the Professor and Students of this course
|
|
# Get all student IDs enrolled in this course
|
|
student_ids = instance.participants.values_list('student_id', flat=True)
|
|
|
|
if student_ids:
|
|
RoomMessage.objects.filter(
|
|
room_type=RoomMessage.RoomTypeChoices.PRIVATE
|
|
).filter(
|
|
# Find rooms where initiator is prof and recipient is student, OR vice versa
|
|
Q(initiator_id=instance.professor_id, recipient_id__in=student_ids) |
|
|
Q(initiator_id__in=student_ids, recipient_id=instance.professor_id)
|
|
).update(is_locked=instance.is_professor_chat_locked)
|
|
|
|
|
|
def _recalculate_course_lessons_count(course_id):
|
|
Course.recalculate_lessons_count_for_course(course_id)
|
|
|
|
|
|
@receiver(pre_save, sender=CourseLesson)
|
|
def capture_previous_course_lesson_state(sender, instance, **kwargs):
|
|
if not instance.pk:
|
|
instance._previous_course_id = None
|
|
instance._previous_is_active = None
|
|
return
|
|
|
|
previous = sender.objects.filter(pk=instance.pk).values('course_id', 'is_active').first()
|
|
if previous:
|
|
instance._previous_course_id = previous['course_id']
|
|
instance._previous_is_active = previous['is_active']
|
|
else:
|
|
instance._previous_course_id = None
|
|
instance._previous_is_active = None
|
|
|
|
|
|
@receiver(post_save, sender=CourseLesson)
|
|
def sync_lessons_count_on_course_lesson_save(sender, instance, **kwargs):
|
|
affected_course_ids = {instance.course_id}
|
|
|
|
previous_course_id = getattr(instance, '_previous_course_id', None)
|
|
previous_is_active = getattr(instance, '_previous_is_active', None)
|
|
if previous_course_id and previous_course_id != instance.course_id:
|
|
affected_course_ids.add(previous_course_id)
|
|
|
|
if previous_is_active is not None and previous_is_active != instance.is_active and previous_course_id:
|
|
affected_course_ids.add(previous_course_id)
|
|
|
|
for course_id in affected_course_ids:
|
|
_recalculate_course_lessons_count(course_id)
|
|
|
|
|
|
@receiver(post_delete, sender=CourseLesson)
|
|
def sync_lessons_count_on_course_lesson_delete(sender, instance, **kwargs):
|
|
_recalculate_course_lessons_count(instance.course_id)
|
|
|
|
|
|
@receiver(post_save, sender=LiveSessionRecording)
|
|
def create_course_lesson_from_recording(sender, instance, **kwargs):
|
|
"""
|
|
Automatically creates a CourseChapter (if not exists) and CourseLesson
|
|
whenever a LiveSessionRecording is saved with a valid file.
|
|
"""
|
|
if not instance.file:
|
|
return
|
|
|
|
# To avoid duplicate creation if save is called multiple times
|
|
from apps.course.models.lesson import Lesson, CourseChapter, CourseLesson
|
|
if Lesson.objects.filter(content_file=instance.file.name).exists():
|
|
return
|
|
|
|
session = instance.session
|
|
course = session.course
|
|
|
|
# 1. Determine chapter title as recording date (YYYY-MM-DD)
|
|
date_val = instance.created_at or timezone.now()
|
|
recording_date_str = date_val.strftime('%Y-%m-%d')
|
|
|
|
# 2. Get or create CourseChapter for this date
|
|
from apps.course.models.course import extract_text_from_json
|
|
chapter = None
|
|
for ch in CourseChapter.objects.filter(course=course):
|
|
if extract_text_from_json(ch.title) == recording_date_str:
|
|
chapter = ch
|
|
break
|
|
|
|
if not chapter:
|
|
chapter = CourseChapter.objects.create(
|
|
course=course,
|
|
title=recording_date_str,
|
|
is_active=True
|
|
)
|
|
|
|
# 3. Calculate next part number based on total lessons in the course
|
|
lessons_count = CourseLesson.objects.filter(course=course).count()
|
|
part_num = lessons_count + 1
|
|
lesson_title = f"Part {part_num}"
|
|
|
|
# 4. Calculate duration in minutes
|
|
duration_minutes = 0
|
|
if instance.file_time:
|
|
duration_minutes = int(instance.file_time.total_seconds() / 60)
|
|
if duration_minutes == 0 and instance.file_time.total_seconds() > 0:
|
|
duration_minutes = 1
|
|
|
|
# 5. Create the Lesson object
|
|
lesson = Lesson.objects.create(
|
|
title=lesson_title,
|
|
content_type="video_file",
|
|
content_file=instance.file,
|
|
duration=duration_minutes
|
|
)
|
|
|
|
# 6. Create the CourseLesson object linking it to the chapter
|
|
course_lesson = CourseLesson.objects.create(
|
|
course=course,
|
|
chapter=chapter,
|
|
lesson=lesson,
|
|
title=lesson_title,
|
|
is_active=True
|
|
)
|
|
|
|
logger.info(f"✨ [Signal] Automatically created CourseLesson {course_lesson.id} ({lesson_title}) for course {course.id} from recording {instance.id}")
|