add leaderboard view to admin panel

This commit is contained in:
Timur
2025-03-04 06:13:38 +03:00
parent 515c36dd51
commit 3ac2cebffd
3 changed files with 107 additions and 1 deletions
@@ -1,7 +1,15 @@
from django.contrib import admin from django.contrib import admin
from django.urls import path, reverse
from django.http import HttpResponse
from django.template.response import TemplateResponse
from django.db.models import Count, Q, Sum
from django.shortcuts import get_object_or_404
from django.utils.html import format_html
from apps.user.models import User
from apps.competition.models import Competition from apps.competition.models import Competition
from apps.task.admin import CompetitionTaskInline from apps.task.admin import CompetitionTaskInline
from apps.task.models import CompetitionTaskSubmission, CompetitionTask
@admin.register(Competition) @admin.register(Competition)
@@ -11,6 +19,7 @@ class CompetitionAdmin(admin.ModelAdmin):
"title", "title",
"end_date", "end_date",
"type", "type",
"view_leaderboard",
) )
search_fields = ( search_fields = (
"title", "title",
@@ -21,3 +30,57 @@ class CompetitionAdmin(admin.ModelAdmin):
"participation_type", "participation_type",
) )
inlines = [CompetitionTaskInline] inlines = [CompetitionTaskInline]
def get_urls(self):
urls = super().get_urls()
custom_urls = [
path('leaderboard/', self.admin_site.admin_view(self.leaderboard_view), name='competition_leaderboard'),
path('<uuid:competition_id>/leaderboard/', self.admin_site.admin_view(self.competition_leaderboard_view),
name='competition_specific_leaderboard'),
]
return custom_urls + urls
def view_leaderboard(self, obj):
url = reverse('admin:competition_specific_leaderboard', args=[obj.id])
return format_html('<a href="{}">лидерборд</a>', url)
view_leaderboard.short_description = "Лидерборд"
view_leaderboard.allow_tags = True
def competition_leaderboard_view(self, request, competition_id):
competition = get_object_or_404(Competition, id=competition_id)
competition_tasks = CompetitionTask.objects.filter(competition=competition)
leaderboard = User.objects.annotate(
total_score=Sum(
'competitiontasksubmission__earned_points',
filter=Q(
competitiontasksubmission__status='checked',
competitiontasksubmission__task__in=competition_tasks
)
)
).exclude(total_score__isnull=True).order_by('-total_score')[:20]
context = dict(
self.admin_site.each_context(request),
title=f"Лидерборд для {competition.title}",
leaderboard=leaderboard,
competition=competition,
)
return TemplateResponse(request, "admin/competition_leaderboard.html", context)
def leaderboard_view(self, request):
leaderboard = User.objects.annotate(
total_score=Sum(
'competitiontasksubmission__earned_points',
filter=Q(competitiontasksubmission__status='checked')
)
).exclude(total_score__isnull=True).order_by('-total_score')[:20]
context = dict(
self.admin_site.each_context(request),
title="Global Competition Leaderboard",
leaderboard=leaderboard,
)
return TemplateResponse(request, "admin/competition_leaderboard.html", context)
+1 -1
View File
@@ -552,7 +552,7 @@ SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", "BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [], "DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True, "APP_DIRS": True,
"OPTIONS": { "OPTIONS": {
"autoescape": True, "autoescape": True,
@@ -0,0 +1,43 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label='competition' %}">Соревнования</a>
{% if competition %}
&rsaquo; <a href="{% url 'admin:competition_competition_change' competition.id %}">{{ competition.title }}</a>
{% endif %}
&rsaquo; Лидерборд
</div>
{% endblock %}
{% block content %}
<div id="content-main">
<h1>{% if competition %}Лидерборд: {{ competition.title }}{% else %}Лидерборд соревнования{% endif %}</h1>
<div class="module">
<table>
<thead>
<tr>
<th>Ранг</th>
<th>Участник</th>
<th>Баллы</th>
</tr>
</thead>
<tbody>
{% for user in leaderboard %}
<tr class="{% cycle 'row1' 'row2' %}">
<td>{{ forloop.counter }}</td>
<td>{{ user.username }}</td>
<td>{{ user.total_score|default:0 }}</td>
</tr>
{% empty %}
<tr>
<td colspan="3">Пока что нет посылок</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}