From 4125cf497467b083cb67bf6ffa75c5b9e55dc374 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Sat, 6 Jun 2026 17:13:47 +0330 Subject: [PATCH] media file urls enhancements --- apps/course/serializers/admin.py | 70 ++++++++++++++++++++++++++++++-- utils/__init__.py | 31 +++++++++++++- 2 files changed, 96 insertions(+), 5 deletions(-) diff --git a/apps/course/serializers/admin.py b/apps/course/serializers/admin.py index 2feb4f6..468956b 100644 --- a/apps/course/serializers/admin.py +++ b/apps/course/serializers/admin.py @@ -1,4 +1,4 @@ -from rest_framework import serializers +from rest_framework import serializers from utils import FileFieldSerializer, get_thumbs from apps.course.models import ( Course, @@ -12,7 +12,8 @@ from apps.course.models import ( Glossary, CourseLiveSession, LiveSessionUser, - LiveSessionRecording + LiveSessionRecording, + Participant ) from apps.account.models import ProfessorUser from apps.course.models.course import extract_text_from_json, get_localized_field @@ -502,7 +503,7 @@ class AdminLiveSessionListSerializer(serializers.ModelSerializer): class AdminLiveSessionDetailSerializer(serializers.ModelSerializer): course_title = serializers.SerializerMethodField() - user_sessions = AdminLiveSessionUserSerializer(many=True, read_only=True) + user_sessions = serializers.SerializerMethodField() recordings = AdminLiveSessionRecordingSerializer(many=True, read_only=True) recorded_file = FileFieldSerializer(required=False, allow_null=True) @@ -516,3 +517,66 @@ class AdminLiveSessionDetailSerializer(serializers.ModelSerializer): lang = get_request_lang(self) return get_localized_field(lang, obj.course.title) + def get_user_sessions(self, obj): + # 1. Get all active participants of this course + participants = obj.course.participants.filter(is_active=True).select_related('student') + + # 2. Get all live session user records for this session + attendance_map = { + ls_user.user_id: ls_user + for ls_user in obj.user_sessions.all().select_related('user') + } + + # 3. Build the combined list + data = [] + for p in participants: + student = p.student + # Check if this student has an attendance record + attendance = attendance_map.get(student.id) + + if attendance: + data.append({ + 'id': attendance.id, + 'session': obj.id, + 'user': student.id, + 'user_name': student.fullname or student.username, + 'user_email': student.email, + 'role': attendance.role, + 'entered_at': attendance.entered_at.isoformat() if attendance.entered_at else None, + 'exited_at': attendance.exited_at.isoformat() if attendance.exited_at else None, + 'is_online': attendance.is_online, + 'has_attended': True + }) + else: + data.append({ + 'id': None, + 'session': obj.id, + 'user': student.id, + 'user_name': student.fullname or student.username, + 'user_email': student.email, + 'role': 'participant', + 'entered_at': None, + 'exited_at': None, + 'is_online': False, + 'has_attended': False + }) + + # Also append any attendees who are NOT in the participants list (e.g. professor/moderator/observer) + participant_student_ids = {p.student_id for p in participants} + for user_id, attendance in attendance_map.items(): + if user_id not in participant_student_ids: + data.append({ + 'id': attendance.id, + 'session': obj.id, + 'user': user_id, + 'user_name': attendance.user.fullname or attendance.user.username, + 'user_email': attendance.user.email, + 'role': attendance.role, + 'entered_at': attendance.entered_at.isoformat() if attendance.entered_at else None, + 'exited_at': attendance.exited_at.isoformat() if attendance.exited_at else None, + 'is_online': attendance.is_online, + 'has_attended': True + }) + + return data + diff --git a/utils/__init__.py b/utils/__init__.py index 52551eb..80cb882 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -317,8 +317,35 @@ class FileFieldSerializer(serializers.CharField): """ def get_rpath(self, p): - # extract relative path of doc - return p[p.find('/static/') + 7:] + if not p: + return p + if not isinstance(p, str): + return p + + # If /media/ is in the path, extract the relative path after the last /media/ + if "/media/" in p: + idx = p.rfind("/media/") + return p[idx + 7:].lstrip('/') + + # If /static/ is in the path, extract the relative path after the last /static/ + if "/static/" in p: + idx = p.rfind("/static/") + return p[idx + 8:].lstrip('/') + + # If it's a URL but doesn't contain /media/ or /static/ + if "://" in p: + try: + parsed = urlparse(p) + path = parsed.path + if path.startswith('/media/'): + return path[7:].lstrip('/') + elif path.startswith('/static/'): + return path[8:].lstrip('/') + return path.lstrip('/') + except Exception: + pass + + return p.lstrip('/') def to_representation(self, value): request = self.context.get('request', None)