diff --git a/apps/course/signals.py b/apps/course/signals.py index ad033fc..b37e686 100644 --- a/apps/course/signals.py +++ b/apps/course/signals.py @@ -167,8 +167,8 @@ def create_course_lesson_from_recording(sender, instance, **kwargs): is_active=True ) - # 3. Calculate next part number based on total lessons in the course - lessons_count = CourseLesson.objects.filter(course=course).count() + # 3. Calculate next part number based on total lessons in the chapter + lessons_count = CourseLesson.objects.filter(chapter=chapter).count() part_num = lessons_count + 1 lesson_title = f"Part {part_num}" diff --git a/apps/course/views/live_session.py b/apps/course/views/live_session.py index 8b743d0..c6aff88 100644 --- a/apps/course/views/live_session.py +++ b/apps/course/views/live_session.py @@ -13,7 +13,7 @@ from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi import time import jwt -from apps.course.models import Course, CourseLiveSession, Participant, LiveSessionRecording +from apps.course.models import Course, CourseLiveSession, Participant, LiveSessionRecording, LiveSessionUser from apps.course.serializers import LiveSessionRoomCreateSerializer, LiveSessionTokenSerializer, LiveSessionRecordedFileSerializer, LiveSessionRecordingSerializer from apps.course.services.plugnmeet import PlugNMeetClient, PlugNMeetError from utils.exceptions import AppAPIException @@ -426,6 +426,23 @@ class CourseLiveSessionTokenAPIView(GenericAPIView): client = PlugNMeetClient() plugnmeet_response = client.get_join_token(payload) logger.info(f"[LiveSession Token] Token generated successfully - room_id={room_id} user_id={user.id}") + + # Fail-safe participant registration in database + try: + role = 'moderator' if is_admin else 'participant' + LiveSessionUser.objects.update_or_create( + session=session, + user=user, + defaults={ + 'role': role, + 'is_online': True, + 'exited_at': None, + 'entered_at': timezone.now() + } + ) + logger.info(f"[LiveSession Token] Fail-safe registered user entry - session_id={session.id} user_id={user.id}") + except Exception as e: + logger.error(f"[LiveSession Token] Failed to register fail-safe user entry: {e}") except ImproperlyConfigured as exc: logger.error(f"[LiveSession Token] Configuration error - {str(exc)}") raise AppAPIException({'message': str(exc)}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) @@ -540,6 +557,17 @@ class CourseLiveSessionTokenAPIView(GenericAPIView): now = timezone.now() session.ended_at = now session.save(update_fields=['ended_at', 'updated_at']) + + # Also set all users offline + try: + updated_count = LiveSessionUser.objects.filter( + session=session, + is_online=True + ).update(is_online=False, exited_at=now, updated_at=now) + logger.info(f"[Room Verify] Set {updated_count} users offline for session {session.id}") + except Exception as e: + logger.error(f"[Room Verify] Failed to set users offline: {e}") + logger.info(f"[Room Verify] Session auto-closed - session_id={session.id} room_id={session.room_id}") return False @@ -552,6 +580,17 @@ class CourseLiveSessionTokenAPIView(GenericAPIView): now = timezone.now() session.ended_at = now session.save(update_fields=['ended_at', 'updated_at']) + + # Also set all users offline + try: + updated_count = LiveSessionUser.objects.filter( + session=session, + is_online=True + ).update(is_online=False, exited_at=now, updated_at=now) + logger.info(f"[Room Verify] Set {updated_count} users offline (room not found) for session {session.id}") + except Exception as ex: + logger.error(f"[Room Verify] Failed to set users offline: {ex}") + logger.warning(f"[Room Verify] Room not found - session closed - session_id={session.id}") return False