You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

111 lines
4.1 KiB

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)