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.account.models import User, LoginHistory
from apps.course.models import Course, Participant, CourseLiveSession 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.quiz.models import QuizParticipant
from apps.blog.models import Blog from apps.blog.models import Blog
from apps.chat.models import ChatMessage, RoomMessage from apps.chat.models import ChatMessage, RoomMessage
@ -31,7 +32,7 @@ class AdminDashboardStatsView(APIView):
is_active=True is_active=True
).count() ).count()
professors = User.objects.filter(user_type=User.UserType.PROFESSOR, 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() total_blogs = Blog.objects.count()
pending_certificates = Certificate.objects.filter(status='pending').count() pending_certificates = Certificate.objects.filter(status='pending').count()
active_live_sessions = CourseLiveSession.objects.filter(ended_at__isnull=True).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] chat_chart = [{"date": item['date'].strftime('%Y-%m-%d'), "messages": item['count']} for item in chat_volume]
# Course Pipeline (Stacked Bar) # Course Pipeline (Stacked Bar)
course_statuses = Course.objects.values('status').annotate(count=Count('id'))
course_pipeline = {"name": "Courses"} 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) # 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 # 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, course=course,
student__lesson_completions__course_lesson__course=course student__lesson_completions__course_lesson__course=course
).distinct().count() ).distinct().count()
title_text = extract_text_from_json(course.title)
course_efficacy.append({ 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, "enrolled": enrolled,
"completed": completed "completed": completed
}) })
@ -113,7 +114,7 @@ class AdminDashboardStatsView(APIView):
top_courses_list = [ top_courses_list = [
{ {
"id": c.id, "id": c.id,
"title": c.title,
"title": extract_text_from_json(c.title),
"professor": c.professor.fullname if c.professor else "Unknown", "professor": c.professor.fullname if c.professor else "Unknown",
"participants": c.participant_count "participants": c.participant_count
} for c in top_courses_qs } for c in top_courses_qs

10
apps/certificate/serializers.py

@ -50,7 +50,7 @@ class CertificateRequestSerializer(serializers.ModelSerializer):
class AdminCertificateSerializer(serializers.ModelSerializer): class AdminCertificateSerializer(serializers.ModelSerializer):
student_name = serializers.CharField(source='student.fullname', read_only=True) student_name = serializers.CharField(source='student.fullname', read_only=True)
student_email = serializers.CharField(source='student.email', 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: class Meta:
model = Certificate model = Certificate
@ -60,6 +60,14 @@ class AdminCertificateSerializer(serializers.ModelSerializer):
'created_at', 'updated_at' '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 LiveSessionRecording
) )
from apps.account.models import ProfessorUser 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 AdminLessonSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Lesson model = Lesson
fields = '__all__' 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 AdminGlossarySerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Glossary model = Glossary
fields = '__all__' 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 AdminAttachmentSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Attachment model = Attachment
fields = '__all__' 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): def serialize_file(value, serializer):
if not value: if not value:
@ -47,6 +70,13 @@ class AdminCourseCategorySerializer(serializers.ModelSerializer):
model = CourseCategory model = CourseCategory
fields = ['id', 'name', 'slug'] 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): class AdminCourseListSerializer(serializers.ModelSerializer):
category_name = serializers.SerializerMethodField() category_name = serializers.SerializerMethodField()
professor_name = serializers.CharField(source='professor.fullname', read_only=True) 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')) return get_thumbs(obj.thumbnail, self.context.get('request'))
def get_category_name(self, obj): 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): class AdminCourseDetailSerializer(serializers.ModelSerializer):
@ -135,7 +177,21 @@ class AdminCourseDetailSerializer(serializers.ModelSerializer):
return obj.participants.count() return obj.participants.count()
def get_category_name(self, obj): 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): class AdminCourseChapterSerializer(serializers.ModelSerializer):
@ -144,6 +200,12 @@ class AdminCourseChapterSerializer(serializers.ModelSerializer):
fields = ['id', 'course', 'title', 'priority', 'is_active', 'created_at'] fields = ['id', 'course', 'title', 'priority', 'is_active', 'created_at']
read_only_fields = ['id', '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): class AdminCourseLessonSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True) id = serializers.IntegerField(read_only=True)
@ -168,15 +230,23 @@ class AdminCourseLessonSerializer(serializers.Serializer):
Map a CourseLesson instance to representation fields. Map a CourseLesson instance to representation fields.
""" """
lesson_obj = instance.lesson 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 { return {
'id': instance.id, 'id': instance.id,
'course': instance.course_id, 'course': instance.course_id,
'chapter': instance.chapter_id, 'chapter': instance.chapter_id,
'lesson': instance.lesson_id, 'lesson': instance.lesson_id,
'title': instance.title or (lesson_obj.title if lesson_obj else []),
'title': localized_title,
'priority': instance.priority, 'priority': instance.priority,
'is_active': instance.is_active, '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, '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 "", 'video_link': lesson_obj.video_link if lesson_obj else "",
'duration': lesson_obj.duration if lesson_obj else 0, 'duration': lesson_obj.duration if lesson_obj else 0,
@ -273,11 +343,14 @@ class AdminCourseAttachmentSerializer(serializers.Serializer):
def to_representation(self, instance): def to_representation(self, instance):
attachment_obj = instance.attachment 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 { return {
'id': instance.id, 'id': instance.id,
'course': instance.course_id, 'course': instance.course_id,
'attachment': instance.attachment_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': 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 'file_size': attachment_obj.file_size if attachment_obj else 0
} }
@ -339,12 +412,15 @@ class AdminCourseGlossarySerializer(serializers.Serializer):
def to_representation(self, instance): def to_representation(self, instance):
glossary_obj = instance.glossary 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 { return {
'id': instance.id, 'id': instance.id,
'course': instance.course_id, 'course': instance.course_id,
'glossary': instance.glossary_id, 'glossary': instance.glossary_id,
'title': glossary_obj.title if glossary_obj else "", 'title': glossary_obj.title if glossary_obj else "",
'description': glossary_obj.description if glossary_obj else []
'description': localized_description
} }
def create(self, validated_data): def create(self, validated_data):
@ -411,19 +487,25 @@ class AdminLiveSessionRecordingSerializer(serializers.ModelSerializer):
class AdminLiveSessionListSerializer(serializers.ModelSerializer): class AdminLiveSessionListSerializer(serializers.ModelSerializer):
course_title = serializers.CharField(source='course.title', read_only=True)
course_title = serializers.SerializerMethodField()
participant_count = serializers.SerializerMethodField() participant_count = serializers.SerializerMethodField()
class Meta: class Meta:
model = CourseLiveSession model = CourseLiveSession
fields = ['id', 'course', 'course_title', 'room_id', 'subject', 'started_at', 'ended_at', 'participant_count', 'created_at'] 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): def get_participant_count(self, obj):
return obj.user_sessions.count() return obj.user_sessions.count()
class AdminLiveSessionDetailSerializer(serializers.ModelSerializer): 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) user_sessions = AdminLiveSessionUserSerializer(many=True, read_only=True)
recordings = AdminLiveSessionRecordingSerializer(many=True, read_only=True) recordings = AdminLiveSessionRecordingSerializer(many=True, read_only=True)
recorded_file = FileFieldSerializer(required=False, allow_null=True) recorded_file = FileFieldSerializer(required=False, allow_null=True)
@ -431,3 +513,9 @@ class AdminLiveSessionDetailSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = CourseLiveSession model = CourseLiveSession
fields = ['id', 'course', 'course_title', 'room_id', 'subject', 'started_at', 'ended_at', 'recorded_file', 'user_sessions', 'recordings', 'created_at', 'updated_at'] 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, LiveSessionUser,
Participant, Participant,
) )
from apps.course.models.course import extract_text_from_json
from apps.course.doc import * from apps.course.doc import *
from apps.course.services.plugnmeet import PlugNMeetClient, PlugNMeetError from apps.course.services.plugnmeet import PlugNMeetClient, PlugNMeetError
from apps.account.serializers import UserProfileSerializer from apps.account.serializers import UserProfileSerializer
@ -618,7 +619,7 @@ class CourseOnlineClassTokenValidateAPIView(GenericAPIView):
}, status=status.HTTP_200_OK) }, status=status.HTTP_200_OK)
def _build_metadata(self, course: Course, payload: dict, user=None) -> dict: 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] has_started = status_value in [Course.StatusChoices.ONGOING, Course.StatusChoices.FINISHED]
timing_data = course.timing if isinstance(course.timing, dict) else {} 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__lesson',
'lessons__completions', 'lessons__completions',
'participants__student', '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.quiz.models import Quiz, Question, QuizParticipant, ParticipantAnswer
from apps.course.models import Course, CourseLesson from apps.course.models import Course, CourseLesson
from apps.account.models import User 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): class AdminCourseSerializer(serializers.ModelSerializer):
@ -9,6 +14,12 @@ class AdminCourseSerializer(serializers.ModelSerializer):
model = Course model = Course
fields = ['id', 'title'] 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): class AdminCourseLessonSerializer(serializers.ModelSerializer):
title = serializers.SerializerMethodField() title = serializers.SerializerMethodField()
@ -19,11 +30,9 @@ class AdminCourseLessonSerializer(serializers.ModelSerializer):
ref_name = 'QuizAdminCourseLesson' ref_name = 'QuizAdminCourseLesson'
def get_title(self, obj): 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): class AdminQuizListSerializer(serializers.ModelSerializer):
@ -55,12 +64,17 @@ class AdminQuizListSerializer(serializers.ModelSerializer):
return obj.participants.count() return obj.participants.count()
def get_course_title(self, obj): 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): def get_lesson_title(self, obj):
if not obj.lesson: if not obj.lesson:
return None 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): class AdminQuizDetailSerializer(serializers.ModelSerializer):
@ -93,12 +107,17 @@ class AdminQuizDetailSerializer(serializers.ModelSerializer):
return obj.participants.count() return obj.participants.count()
def get_course_title(self, obj): 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): def get_lesson_title(self, obj):
if not obj.lesson: if not obj.lesson:
return None 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): class AdminQuestionSerializer(serializers.ModelSerializer):

12
apps/transaction/serializers.py

@ -127,7 +127,7 @@ class AdminParticipantInfoSerializer(serializers.ModelSerializer):
class AdminTransactionSerializer(serializers.ModelSerializer): class AdminTransactionSerializer(serializers.ModelSerializer):
user_name = serializers.CharField(source='user.fullname', read_only=True) user_name = serializers.CharField(source='user.fullname', read_only=True)
user_email = serializers.CharField(source='user.email', 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) receipts = AdminTransactionReceiptSerializer(many=True, read_only=True)
participant_infos = AdminParticipantInfoSerializer(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', 'id', 'user', 'user_name', 'user_email',
'course', 'course_title', 'price', 'payment_method', 'course', 'course_title', 'price', 'payment_method',
'participant_infos', 'receipts', 'created_at', 'updated_at' '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