Contest caching
This commit is contained in:
parent
86d1ff4eaa
commit
571596dcbf
9 changed files with 72 additions and 23 deletions
|
@ -299,6 +299,18 @@ class ContestAdmin(CompareVersionAdmin):
|
||||||
self._rescore(obj.key)
|
self._rescore(obj.key)
|
||||||
self._rescored = True
|
self._rescored = True
|
||||||
|
|
||||||
|
if form.changed_data and any(
|
||||||
|
f in form.changed_data
|
||||||
|
for f in (
|
||||||
|
"authors",
|
||||||
|
"curators",
|
||||||
|
"testers",
|
||||||
|
)
|
||||||
|
):
|
||||||
|
Contest._author_ids.dirty(obj)
|
||||||
|
Contest._curator_ids.dirty(obj)
|
||||||
|
Contest._tester_ids.dirty(obj)
|
||||||
|
|
||||||
def save_related(self, request, form, formsets, change):
|
def save_related(self, request, form, formsets, change):
|
||||||
super().save_related(request, form, formsets, change)
|
super().save_related(request, form, formsets, change)
|
||||||
# Only rescored if we did not already do so in `save_model`
|
# Only rescored if we did not already do so in `save_model`
|
||||||
|
|
|
@ -25,6 +25,7 @@ from judge.ratings import rate_contest
|
||||||
from judge.models.pagevote import PageVotable
|
from judge.models.pagevote import PageVotable
|
||||||
from judge.models.bookmark import Bookmarkable
|
from judge.models.bookmark import Bookmarkable
|
||||||
from judge.fulltext import SearchManager
|
from judge.fulltext import SearchManager
|
||||||
|
from judge.caching import cache_wrapper
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Contest",
|
"Contest",
|
||||||
|
@ -455,25 +456,41 @@ class Contest(models.Model, PageVotable, Bookmarkable):
|
||||||
def ended(self):
|
def ended(self):
|
||||||
return self.end_time < self._now
|
return self.end_time < self._now
|
||||||
|
|
||||||
@cached_property
|
@cache_wrapper(prefix="Coai")
|
||||||
def author_ids(self):
|
def _author_ids(self):
|
||||||
return Contest.authors.through.objects.filter(contest=self).values_list(
|
return set(
|
||||||
|
Contest.authors.through.objects.filter(contest=self).values_list(
|
||||||
"profile_id", flat=True
|
"profile_id", flat=True
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@cached_property
|
@cache_wrapper(prefix="Coci")
|
||||||
def editor_ids(self):
|
def _curator_ids(self):
|
||||||
return self.author_ids.union(
|
return set(
|
||||||
Contest.curators.through.objects.filter(contest=self).values_list(
|
Contest.curators.through.objects.filter(contest=self).values_list(
|
||||||
"profile_id", flat=True
|
"profile_id", flat=True
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@cached_property
|
@cache_wrapper(prefix="Coti")
|
||||||
def tester_ids(self):
|
def _tester_ids(self):
|
||||||
return Contest.testers.through.objects.filter(contest=self).values_list(
|
return set(
|
||||||
|
Contest.testers.through.objects.filter(contest=self).values_list(
|
||||||
"profile_id", flat=True
|
"profile_id", flat=True
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def author_ids(self):
|
||||||
|
return self._author_ids()
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def editor_ids(self):
|
||||||
|
return self.author_ids.union(self._curator_ids())
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def tester_ids(self):
|
||||||
|
return self._tester_ids()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.name} ({self.key})"
|
return f"{self.name} ({self.key})"
|
||||||
|
|
|
@ -116,7 +116,7 @@ def infinite_paginate(queryset, page, page_size, pad_pages, paginator=None):
|
||||||
|
|
||||||
|
|
||||||
class InfinitePaginationMixin:
|
class InfinitePaginationMixin:
|
||||||
pad_pages = 4
|
pad_pages = 2
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def use_infinite_pagination(self):
|
def use_infinite_pagination(self):
|
||||||
|
|
|
@ -198,7 +198,7 @@ class ContestList(
|
||||||
queryset = (
|
queryset = (
|
||||||
super(ContestList, self)
|
super(ContestList, self)
|
||||||
.get_queryset()
|
.get_queryset()
|
||||||
.prefetch_related("tags", "organizations", "authors", "curators", "testers")
|
.prefetch_related("tags", "organizations")
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.request.GET.get("contest"):
|
if self.request.GET.get("contest"):
|
||||||
|
@ -248,9 +248,6 @@ class ContestList(
|
||||||
virtual=0, user=self.request.profile, contest_id__in=present
|
virtual=0, user=self.request.profile, contest_id__in=present
|
||||||
)
|
)
|
||||||
.select_related("contest")
|
.select_related("contest")
|
||||||
.prefetch_related(
|
|
||||||
"contest__authors", "contest__curators", "contest__testers"
|
|
||||||
)
|
|
||||||
.annotate(key=F("contest__key"))
|
.annotate(key=F("contest__key"))
|
||||||
):
|
):
|
||||||
if not participation.ended:
|
if not participation.ended:
|
||||||
|
|
|
@ -478,6 +478,9 @@ class OrganizationSubmissions(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_title(self):
|
||||||
|
return _("Submissions in") + f" {self.organization}"
|
||||||
|
|
||||||
|
|
||||||
class OrganizationMembershipChange(
|
class OrganizationMembershipChange(
|
||||||
LoginRequiredMixin, OrganizationMixin, SingleObjectMixin, View
|
LoginRequiredMixin, OrganizationMixin, SingleObjectMixin, View
|
||||||
|
@ -983,6 +986,18 @@ class EditOrganizationContest(
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
transaction.on_commit(rescore_contest.s(self.object.key).delay)
|
transaction.on_commit(rescore_contest.s(self.object.key).delay)
|
||||||
|
|
||||||
|
if any(
|
||||||
|
f in form.changed_data
|
||||||
|
for f in (
|
||||||
|
"authors",
|
||||||
|
"curators",
|
||||||
|
"testers",
|
||||||
|
)
|
||||||
|
):
|
||||||
|
Contest._author_ids.dirty(self.object)
|
||||||
|
Contest._curator_ids.dirty(self.object)
|
||||||
|
Contest._tester_ids.dirty(self.object)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def get_problem_formset(self, post=False):
|
def get_problem_formset(self, post=False):
|
||||||
|
@ -1075,7 +1090,7 @@ class EditOrganizationBlog(
|
||||||
if self.organization not in self.blog.organizations.all():
|
if self.organization not in self.blog.organizations.all():
|
||||||
raise Exception("This blog does not belong to this organization")
|
raise Exception("This blog does not belong to this organization")
|
||||||
if (
|
if (
|
||||||
self.request.profile not in self.blog.authors.all()
|
self.request.profile.id not in self.blog.get_authors()
|
||||||
and not self.can_edit_organization(self.organization)
|
and not self.can_edit_organization(self.organization)
|
||||||
):
|
):
|
||||||
raise Exception("Not allowed to edit this blog")
|
raise Exception("Not allowed to edit this blog")
|
||||||
|
|
|
@ -64,6 +64,7 @@ from judge.models import (
|
||||||
Organization,
|
Organization,
|
||||||
Profile,
|
Profile,
|
||||||
LanguageTemplate,
|
LanguageTemplate,
|
||||||
|
Contest,
|
||||||
)
|
)
|
||||||
from judge.pdf_problems import DefaultPdfMaker, HAS_PDF
|
from judge.pdf_problems import DefaultPdfMaker, HAS_PDF
|
||||||
from judge.utils.diggpaginator import DiggPaginator
|
from judge.utils.diggpaginator import DiggPaginator
|
||||||
|
@ -587,7 +588,7 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
|
||||||
i
|
i
|
||||||
for i in query
|
for i in query
|
||||||
if i in self.profile.organizations.values_list("id", flat=True)
|
if i in self.profile.organizations.values_list("id", flat=True)
|
||||||
]
|
][:3]
|
||||||
|
|
||||||
def get_normal_queryset(self):
|
def get_normal_queryset(self):
|
||||||
queryset = Problem.get_visible_problems(self.request.user)
|
queryset = Problem.get_visible_problems(self.request.user)
|
||||||
|
@ -599,9 +600,14 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
|
||||||
self.org_query = [self.request.organization.id]
|
self.org_query = [self.request.organization.id]
|
||||||
if self.org_query:
|
if self.org_query:
|
||||||
self.org_query = self.get_org_query(self.org_query)
|
self.org_query = self.get_org_query(self.org_query)
|
||||||
|
contest_problems = (
|
||||||
|
Contest.objects.filter(organizations__in=self.org_query)
|
||||||
|
.select_related("problems")
|
||||||
|
.values_list("contest_problems__problem__id")
|
||||||
|
.distinct()
|
||||||
|
)
|
||||||
queryset = queryset.filter(
|
queryset = queryset.filter(
|
||||||
Q(organizations__in=self.org_query)
|
Q(organizations__in=self.org_query) | Q(id__in=contest_problems)
|
||||||
| Q(contests__contest__organizations__in=self.org_query)
|
|
||||||
)
|
)
|
||||||
if self.author_query:
|
if self.author_query:
|
||||||
queryset = queryset.filter(authors__in=self.author_query)
|
queryset = queryset.filter(authors__in=self.author_query)
|
||||||
|
|
|
@ -52,7 +52,7 @@ from judge.utils.timedelta import nice_repr
|
||||||
def submission_related(queryset):
|
def submission_related(queryset):
|
||||||
return queryset.select_related("user", "problem", "language").only(
|
return queryset.select_related("user", "problem", "language").only(
|
||||||
"id",
|
"id",
|
||||||
"user_id",
|
"user__id",
|
||||||
"problem__name",
|
"problem__name",
|
||||||
"problem__code",
|
"problem__code",
|
||||||
"problem__is_public",
|
"problem__is_public",
|
||||||
|
@ -67,7 +67,8 @@ def submission_related(queryset):
|
||||||
"case_points",
|
"case_points",
|
||||||
"case_total",
|
"case_total",
|
||||||
"current_testcase",
|
"current_testcase",
|
||||||
"contest_object",
|
"contest_object__key",
|
||||||
|
"contest_object__name",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ ul.problem-list {
|
||||||
|
|
||||||
.organization-tags {
|
.organization-tags {
|
||||||
padding-left: 0.75em;
|
padding-left: 0.75em;
|
||||||
vertical-align: middle;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.organization-tag {
|
.organization-tag {
|
||||||
|
@ -183,6 +183,7 @@ ul.problem-list {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
color: initial;
|
color: initial;
|
||||||
|
min-height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.organization-tag a {
|
.organization-tag a {
|
||||||
|
|
|
@ -193,7 +193,7 @@
|
||||||
{% macro contest_join(contest, request) %}
|
{% macro contest_join(contest, request) %}
|
||||||
{% if request.in_contest and request.participation.contest == contest %}
|
{% if request.in_contest and request.participation.contest == contest %}
|
||||||
<button class="small" disabled>{{ _('In contest') }}</button>
|
<button class="small" disabled>{{ _('In contest') }}</button>
|
||||||
{% elif request.profile in contest.authors.all() or request.profile in contest.curators.all() or request.profile in contest.testers.all() %}
|
{% elif request.profile.id in contest.editor_ids or request.profile.id in contest.tester_ids %}
|
||||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="submit" class="unselectable button full small"
|
<input type="submit" class="unselectable button full small"
|
||||||
|
|
Loading…
Reference in a new issue