From b2a91af0116a55f6808147bba43d96f3326d5302 Mon Sep 17 00:00:00 2001 From: cuom1999 Date: Fri, 24 Nov 2023 00:35:38 -0600 Subject: [PATCH] Use DB field instead of cache for contest summary --- judge/admin/contest.py | 7 ++ .../migrations/0174_contest_summary_result.py | 50 +++++++++++ judge/models/contest.py | 1 + judge/views/contests.py | 89 ++++++++----------- templates/contest/contests_summary.html | 5 +- 5 files changed, 97 insertions(+), 55 deletions(-) create mode 100644 judge/migrations/0174_contest_summary_result.py diff --git a/judge/admin/contest.py b/judge/admin/contest.py index b7b5457..df2d412 100644 --- a/judge/admin/contest.py +++ b/judge/admin/contest.py @@ -24,6 +24,7 @@ from judge.widgets import ( AdminSelect2Widget, HeavyPreviewAdminPageDownWidget, ) +from judge.views.contests import recalculate_contest_summary_result class AdminHeavySelect2Widget(AdminHeavySelect2Widget): @@ -518,3 +519,9 @@ class ContestsSummaryAdmin(admin.ModelAdmin): list_display = ("key",) search_fields = ("key", "contests__key") form = ContestsSummaryForm + + def save_model(self, request, obj, form, change): + super(ContestsSummaryAdmin, self).save_model(request, obj, form, change) + obj.refresh_from_db() + obj.results = recalculate_contest_summary_result(obj) + obj.save() diff --git a/judge/migrations/0174_contest_summary_result.py b/judge/migrations/0174_contest_summary_result.py new file mode 100644 index 0000000..1a4c9cc --- /dev/null +++ b/judge/migrations/0174_contest_summary_result.py @@ -0,0 +1,50 @@ +# Generated by Django 3.2.18 on 2023-11-24 05:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("judge", "0173_fulltext"), + ] + + operations = [ + migrations.AddField( + model_name="contestssummary", + name="results", + field=models.JSONField(blank=True, null=True), + ), + migrations.AlterField( + model_name="contest", + name="authors", + field=models.ManyToManyField( + help_text="These users will be able to edit the contest.", + related_name="_judge_contest_authors_+", + to="judge.Profile", + verbose_name="authors", + ), + ), + migrations.AlterField( + model_name="contest", + name="curators", + field=models.ManyToManyField( + blank=True, + help_text="These users will be able to edit the contest, but will not be listed as authors.", + related_name="_judge_contest_curators_+", + to="judge.Profile", + verbose_name="curators", + ), + ), + migrations.AlterField( + model_name="contest", + name="testers", + field=models.ManyToManyField( + blank=True, + help_text="These users will be able to view the contest, but not edit it.", + related_name="_judge_contest_testers_+", + to="judge.Profile", + verbose_name="testers", + ), + ), + ] diff --git a/judge/models/contest.py b/judge/models/contest.py index 1432d94..9f22396 100644 --- a/judge/models/contest.py +++ b/judge/models/contest.py @@ -920,6 +920,7 @@ class ContestsSummary(models.Model): max_length=20, unique=True, ) + results = models.JSONField(null=True, blank=True) class Meta: verbose_name = _("contests summary") diff --git a/judge/views/contests.py b/judge/views/contests.py index 1ae746b..64b3d9f 100644 --- a/judge/views/contests.py +++ b/judge/views/contests.py @@ -27,7 +27,6 @@ from django.db.models import ( Value, When, ) -from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.db.models.expressions import CombinedExpression from django.http import ( @@ -1422,7 +1421,7 @@ def update_contest_mode(request): ContestsSummaryData = namedtuple( "ContestsSummaryData", - "user points point_contests css_class", + "username first_name last_name points point_contests css_class", ) @@ -1437,64 +1436,48 @@ class ContestsSummaryView(DiggPaginatorMixin, ListView): raise Http404() return super().get(*args, **kwargs) - def _get_contests_and_ranking(self): - contests_summary = self.contests_summary - cache_key = "csv:" + contests_summary.key - result = cache.get(cache_key) - if result: - return result - - scores_system = contests_summary.scores - contests = contests_summary.contests.all() - total_points = defaultdict(int) - result_per_contest = defaultdict(lambda: [(0, 0)] * len(contests)) - user_css_class = {} - - for i in range(len(contests)): - contest = contests[i] - users, problems = get_contest_ranking_list(self.request, contest) - for rank, user in users: - curr_score = 0 - if rank - 1 < len(scores_system): - curr_score = scores_system[rank - 1] - total_points[user.user] += curr_score - result_per_contest[user.user][i] = (curr_score, rank) - user_css_class[user.user] = user.css_class - - sorted_total_points = [ - ContestsSummaryData( - user=user, - points=total_points[user], - point_contests=result_per_contest[user], - css_class=user_css_class[user], - ) - for user in total_points - ] - - sorted_total_points.sort(key=lambda x: x.points, reverse=True) - total_rank = ranker(sorted_total_points) - - result = { - "total_rank": list(total_rank), - "contests": contests, - } - cache.set(cache_key, result) - return result - def get_queryset(self): - rank_and_contests = self._get_contests_and_ranking() - total_rank = rank_and_contests["total_rank"] - self.contests = rank_and_contests["contests"] + total_rank = self.contests_summary.results return total_rank def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["contests"] = self.contests + context["contests"] = self.contests_summary.contests.all() context["title"] = _("Contests") context["first_page_href"] = "." return context -@receiver([post_save, post_delete], sender=ContestsSummary) -def clear_cache(sender, instance, **kwargs): - cache.delete("csv:" + instance.key) +def recalculate_contest_summary_result(contest_summary): + scores_system = contest_summary.scores + contests = contest_summary.contests.all() + total_points = defaultdict(int) + result_per_contest = defaultdict(lambda: [(0, 0)] * len(contests)) + user_css_class = {} + + for i in range(len(contests)): + contest = contests[i] + users, problems = get_contest_ranking_list(None, contest) + for rank, user in users: + curr_score = 0 + if rank - 1 < len(scores_system): + curr_score = scores_system[rank - 1] + total_points[user.user] += curr_score + result_per_contest[user.user][i] = (curr_score, rank) + user_css_class[user.user] = user.css_class + + sorted_total_points = [ + ContestsSummaryData( + username=user.username, + first_name=user.first_name, + last_name=user.last_name, + points=total_points[user], + point_contests=result_per_contest[user], + css_class=user_css_class[user], + ) + for user in total_points + ] + + sorted_total_points.sort(key=lambda x: x.points, reverse=True) + total_rank = ranker(sorted_total_points) + return [(rank, item._asdict()) for rank, item in total_rank] diff --git a/templates/contest/contests_summary.html b/templates/contest/contests_summary.html index 3d5a999..68cf500 100644 --- a/templates/contest/contests_summary.html +++ b/templates/contest/contests_summary.html @@ -49,9 +49,10 @@
- {{item.user.username}} + {{item.username}}
-
{{item.user.first_name}}
+
{{item.first_name}}
+
{{item.last_name}}
{% for point_contest, rank_contest in item.point_contests %}