diff --git a/apps/quiz/models/quiz.py b/apps/quiz/models/quiz.py index 37a9564..e7ce23c 100644 --- a/apps/quiz/models/quiz.py +++ b/apps/quiz/models/quiz.py @@ -54,7 +54,7 @@ class Question(models.Model): class Meta: verbose_name = _("Question") verbose_name_plural = _("Questions") - ordering = ("-priority", "-id",) + ordering = ("priority", "id",) def __str__(self): return self.question @@ -62,6 +62,12 @@ class Question(models.Model): def __repr__(self): return f"Question(id={self.id})" + def save(self, *args, **kwargs): + if self.priority is None: + max_priority = Question.objects.filter(quiz=self.quiz).aggregate(models.Max('priority'))['priority__max'] + self.priority = (max_priority or 0) + 1 + super().save(*args, **kwargs) + class QuizRankUser(User): class Meta: diff --git a/apps/quiz/serializers/admin.py b/apps/quiz/serializers/admin.py index 7bf1cb0..d72570d 100644 --- a/apps/quiz/serializers/admin.py +++ b/apps/quiz/serializers/admin.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from apps.quiz.models import Quiz, Question, QuizParticipant +from apps.quiz.models import Quiz, Question, QuizParticipant, ParticipantAnswer from apps.course.models import Course, CourseLesson from apps.account.models import User @@ -93,9 +93,16 @@ class AdminQuestionSerializer(serializers.ModelSerializer): ] +class AdminParticipantAnswerSerializer(serializers.ModelSerializer): + class Meta: + model = ParticipantAnswer + fields = ['id', 'question', 'option_num', 'at_time', 'answer_timing'] + + class AdminQuizParticipantSerializer(serializers.ModelSerializer): user_name = serializers.CharField(source='user.fullname', read_only=True) user_email = serializers.EmailField(source='user.email', read_only=True) + answers = AdminParticipantAnswerSerializer(many=True, read_only=True) class Meta: model = QuizParticipant @@ -109,7 +116,8 @@ class AdminQuizParticipantSerializer(serializers.ModelSerializer): 'total_timing', 'question_score', 'timing_score', - 'total_score' + 'total_score', + 'answers' ] diff --git a/apps/quiz/views/admin.py b/apps/quiz/views/admin.py index 2a99f52..0a7babb 100644 --- a/apps/quiz/views/admin.py +++ b/apps/quiz/views/admin.py @@ -76,7 +76,7 @@ class AdminQuizViewSet(ModelViewSet): quiz = self.get_object() # 1. Fetch participants - participants_qs = QuizParticipant.objects.filter(quiz=quiz).select_related('user') + participants_qs = QuizParticipant.objects.filter(quiz=quiz).select_related('user').prefetch_related('answers') participants_serializer = AdminQuizParticipantSerializer(participants_qs, many=True) # 2. Fetch absentees @@ -128,6 +128,34 @@ class AdminQuestionViewSet(ModelViewSet): return queryset.order_by('priority', 'id') + @action(detail=False, methods=['post']) + def reorder(self, request): + """ + Reorders questions in bulk. + Accepts a list of question IDs. + Format: + { + "question_ids": [10, 12, 11, 15] + } + """ + question_ids = request.data.get('question_ids', []) + if not question_ids: + return Response({'error': 'question_ids list is required'}, status=400) + + # Update priority for each question based on its position in the list + from django.db import transaction + questions = Question.objects.filter(id__in=question_ids) + question_map = {q.id: q for q in questions} + + with transaction.atomic(): + for idx, q_id in enumerate(question_ids): + if q_id in question_map: + question = question_map[q_id] + question.priority = idx + 1 + question.save(update_fields=['priority']) + + return Response({'status': 'success'}) + class AdminCourseListView(ListAPIView): """