Browse Source

admin panel course multilanguage fixed and added

master
Mohsen Taba 2 months ago
parent
commit
d234d98950
  1. 13
      apps/api/views/admin_dashboard.py
  2. 10
      apps/certificate/serializers.py
  3. 106
      apps/course/serializers/admin.py
  4. 3
      apps/course/views/course.py
  5. 2
      apps/course/views/professor.py
  6. 37
      apps/quiz/serializers/admin.py
  7. 12
      apps/transaction/serializers.py

13
apps/api/views/admin_dashboard.py

@ -8,6 +8,7 @@ from django.db.models.functions import TruncDate
from apps.account.models import User, LoginHistory
from apps.course.models import Course, Participant, CourseLiveSession
from apps.course.models.course import extract_text_from_json
from apps.quiz.models import QuizParticipant
from apps.blog.models import Blog
from apps.chat.models import ChatMessage, RoomMessage
@ -31,7 +32,7 @@ class AdminDashboardStatsView(APIView):
is_active=True
).count()
professors = User.objects.filter(user_type=User.UserType.PROFESSOR, is_active=True).count()
active_courses = Course.objects.filter(status=Course.StatusChoices.ONGOING).count()
active_courses = Course.objects.filter(status__contains=[{"title": Course.StatusChoices.ONGOING}]).count()
total_blogs = Blog.objects.count()
pending_certificates = Certificate.objects.filter(status='pending').count()
active_live_sessions = CourseLiveSession.objects.filter(ended_at__isnull=True).count()
@ -84,10 +85,9 @@ class AdminDashboardStatsView(APIView):
chat_chart = [{"date": item['date'].strftime('%Y-%m-%d'), "messages": item['count']} for item in chat_volume]
# Course Pipeline (Stacked Bar)
course_statuses = Course.objects.values('status').annotate(count=Count('id'))
course_pipeline = {"name": "Courses"}
for status in course_statuses:
course_pipeline[status['status']] = status['count']
for choice in Course.StatusChoices.values:
course_pipeline[choice] = Course.objects.filter(status__contains=[{"title": choice}]).count()
# Course Efficacy: Completed vs Enrolled (Bar Chart)
# We take the top 5 courses, check how many users are enrolled, and how many have at least 1 completed lesson
@ -99,8 +99,9 @@ class AdminDashboardStatsView(APIView):
course=course,
student__lesson_completions__course_lesson__course=course
).distinct().count()
title_text = extract_text_from_json(course.title)
course_efficacy.append({
"title": course.title[:20] + "..." if len(course.title) > 20 else course.title,
"title": title_text[:20] + "..." if len(title_text) > 20 else title_text,
"enrolled": enrolled,
"completed": completed
})
@ -113,7 +114,7 @@ class AdminDashboardStatsView(APIView):
top_courses_list = [
{
"id": c.id,
"title": c.title,
"title": extract_text_from_json(c.title),
"professor": c.professor.fullname if c.professor else "Unknown",
"participants": c.participant_count
} for c in top_courses_qs

10
apps/certificate/serializers.py

@ -50,7 +50,7 @@ class CertificateRequestSerializer(serializers.ModelSerializer):
class AdminCertificateSerializer(serializers.ModelSerializer):
student_name = serializers.CharField(source='student.fullname', read_only=True)
student_email = serializers.CharField(source='student.email', read_only=True)
course_title = serializers.CharField(source='course.title', read_only=True)
course_title = serializers.SerializerMethodField()
class Meta:
model = Certificate
@ -60,6 +60,14 @@ class AdminCertificateSerializer(serializers.ModelSerializer):
'created_at', 'updated_at'
]
def get_course_title(self, obj):
if not obj.course:
return None
request = self.context.get('request')
lang = getattr(request, 'LANGUAGE_CODE', None) or 'en'
from apps.course.models.course import get_localized_field
return get_localized_field(lang, obj.course.title)

106
apps/course/serializers/admin.py

@ -15,23 +15,46 @@ from apps.course.models import (
LiveSessionRecording
)
from apps.account.models import ProfessorUser
from apps.course.models.course import extract_text_from_json
from apps.course.models.course import extract_text_from_json, get_localized_field
def get_request_lang(serializer_instance):
request = serializer_instance.context.get('request')
return getattr(request, 'LANGUAGE_CODE', None) or 'en'
class AdminLessonSerializer(serializers.ModelSerializer):
class Meta:
model = Lesson
fields = '__all__'
def to_representation(self, instance):
ret = super().to_representation(instance)
lang = get_request_lang(self)
ret['title'] = get_localized_field(lang, instance.title)
ret['content_type'] = get_localized_field(lang, instance.content_type)
return ret
class AdminGlossarySerializer(serializers.ModelSerializer):
class Meta:
model = Glossary
fields = '__all__'
def to_representation(self, instance):
ret = super().to_representation(instance)
lang = get_request_lang(self)
ret['description'] = get_localized_field(lang, instance.description)
return ret
class AdminAttachmentSerializer(serializers.ModelSerializer):
class Meta:
model = Attachment
fields = '__all__'
def to_representation(self, instance):
ret = super().to_representation(instance)
lang = get_request_lang(self)
ret['title'] = get_localized_field(lang, instance.title)
return ret
def serialize_file(value, serializer):
if not value:
@ -47,6 +70,13 @@ class AdminCourseCategorySerializer(serializers.ModelSerializer):
model = CourseCategory
fields = ['id', 'name', 'slug']
def to_representation(self, instance):
ret = super().to_representation(instance)
lang = get_request_lang(self)
ret['name'] = get_localized_field(lang, instance.name)
ret['slug'] = get_localized_field(lang, instance.slug)
return ret
class AdminCourseListSerializer(serializers.ModelSerializer):
category_name = serializers.SerializerMethodField()
professor_name = serializers.CharField(source='professor.fullname', read_only=True)
@ -84,7 +114,19 @@ class AdminCourseListSerializer(serializers.ModelSerializer):
return get_thumbs(obj.thumbnail, self.context.get('request'))
def get_category_name(self, obj):
return extract_text_from_json(obj.category.name) if obj.category else ""
if not obj.category:
return ""
lang = get_request_lang(self)
return get_localized_field(lang, obj.category.name)
def to_representation(self, instance):
ret = super().to_representation(instance)
lang = get_request_lang(self)
ret['title'] = get_localized_field(lang, instance.title)
ret['slug'] = get_localized_field(lang, instance.slug)
ret['level'] = get_localized_field(lang, instance.level)
ret['status'] = get_localized_field(lang, instance.status)
return ret
class AdminCourseDetailSerializer(serializers.ModelSerializer):
@ -135,7 +177,21 @@ class AdminCourseDetailSerializer(serializers.ModelSerializer):
return obj.participants.count()
def get_category_name(self, obj):
return extract_text_from_json(obj.category.name) if obj.category else ""
if not obj.category:
return ""
lang = get_request_lang(self)
return get_localized_field(lang, obj.category.name)
def to_representation(self, instance):
ret = super().to_representation(instance)
lang = get_request_lang(self)
ret['title'] = get_localized_field(lang, instance.title)
ret['slug'] = get_localized_field(lang, instance.slug)
ret['level'] = get_localized_field(lang, instance.level)
ret['status'] = get_localized_field(lang, instance.status)
ret['timing'] = get_localized_field(lang, instance.timing)
ret['features'] = get_localized_field(lang, instance.features)
return ret
class AdminCourseChapterSerializer(serializers.ModelSerializer):
@ -144,6 +200,12 @@ class AdminCourseChapterSerializer(serializers.ModelSerializer):
fields = ['id', 'course', 'title', 'priority', 'is_active', 'created_at']
read_only_fields = ['id', 'created_at']
def to_representation(self, instance):
ret = super().to_representation(instance)
lang = get_request_lang(self)
ret['title'] = get_localized_field(lang, instance.title)
return ret
class AdminCourseLessonSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
@ -168,15 +230,23 @@ class AdminCourseLessonSerializer(serializers.Serializer):
Map a CourseLesson instance to representation fields.
"""
lesson_obj = instance.lesson
lang = get_request_lang(self)
raw_title = instance.title or (lesson_obj.title if lesson_obj else [])
localized_title = get_localized_field(lang, raw_title)
raw_content_type = lesson_obj.content_type if lesson_obj else []
localized_content_type = get_localized_field(lang, raw_content_type)
return {
'id': instance.id,
'course': instance.course_id,
'chapter': instance.chapter_id,
'lesson': instance.lesson_id,
'title': instance.title or (lesson_obj.title if lesson_obj else []),
'title': localized_title,
'priority': instance.priority,
'is_active': instance.is_active,
'content_type': lesson_obj.content_type if lesson_obj else [],
'content_type': localized_content_type,
'content_file': serialize_file(lesson_obj.content_file, self) if lesson_obj and lesson_obj.content_file else None,
'video_link': lesson_obj.video_link if lesson_obj else "",
'duration': lesson_obj.duration if lesson_obj else 0,
@ -273,11 +343,14 @@ class AdminCourseAttachmentSerializer(serializers.Serializer):
def to_representation(self, instance):
attachment_obj = instance.attachment
lang = get_request_lang(self)
raw_title = attachment_obj.title if attachment_obj else []
localized_title = get_localized_field(lang, raw_title)
return {
'id': instance.id,
'course': instance.course_id,
'attachment': instance.attachment_id,
'title': attachment_obj.title if attachment_obj else [],
'title': localized_title,
'file': serialize_file(attachment_obj.file, self) if attachment_obj and attachment_obj.file else None,
'file_size': attachment_obj.file_size if attachment_obj else 0
}
@ -339,12 +412,15 @@ class AdminCourseGlossarySerializer(serializers.Serializer):
def to_representation(self, instance):
glossary_obj = instance.glossary
lang = get_request_lang(self)
raw_description = glossary_obj.description if glossary_obj else []
localized_description = get_localized_field(lang, raw_description)
return {
'id': instance.id,
'course': instance.course_id,
'glossary': instance.glossary_id,
'title': glossary_obj.title if glossary_obj else "",
'description': glossary_obj.description if glossary_obj else []
'description': localized_description
}
def create(self, validated_data):
@ -411,19 +487,25 @@ class AdminLiveSessionRecordingSerializer(serializers.ModelSerializer):
class AdminLiveSessionListSerializer(serializers.ModelSerializer):
course_title = serializers.CharField(source='course.title', read_only=True)
course_title = serializers.SerializerMethodField()
participant_count = serializers.SerializerMethodField()
class Meta:
model = CourseLiveSession
fields = ['id', 'course', 'course_title', 'room_id', 'subject', 'started_at', 'ended_at', 'participant_count', 'created_at']
def get_course_title(self, obj):
if not obj.course:
return ""
lang = get_request_lang(self)
return get_localized_field(lang, obj.course.title)
def get_participant_count(self, obj):
return obj.user_sessions.count()
class AdminLiveSessionDetailSerializer(serializers.ModelSerializer):
course_title = serializers.CharField(source='course.title', read_only=True)
course_title = serializers.SerializerMethodField()
user_sessions = AdminLiveSessionUserSerializer(many=True, read_only=True)
recordings = AdminLiveSessionRecordingSerializer(many=True, read_only=True)
recorded_file = FileFieldSerializer(required=False, allow_null=True)
@ -431,3 +513,9 @@ class AdminLiveSessionDetailSerializer(serializers.ModelSerializer):
class Meta:
model = CourseLiveSession
fields = ['id', 'course', 'course_title', 'room_id', 'subject', 'started_at', 'ended_at', 'recorded_file', 'user_sessions', 'recordings', 'created_at', 'updated_at']
def get_course_title(self, obj):
if not obj.course:
return ""
lang = get_request_lang(self)
return get_localized_field(lang, obj.course.title)

3
apps/course/views/course.py

@ -37,6 +37,7 @@ from apps.course.models import (
LiveSessionUser,
Participant,
)
from apps.course.models.course import extract_text_from_json
from apps.course.doc import *
from apps.course.services.plugnmeet import PlugNMeetClient, PlugNMeetError
from apps.account.serializers import UserProfileSerializer
@ -618,7 +619,7 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
}, status=status.HTTP_200_OK)
def _build_metadata(self, course: Course, payload: dict, user=None) -> dict:
status_value = course.status
status_value = extract_text_from_json(course.status)
has_started = status_value in [Course.StatusChoices.ONGOING, Course.StatusChoices.FINISHED]
timing_data = course.timing if isinstance(course.timing, dict) else {}

2
apps/course/views/professor.py

@ -202,4 +202,4 @@ class ProfessorCourseListAPIView(ListAPIView):
'lessons__lesson',
'lessons__completions',
'participants__student',
).exclude(status=Course.StatusChoices.INACTIVE).filter(professor=professor)
).exclude(status__contains=[{"title": Course.StatusChoices.INACTIVE}]).filter(professor=professor)

37
apps/quiz/serializers/admin.py

@ -2,6 +2,11 @@ from rest_framework import serializers
from apps.quiz.models import Quiz, Question, QuizParticipant, ParticipantAnswer
from apps.course.models import Course, CourseLesson
from apps.account.models import User
from apps.course.models.course import get_localized_field
def get_request_lang(serializer_instance):
request = serializer_instance.context.get('request')
return getattr(request, 'LANGUAGE_CODE', None) or 'en'
class AdminCourseSerializer(serializers.ModelSerializer):
@ -9,6 +14,12 @@ class AdminCourseSerializer(serializers.ModelSerializer):
model = Course
fields = ['id', 'title']
def to_representation(self, instance):
ret = super().to_representation(instance)
lang = get_request_lang(self)
ret['title'] = get_localized_field(lang, instance.title)
return ret
class AdminCourseLessonSerializer(serializers.ModelSerializer):
title = serializers.SerializerMethodField()
@ -19,11 +30,9 @@ class AdminCourseLessonSerializer(serializers.ModelSerializer):
ref_name = 'QuizAdminCourseLesson'
def get_title(self, obj):
if obj.title:
return obj.title
if obj.lesson:
return obj.lesson.title
return f"Lesson {obj.id}"
lang = get_request_lang(self)
raw_title = obj.title or (obj.lesson.title if obj.lesson else [])
return get_localized_field(lang, raw_title)
class AdminQuizListSerializer(serializers.ModelSerializer):
@ -55,12 +64,17 @@ class AdminQuizListSerializer(serializers.ModelSerializer):
return obj.participants.count()
def get_course_title(self, obj):
return obj.course.title if obj.course else None
if not obj.course:
return None
lang = get_request_lang(self)
return get_localized_field(lang, obj.course.title)
def get_lesson_title(self, obj):
if not obj.lesson:
return None
return obj.lesson.title or (obj.lesson.lesson.title if obj.lesson.lesson else None)
lang = get_request_lang(self)
raw_title = obj.lesson.title or (obj.lesson.lesson.title if obj.lesson.lesson else None)
return get_localized_field(lang, raw_title)
class AdminQuizDetailSerializer(serializers.ModelSerializer):
@ -93,12 +107,17 @@ class AdminQuizDetailSerializer(serializers.ModelSerializer):
return obj.participants.count()
def get_course_title(self, obj):
return obj.course.title if obj.course else None
if not obj.course:
return None
lang = get_request_lang(self)
return get_localized_field(lang, obj.course.title)
def get_lesson_title(self, obj):
if not obj.lesson:
return None
return obj.lesson.title or (obj.lesson.lesson.title if obj.lesson.lesson else None)
lang = get_request_lang(self)
raw_title = obj.lesson.title or (obj.lesson.lesson.title if obj.lesson.lesson else None)
return get_localized_field(lang, raw_title)
class AdminQuestionSerializer(serializers.ModelSerializer):

12
apps/transaction/serializers.py

@ -127,7 +127,7 @@ class AdminParticipantInfoSerializer(serializers.ModelSerializer):
class AdminTransactionSerializer(serializers.ModelSerializer):
user_name = serializers.CharField(source='user.fullname', read_only=True)
user_email = serializers.CharField(source='user.email', read_only=True)
course_title = serializers.CharField(source='course.title', read_only=True)
course_title = serializers.SerializerMethodField()
receipts = AdminTransactionReceiptSerializer(many=True, read_only=True)
participant_infos = AdminParticipantInfoSerializer(many=True, read_only=True)
@ -144,4 +144,12 @@ class AdminTransactionSerializer(serializers.ModelSerializer):
'id', 'user', 'user_name', 'user_email',
'course', 'course_title', 'price', 'payment_method',
'participant_infos', 'receipts', 'created_at', 'updated_at'
]
]
def get_course_title(self, obj):
if not obj.course:
return None
request = self.context.get('request')
lang = getattr(request, 'LANGUAGE_CODE', None) or 'en'
from apps.course.models.course import get_localized_field
return get_localized_field(lang, obj.course.title)
Loading…
Cancel
Save