from django.contrib import admin from django.db.models import F from django.contrib.admin import SimpleListFilter from django.utils.translation import gettext_lazy as _ from django import forms from unfold.admin import ModelAdmin, StackedInline, TabularInline from unfold.decorators import display from apps.quiz.models import QuizParticipant, ParticipantAnswer from apps.account.models import User from utils.admin import project_admin_site # --- INLINE FOR QUIZ DETAIL PAGE --- class MinWidthInlineForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) target_dropdown_fields = ['user'] for field_name, field in self.fields.items(): if field_name in target_dropdown_fields and hasattr(field.widget, 'attrs'): existing_class = field.widget.attrs.get('class', '') field.widget.attrs['class'] = f"{existing_class} min-w-[250px] w-full" existing_style = field.widget.attrs.get('style', '') field.widget.attrs['style'] = f"{existing_style} min-width: 250px; width: 250px;" class QuizParticipantInline(TabularInline): model = QuizParticipant form = MinWidthInlineForm extra = 0 tab = True fields = ('user', 'started_at', 'total_timing', 'total_score') readonly_fields = ('user', 'started_at', 'total_timing', 'total_score') autocomplete_fields = ('user',) show_change_link = True verbose_name = _("Recent Participant") verbose_name_plural = _("Recent Participants (Latest 10)") def get_queryset(self, request): qs = super().get_queryset(request) object_id = request.resolver_match.kwargs.get('object_id') if object_id: latest_ids = list(qs.filter(quiz_id=object_id).order_by('-started_at').values_list('id', flat=True)[:10]) return qs.filter(id__in=latest_ids).order_by('-started_at') return qs.none() def has_add_permission(self, request, obj): return False def has_change_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False class ParticipantAnswerInline(StackedInline): model = ParticipantAnswer readonly_fields = ( 'correct_answer_display', 'question', 'at_time', 'answer_timing', ) @display(description=_("Correct Answer")) def correct_answer_display(self, obj): return obj.correct_answer def has_add_permission(self, request, obj): return False def has_delete_permission(self, request, obj=None): return False def get_queryset(self, request): return super().get_queryset(request).annotate(correct_answer=F('question__correct_answer')) class UserEmailFilter(SimpleListFilter): title = _('User Email') parameter_name = 'user_email' def lookups(self, request, model_admin): # 🔔 FILTER: Only fetch active users who have an email (not guests or deleted) users = User.objects.filter(is_active=True, email__isnull=False) return [(user.email, user.email) for user in users] def queryset(self, request, queryset): if self.value(): email = self.value().replace('%40', '@') return queryset.filter(user__email=email) return queryset class ParticipantAdmin(ModelAdmin): inlines = [ParticipantAnswerInline] search_fields = ['user__username', 'user__fullname'] list_display = [ 'quiz', 'user', 'started_at', 'ended_at', 'total_timing', 'question_score', 'timing_score', 'total_score' ] list_filter = ['started_at', 'ended_at', 'quiz__status', UserEmailFilter] # Optional: Add these for better UI experience date_hierarchy = 'started_at' ordering = ['-started_at'] # 🔔 FILTER: Restrict the user dropdown in forms to active, non-guest users def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "user": kwargs["queryset"] = User.objects.filter(is_active=True, email__isnull=False) return super().formfield_for_foreignkey(db_field, request, **kwargs) project_admin_site.register(QuizParticipant, ParticipantAdmin)