@ -30,9 +30,11 @@ from apps.course.models import (
CourseCategory ,
CourseGlossary ,
CourseLiveSession ,
LiveSessionUser ,
Participant ,
)
from apps.course.doc import *
from apps.course.services.plugnmeet import PlugNMeetClient , PlugNMeetError
from apps.account.serializers import UserProfileSerializer
from utils.exceptions import AppAPIException
from utils.redis import OnlineClassTokenManager
@ -398,6 +400,10 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
course = get_object_or_404 ( queryset , slug = slug )
user = request . user
# TODO: This room activity check should be replaced by webhook integration in the future
# Webhook will automatically notify when room becomes inactive, eliminating polling
self . _sync_room_status_with_plugnmeet ( course )
course_data = CourseDetailSerializer ( course , context = { ' request ' : request } ) . data
user_data = UserProfileSerializer ( user , context = { ' request ' : request } ) . data
metadata = self . _build_metadata (
@ -558,4 +564,59 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
return value
if timezone . is_naive ( value ) :
value = timezone . make_aware ( value , timezone . get_current_timezone ( ) )
return timezone . localtime ( value ) . isoformat ( )
return timezone . localtime ( value ) . isoformat ( )
def _sync_room_status_with_plugnmeet ( self , course : Course ) :
"""
Check if active live session ' s room is still active in PlugNMeet.
If room is inactive , close the session and all related user entries .
TODO : This should be replaced by webhook integration in the future .
PlugNMeet should send webhooks when rooms end , eliminating the need for polling .
"""
active_session = CourseLiveSession . objects . filter (
course = course ,
ended_at__isnull = True
) . first ( )
if not active_session or not active_session . room_id :
return
try :
client = PlugNMeetClient ( )
response = client . is_room_active ( active_session . room_id )
is_active = response . get ( ' isActive ' , False )
if not is_active :
logger . info ( f " [Room Sync] Room inactive in PlugNMeet - room_id={active_session.room_id} session_id={active_session.id} " )
self . _close_live_session ( active_session )
else :
logger . debug ( f " [Room Sync] Room still active - room_id={active_session.room_id} session_id={active_session.id} " )
except ( PlugNMeetError , Exception ) as e :
logger . warning ( f " [Room Sync] Failed to check room status - room_id={active_session.room_id} error={str(e)} " )
@staticmethod
def _close_live_session ( session : CourseLiveSession ) :
"""
Close a live session and all related user entries .
Sets ended_at for session and exited_at / is_online for users .
"""
now = timezone . now ( )
session . ended_at = now
session . save ( update_fields = [ ' ended_at ' , ' updated_at ' ] )
logger . info ( f " [Room Sync] Session closed - session_id={session.id} room_id={session.room_id} ended_at={now} " )
updated_count = LiveSessionUser . objects . filter (
session = session ,
is_online = True ,
exited_at__isnull = True
) . update (
is_online = False ,
exited_at = now ,
updated_at = now
)
if updated_count > 0 :
logger . info ( f " [Room Sync] User sessions closed - session_id={session.id} count={updated_count} " )