Skip to content
Draft
65 changes: 65 additions & 0 deletions judge/utils/deferred_paginator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from django.core.exceptions import EmptyResultSet
from django.views.generic import ListView

from judge.utils.raw_sql import join_sql_subquery


class DeferredPaginationListView(ListView):
paginated_model = None

def deferred_paginate(self, queryset):
return queryset

def get_context_data(self, *, object_list=None, **kwargs):
"""Get the context for this view."""
queryset = object_list if object_list is not None else self.object_list
page_size = self.get_paginate_by(queryset)
context_object_name = self.get_context_object_name(queryset)
if page_size:
queryset_pks = queryset.values_list('pk', flat=True)
paginator, page, queryset_pks, is_paginated = self.paginate_queryset(
queryset_pks, page_size
)

try:
query, params = queryset_pks.query.sql_with_params()
queryset = self.__class__.paginated_model.objects.all()
join_sql_subquery(
queryset,
subquery=query,
params=list(params),
join_fields=[('id', 'id')],
alias='deferred_object',
related_model=self.__class__.paginated_model,
)
queryset = self.deferred_paginate(queryset)
ordering = self.get_ordering()
if ordering:
if isinstance(ordering, str):
ordering = (ordering,)
queryset = queryset.order_by(*ordering)
except EmptyResultSet:
queryset = []

page.object_list = queryset
context = {
"paginator": paginator,
"page_obj": page,
"is_paginated": is_paginated,
"object_list": queryset,
}
else:
context = {
"paginator": None,
"page_obj": None,
"is_paginated": False,
"object_list": queryset,
}
if context_object_name is not None:
context[context_object_name] = queryset
context.update(kwargs)

context.setdefault("view", self)
if self.extra_context is not None:
context.update(self.extra_context)
return context
2 changes: 1 addition & 1 deletion judge/utils/infinite_paginator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class InfinitePage(collections.abc.Sequence):
This eliminates the need to count the next pages items.
"""
def __init__(self, object_list, number, unfiltered_queryset, page_size, pad_pages, paginator):
self.object_list = list(object_list)
self.object_list = object_list
self.number = number
self.unfiltered_queryset = unfiltered_queryset
self.page_size = page_size
Expand Down
6 changes: 6 additions & 0 deletions judge/views/ranked_submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ def get_queryset(self):
else:
return queryset.order_by('-points', 'time')

def get_ordering(self):
if self.is_contest_scoped:
return ('-contest__points', 'time')
else:
return ('-points', 'time')

def get_title(self):
return _('Best solutions for %s') % self.problem_name

Expand Down
9 changes: 8 additions & 1 deletion judge/views/submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from judge.highlight_code import highlight_code
from judge.models import Contest, Language, Organization, Problem, ProblemTranslation, Profile, Submission
from judge.models.problem import ProblemTestcaseResultAccess, SubmissionSourceAccess
from judge.utils.deferred_paginator import DeferredPaginationListView
from judge.utils.infinite_paginator import InfinitePaginationMixin
from judge.utils.lazy import memo_lazy
from judge.utils.problem_data import get_problem_testcases_data
Expand Down Expand Up @@ -344,7 +345,7 @@ def filter_submissions_by_visible_problems(queryset, user):
)


class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, DeferredPaginationListView):
model = Submission
paginate_by = 50
show_problem = True
Expand All @@ -354,6 +355,8 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
template_name = 'submission/list.html'
context_object_name = 'submissions'
first_page_href = None
paginated_model = Submission
ordering = '-id'

def get_result_data(self):
result = self._get_result_data()
Expand Down Expand Up @@ -382,6 +385,10 @@ def is_contest_scoped(self):
def contest(self):
return self.request.profile.current_contest.contest

def deferred_paginate(self, queryset):
return (queryset.select_related('user__user', 'user__display_badge', 'problem', 'language')
.prefetch_related('contest_object__authors', 'contest_object__curators'))

def _get_queryset(self):
queryset = Submission.objects.all()
use_straight_join(queryset)
Expand Down
Loading