Browse Source
dashboard quiz page endpoints added
dashboard quiz page endpoints added
created views and serializers specially for dashboard , crud operations on quizez , filtering and ...master
4 changed files with 287 additions and 3 deletions
-
119apps/quiz/serializers/admin.py
-
12apps/quiz/urls.py
-
1apps/quiz/views/__init__.py
-
156apps/quiz/views/admin.py
@ -0,0 +1,119 @@ |
|||
from rest_framework import serializers |
|||
from apps.quiz.models import Quiz, Question, QuizParticipant |
|||
from apps.course.models import Course, CourseLesson |
|||
from apps.account.models import User |
|||
|
|||
|
|||
class AdminCourseSerializer(serializers.ModelSerializer): |
|||
class Meta: |
|||
model = Course |
|||
fields = ['id', 'title'] |
|||
|
|||
|
|||
class AdminCourseLessonSerializer(serializers.ModelSerializer): |
|||
title = serializers.SerializerMethodField() |
|||
|
|||
class Meta: |
|||
model = CourseLesson |
|||
fields = ['id', 'title'] |
|||
|
|||
def get_title(self, obj): |
|||
if obj.title: |
|||
return obj.title |
|||
if obj.lesson: |
|||
return obj.lesson.title |
|||
return f"Lesson {obj.id}" |
|||
|
|||
|
|||
class AdminQuizListSerializer(serializers.ModelSerializer): |
|||
questions_count = serializers.SerializerMethodField() |
|||
participants_count = serializers.SerializerMethodField() |
|||
course_title = serializers.SerializerMethodField() |
|||
lesson_title = serializers.SerializerMethodField() |
|||
|
|||
class Meta: |
|||
model = Quiz |
|||
fields = [ |
|||
'id', |
|||
'title', |
|||
'description', |
|||
'each_question_timing', |
|||
'status', |
|||
'course', |
|||
'course_title', |
|||
'lesson', |
|||
'lesson_title', |
|||
'questions_count', |
|||
'participants_count' |
|||
] |
|||
|
|||
def get_questions_count(self, obj): |
|||
return obj.questions.count() |
|||
|
|||
def get_participants_count(self, obj): |
|||
return obj.participants.count() |
|||
|
|||
def get_course_title(self, obj): |
|||
return obj.course.title if obj.course else None |
|||
|
|||
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) |
|||
|
|||
|
|||
class AdminQuizDetailSerializer(serializers.ModelSerializer): |
|||
class Meta: |
|||
model = Quiz |
|||
fields = [ |
|||
'id', |
|||
'title', |
|||
'description', |
|||
'each_question_timing', |
|||
'status', |
|||
'course', |
|||
'lesson' |
|||
] |
|||
read_only_fields = ['course'] # Set automatically on save based on lesson.course |
|||
|
|||
|
|||
class AdminQuestionSerializer(serializers.ModelSerializer): |
|||
class Meta: |
|||
model = Question |
|||
fields = [ |
|||
'id', |
|||
'quiz', |
|||
'question', |
|||
'option1', |
|||
'option2', |
|||
'option3', |
|||
'option4', |
|||
'correct_answer', |
|||
'priority' |
|||
] |
|||
|
|||
|
|||
class AdminQuizParticipantSerializer(serializers.ModelSerializer): |
|||
user_name = serializers.CharField(source='user.fullname', read_only=True) |
|||
user_email = serializers.EmailField(source='user.email', read_only=True) |
|||
|
|||
class Meta: |
|||
model = QuizParticipant |
|||
fields = [ |
|||
'id', |
|||
'user_id', |
|||
'user_name', |
|||
'user_email', |
|||
'started_at', |
|||
'ended_at', |
|||
'total_timing', |
|||
'question_score', |
|||
'timing_score', |
|||
'total_score' |
|||
] |
|||
|
|||
|
|||
class AdminQuizAbsenteeSerializer(serializers.ModelSerializer): |
|||
class Meta: |
|||
model = User |
|||
fields = ['id', 'fullname', 'email'] |
|||
@ -1,13 +1,21 @@ |
|||
from django.urls import path |
|||
from django.urls import path, include |
|||
from rest_framework.routers import DefaultRouter |
|||
|
|||
from . import views |
|||
|
|||
router = DefaultRouter() |
|||
router.register(r'admin/quizzes', views.AdminQuizViewSet, basename='admin-quizzes') |
|||
router.register(r'admin/questions', views.AdminQuestionViewSet, basename='admin-questions') |
|||
|
|||
urlpatterns = [ |
|||
path('', include(router.urls)), |
|||
path('admin/courses/', views.AdminCourseListView.as_view(), name='admin-courses-list'), |
|||
path('admin/lessons/', views.AdminLessonListView.as_view(), name='admin-lessons-list'), |
|||
|
|||
# path('prizes/', PrizeListAPIView.as_view()), |
|||
# path('ranked-list/', RankedListAPIView.as_view()), |
|||
# path('self-rank/', SelfRankAPIView.as_view()), |
|||
# path('my-quizzes/', UserQuizScores.as_view()), |
|||
path('submit-quiz/', views.QuizParticipantCreateAPIView.as_view()), |
|||
path('<int:quiz_id>/', views.QuizDetailAPIView.as_view()), |
|||
|
|||
] |
|||
@ -1,2 +1,3 @@ |
|||
from .quiz import * |
|||
from .participant import * |
|||
from .admin import * |
|||
@ -0,0 +1,156 @@ |
|||
from django.db.models import Q |
|||
from rest_framework.viewsets import ModelViewSet |
|||
from rest_framework.generics import ListAPIView |
|||
from rest_framework.permissions import IsAuthenticated, IsAdminUser |
|||
from rest_framework.authentication import TokenAuthentication |
|||
from rest_framework.decorators import action |
|||
from rest_framework.response import Response |
|||
from django.shortcuts import get_object_or_404 |
|||
|
|||
from apps.quiz.models import Quiz, Question, QuizParticipant |
|||
from apps.course.models import Course, CourseLesson, Participant |
|||
from apps.account.models import User |
|||
|
|||
from apps.quiz.serializers.admin import ( |
|||
AdminCourseSerializer, |
|||
AdminCourseLessonSerializer, |
|||
AdminQuizListSerializer, |
|||
AdminQuizDetailSerializer, |
|||
AdminQuestionSerializer, |
|||
AdminQuizParticipantSerializer, |
|||
AdminQuizAbsenteeSerializer |
|||
) |
|||
|
|||
|
|||
class AdminQuizViewSet(ModelViewSet): |
|||
""" |
|||
Admin CRUD ViewSet for Quiz management. |
|||
Includes searching, filtering, and retrieving participants/absentees list. |
|||
""" |
|||
permission_classes = [IsAuthenticated, IsAdminUser] |
|||
authentication_classes = [TokenAuthentication] |
|||
|
|||
def get_serializer_class(self): |
|||
if self.action == 'list': |
|||
return AdminQuizListSerializer |
|||
return AdminQuizDetailSerializer |
|||
|
|||
def get_queryset(self): |
|||
queryset = Quiz.objects.all().select_related('course', 'lesson') |
|||
|
|||
# Handle Search |
|||
search_query = self.request.query_params.get('search', None) |
|||
if search_query: |
|||
queryset = queryset.filter( |
|||
Q(title__icontains=search_query) | |
|||
Q(description__icontains=search_query) |
|||
) |
|||
|
|||
# Handle Course Filter |
|||
course_id = self.request.query_params.get('course', None) |
|||
if course_id: |
|||
queryset = queryset.filter(course_id=course_id) |
|||
|
|||
# Handle Lesson Filter |
|||
lesson_id = self.request.query_params.get('lesson', None) |
|||
if lesson_id: |
|||
queryset = queryset.filter(lesson_id=lesson_id) |
|||
|
|||
# Handle Status Filter |
|||
status_param = self.request.query_params.get('status', None) |
|||
if status_param is not None: |
|||
if status_param.lower() == 'true': |
|||
queryset = queryset.filter(status=True) |
|||
elif status_param.lower() == 'false': |
|||
queryset = queryset.filter(status=False) |
|||
|
|||
return queryset.order_by('-id') |
|||
|
|||
@action(detail=True, methods=['get']) |
|||
def participants(self, request, pk=None): |
|||
""" |
|||
Returns two lists: |
|||
1. Participants: users who have participated in the quiz. |
|||
2. Absentees: users who are enrolled in the course but have not participated. |
|||
""" |
|||
quiz = self.get_object() |
|||
|
|||
# 1. Fetch participants |
|||
participants_qs = QuizParticipant.objects.filter(quiz=quiz).select_related('user') |
|||
participants_serializer = AdminQuizParticipantSerializer(participants_qs, many=True) |
|||
|
|||
# 2. Fetch absentees |
|||
absentees_data = [] |
|||
if quiz.course: |
|||
# Enrolled course student IDs |
|||
enrolled_student_ids = Participant.objects.filter( |
|||
course=quiz.course, |
|||
is_active=True |
|||
).values_list('student_id', flat=True) |
|||
|
|||
# Participated user IDs |
|||
participated_user_ids = participants_qs.values_list('user_id', flat=True) |
|||
|
|||
# Absentee IDs |
|||
absentee_ids = set(enrolled_student_ids) - set(participated_user_ids) |
|||
|
|||
# Fetch User details for absentees |
|||
absentees_qs = User.objects.filter(id__in=absentee_ids) |
|||
absentees_serializer = AdminQuizAbsenteeSerializer(absentees_qs, many=True) |
|||
absentees_data = absentees_serializer.data |
|||
|
|||
return Response({ |
|||
'participants': participants_serializer.data, |
|||
'absentees': absentees_data |
|||
}) |
|||
|
|||
|
|||
class AdminQuestionViewSet(ModelViewSet): |
|||
""" |
|||
Admin CRUD ViewSet for Question management. |
|||
Usually filtered by quiz_id. |
|||
""" |
|||
serializer_class = AdminQuestionSerializer |
|||
permission_classes = [IsAuthenticated, IsAdminUser] |
|||
authentication_classes = [TokenAuthentication] |
|||
pagination_class = None |
|||
|
|||
def get_queryset(self): |
|||
queryset = Question.objects.all() |
|||
|
|||
quiz_id = self.request.query_params.get('quiz_id', None) |
|||
if not quiz_id: |
|||
# Fallback parameter name |
|||
quiz_id = self.request.query_params.get('quiz', None) |
|||
|
|||
if quiz_id: |
|||
queryset = queryset.filter(quiz_id=quiz_id) |
|||
|
|||
return queryset.order_by('priority', 'id') |
|||
|
|||
|
|||
class AdminCourseListView(ListAPIView): |
|||
""" |
|||
Dropdown listing helper for all courses. |
|||
""" |
|||
queryset = Course.objects.all().order_by('-id') |
|||
serializer_class = AdminCourseSerializer |
|||
permission_classes = [IsAuthenticated, IsAdminUser] |
|||
authentication_classes = [TokenAuthentication] |
|||
pagination_class = None # Return all courses for dropdown usage |
|||
|
|||
|
|||
class AdminLessonListView(ListAPIView): |
|||
""" |
|||
Dropdown listing helper for lessons. Filtered by course_id. |
|||
""" |
|||
serializer_class = AdminCourseLessonSerializer |
|||
permission_classes = [IsAuthenticated, IsAdminUser] |
|||
authentication_classes = [TokenAuthentication] |
|||
pagination_class = None # Return all matching lessons for dropdown usage |
|||
|
|||
def get_queryset(self): |
|||
course_id = self.request.query_params.get('course_id', None) |
|||
if course_id: |
|||
return CourseLesson.objects.filter(course_id=course_id).select_related('lesson').order_by('priority') |
|||
return CourseLesson.objects.all().select_related('lesson').order_by('-id') |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue