chore(backend): improvements

This commit is contained in:
ITQ
2025-04-08 00:37:39 +03:00
parent ad0e795250
commit 9c121af053
15 changed files with 174 additions and 47 deletions
@@ -14,4 +14,5 @@ class AchievementAdmin(admin.ModelAdmin):
"description",
)
admin.site.register(UserAchievement)
@@ -0,0 +1,34 @@
# Generated by Django 5.2 on 2025-04-07 21:25
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('achievement', '0002_initial'),
('user', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='userachievement',
options={'verbose_name': 'выданная ачивка', 'verbose_name_plural': 'выданные ачивки'},
),
migrations.AlterField(
model_name='userachievement',
name='achievement',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='achievement.achievement', verbose_name='ачивка'),
),
migrations.AlterField(
model_name='userachievement',
name='received_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='дата получения'),
),
migrations.AlterField(
model_name='userachievement',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='user.user', verbose_name='пользователь'),
),
]
+9 -3
View File
@@ -27,10 +27,16 @@ class Achievement(BaseModel):
class UserAchievement(BaseModel):
achievement = models.ForeignKey(Achievement, verbose_name="ачивка", on_delete=models.CASCADE)
user = models.ForeignKey("user.User", verbose_name="пользователь", on_delete=models.CASCADE)
achievement = models.ForeignKey(
Achievement, verbose_name="ачивка", on_delete=models.CASCADE
)
user = models.ForeignKey(
"user.User", verbose_name="пользователь", on_delete=models.CASCADE
)
received_at = models.DateTimeField(verbose_name="дата получения", auto_now_add=True)
received_at = models.DateTimeField(
verbose_name="дата получения", auto_now_add=True
)
class Meta:
verbose_name = "выданная ачивка"
+48 -28
View File
@@ -1,15 +1,14 @@
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.db.models import Q, Sum
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.urls import path, reverse
from django.utils.html import format_html
from apps.user.models import User
from apps.competition.models import Competition, State
from apps.task.admin import CompetitionTaskInline
from apps.task.models import CompetitionTaskSubmission, CompetitionTask
from apps.task.models import CompetitionTask
from apps.user.models import User
@admin.register(Competition)
@@ -34,33 +33,46 @@ class CompetitionAdmin(admin.ModelAdmin):
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'),
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])
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
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]
.exclude(total_score__isnull=True)
.order_by("-total_score")[:20]
)
context = dict(
self.admin_site.each_context(request),
@@ -68,22 +80,30 @@ class CompetitionAdmin(admin.ModelAdmin):
leaderboard=leaderboard,
competition=competition,
)
return TemplateResponse(request, "admin/competition_leaderboard.html", context)
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')
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]
.exclude(total_score__isnull=True)
.order_by("-total_score")[:20]
)
context = dict(
self.admin_site.each_context(request),
title="Лидерборд соревнования",
leaderboard=leaderboard,
)
return TemplateResponse(request, "admin/competition_leaderboard.html", context)
return TemplateResponse(
request, "admin/competition_leaderboard.html", context
)
admin.site.register(State)
+1 -1
View File
@@ -7,4 +7,4 @@ class CompetitionsConfig(AppConfig):
verbose_name = "Соревнование"
def ready(self):
import apps.competition.signals
pass
@@ -0,0 +1,40 @@
# Generated by Django 5.2 on 2025-04-07 21:25
import datetime
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('competition', '0001_initial'),
('user', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='state',
options={'verbose_name': 'участие', 'verbose_name_plural': 'участия'},
),
migrations.AlterField(
model_name='state',
name='changed_at',
field=models.DateTimeField(default=datetime.datetime.now, verbose_name='изменено'),
),
migrations.AlterField(
model_name='state',
name='competition',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.competition', verbose_name='соревнование'),
),
migrations.AlterField(
model_name='state',
name='state',
field=models.CharField(choices=[('not_started', 'Not Started'), ('started', 'Started'), ('finished', 'Finished')], default='not_started', max_length=11, verbose_name='статус'),
),
migrations.AlterField(
model_name='state',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='user.user', verbose_name='участник'),
),
]
+9 -3
View File
@@ -59,15 +59,21 @@ class State(BaseModel):
STARTED = "started"
FINISHED = "finished"
user = models.ForeignKey(User, verbose_name="участник", on_delete=models.CASCADE)
competition = models.ForeignKey(Competition, verbose_name="соревнование", on_delete=models.CASCADE)
user = models.ForeignKey(
User, verbose_name="участник", on_delete=models.CASCADE
)
competition = models.ForeignKey(
Competition, verbose_name="соревнование", on_delete=models.CASCADE
)
state = models.CharField(
choices=StateChoices.choices,
verbose_name="статус",
max_length=11,
default=StateChoices.NOT_STARTED.value,
)
changed_at = models.DateTimeField(verbose_name="изменено", default=datetime.now)
changed_at = models.DateTimeField(
verbose_name="изменено", default=datetime.now
)
class Meta:
verbose_name = "участие"
+1 -1
View File
@@ -1,6 +1,6 @@
from django.contrib import admin
from apps.review.models import Reviewer, Review
from apps.review.models import Review, Reviewer
@admin.register(Reviewer)
+1 -1
View File
@@ -7,4 +7,4 @@ class CoreConfig(AppConfig):
verbose_name = "Проверка"
def ready(self):
import apps.review.signals
pass
@@ -0,0 +1,18 @@
# Generated by Django 5.2 on 2025-04-07 21:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('review', '0002_initial'),
]
operations = [
migrations.AlterField(
model_name='review',
name='evaluation',
field=models.JSONField(blank=True, default=list, null=True, verbose_name='оценка'),
),
]
+1 -1
View File
@@ -45,7 +45,7 @@ class Review(BaseModel):
)
def __str__(self):
return str(self.id)
return f"{str(self.reviewer)} -> {self.submission.task.title} ({self.submission.user.username})"
class Meta:
verbose_name = "проверка"
+1 -1
View File
@@ -7,4 +7,4 @@ class UsersConfig(AppConfig):
verbose_name = "контестанты"
def ready(self):
import apps.user.signals
pass