Use DB field instead of cache for contest summary
This commit is contained in:
parent
159b2b4cc0
commit
b2a91af011
5 changed files with 97 additions and 55 deletions
|
@ -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()
|
||||||
|
|
50
judge/migrations/0174_contest_summary_result.py
Normal file
50
judge/migrations/0174_contest_summary_result.py
Normal 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",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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")
|
||||||
|
|
|
@ -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,64 +1436,48 @@ class ContestsSummaryView(DiggPaginatorMixin, ListView):
|
||||||
raise Http404()
|
raise Http404()
|
||||||
return super().get(*args, **kwargs)
|
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):
|
def get_queryset(self):
|
||||||
rank_and_contests = self._get_contests_and_ranking()
|
total_rank = self.contests_summary.results
|
||||||
total_rank = rank_and_contests["total_rank"]
|
|
||||||
self.contests = rank_and_contests["contests"]
|
|
||||||
return total_rank
|
return total_rank
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["contests"] = self.contests
|
context["contests"] = self.contests_summary.contests.all()
|
||||||
context["title"] = _("Contests")
|
context["title"] = _("Contests")
|
||||||
context["first_page_href"] = "."
|
context["first_page_href"] = "."
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@receiver([post_save, post_delete], sender=ContestsSummary)
|
def recalculate_contest_summary_result(contest_summary):
|
||||||
def clear_cache(sender, instance, **kwargs):
|
scores_system = contest_summary.scores
|
||||||
cache.delete("csv:" + instance.key)
|
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]
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue