Use DB field instead of cache for contest summary

This commit is contained in:
cuom1999 2023-11-24 00:35:38 -06:00
parent 159b2b4cc0
commit b2a91af011
5 changed files with 97 additions and 55 deletions

View file

@ -24,6 +24,7 @@ from judge.widgets import (
AdminSelect2Widget, AdminSelect2Widget,
HeavyPreviewAdminPageDownWidget, HeavyPreviewAdminPageDownWidget,
) )
from judge.views.contests import recalculate_contest_summary_result
class AdminHeavySelect2Widget(AdminHeavySelect2Widget): class AdminHeavySelect2Widget(AdminHeavySelect2Widget):
@ -518,3 +519,9 @@ class ContestsSummaryAdmin(admin.ModelAdmin):
list_display = ("key",) list_display = ("key",)
search_fields = ("key", "contests__key") search_fields = ("key", "contests__key")
form = ContestsSummaryForm 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()

View file

@ -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",
),
),
]

View file

@ -920,6 +920,7 @@ class ContestsSummary(models.Model):
max_length=20, max_length=20,
unique=True, unique=True,
) )
results = models.JSONField(null=True, blank=True)
class Meta: class Meta:
verbose_name = _("contests summary") verbose_name = _("contests summary")

View file

@ -27,7 +27,6 @@ from django.db.models import (
Value, Value,
When, When,
) )
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.expressions import CombinedExpression from django.db.models.expressions import CombinedExpression
from django.http import ( from django.http import (
@ -1422,7 +1421,7 @@ def update_contest_mode(request):
ContestsSummaryData = namedtuple( ContestsSummaryData = namedtuple(
"ContestsSummaryData", "ContestsSummaryData",
"user points point_contests css_class", "username first_name last_name points point_contests css_class",
) )
@ -1437,22 +1436,28 @@ class ContestsSummaryView(DiggPaginatorMixin, ListView):
raise Http404() raise Http404()
return super().get(*args, **kwargs) return super().get(*args, **kwargs)
def _get_contests_and_ranking(self): def get_queryset(self):
contests_summary = self.contests_summary total_rank = self.contests_summary.results
cache_key = "csv:" + contests_summary.key return total_rank
result = cache.get(cache_key)
if result:
return result
scores_system = contests_summary.scores def get_context_data(self, **kwargs):
contests = contests_summary.contests.all() context = super().get_context_data(**kwargs)
context["contests"] = self.contests_summary.contests.all()
context["title"] = _("Contests")
context["first_page_href"] = "."
return context
def recalculate_contest_summary_result(contest_summary):
scores_system = contest_summary.scores
contests = contest_summary.contests.all()
total_points = defaultdict(int) total_points = defaultdict(int)
result_per_contest = defaultdict(lambda: [(0, 0)] * len(contests)) result_per_contest = defaultdict(lambda: [(0, 0)] * len(contests))
user_css_class = {} user_css_class = {}
for i in range(len(contests)): for i in range(len(contests)):
contest = contests[i] contest = contests[i]
users, problems = get_contest_ranking_list(self.request, contest) users, problems = get_contest_ranking_list(None, contest)
for rank, user in users: for rank, user in users:
curr_score = 0 curr_score = 0
if rank - 1 < len(scores_system): if rank - 1 < len(scores_system):
@ -1463,7 +1468,9 @@ class ContestsSummaryView(DiggPaginatorMixin, ListView):
sorted_total_points = [ sorted_total_points = [
ContestsSummaryData( ContestsSummaryData(
user=user, username=user.username,
first_name=user.first_name,
last_name=user.last_name,
points=total_points[user], points=total_points[user],
point_contests=result_per_contest[user], point_contests=result_per_contest[user],
css_class=user_css_class[user], css_class=user_css_class[user],
@ -1473,28 +1480,4 @@ class ContestsSummaryView(DiggPaginatorMixin, ListView):
sorted_total_points.sort(key=lambda x: x.points, reverse=True) sorted_total_points.sort(key=lambda x: x.points, reverse=True)
total_rank = ranker(sorted_total_points) total_rank = ranker(sorted_total_points)
return [(rank, item._asdict()) for rank, item in total_rank]
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"]
return total_rank
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["contests"] = self.contests
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)

View file

@ -49,9 +49,10 @@
</td> </td>
<td> <td>
<div> <div>
<span class="username {{ item.css_class }} wrapline" href="{{url('user_page', item.user.username)}}" >{{item.user.username}}</span> <a href="{{url('user_page', item.username)}}"> <span class="username {{ item.css_class }} wrapline">{{item.username}}</span></a>
</div> </div>
<div>{{item.user.first_name}}</div> <div>{{item.first_name}}</div>
<div>{{item.last_name}}</div>
</td> </td>
{% for point_contest, rank_contest in item.point_contests %} {% for point_contest, rank_contest in item.point_contests %}
<td> <td>