NDOJ/judge/views/submission.py

852 lines
30 KiB
Python
Raw Normal View History

2020-01-21 06:35:58 +00:00
import json
2020-01-22 01:18:43 +00:00
import os.path
import zipfile
2020-01-21 06:35:58 +00:00
from operator import attrgetter
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.cache import cache
2020-01-22 01:18:43 +00:00
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import PermissionDenied
from django.db.models import Prefetch
from django.db.models import Q
from django.http import Http404
from django.http import HttpResponse
from django.http import HttpResponseBadRequest
from django.http import HttpResponseRedirect
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import render
2022-06-01 05:28:56 +00:00
from django.template.defaultfilters import floatformat
2020-01-21 06:35:58 +00:00
from django.urls import reverse
from django.utils import timezone
from django.utils.functional import cached_property
2020-01-22 01:18:43 +00:00
from django.utils.html import escape
from django.utils.html import format_html
2020-01-21 06:35:58 +00:00
from django.utils.safestring import mark_safe
2020-01-22 01:18:43 +00:00
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy
2020-01-21 06:35:58 +00:00
from django.views.decorators.http import require_POST
2020-01-22 01:18:43 +00:00
from django.views.generic import DetailView
from django.views.generic import ListView
2020-01-21 06:35:58 +00:00
from judge import event_poster as event
from judge.highlight_code import highlight_code
2020-01-22 01:18:43 +00:00
from judge.models import Contest
from judge.models import Language
from judge.models import Problem
from judge.models import ProblemTestCase
from judge.models import ProblemTranslation
from judge.models import Profile
from judge.models import Submission
from judge.utils.problems import get_result_data
from judge.utils.problems import user_authored_ids
from judge.utils.problems import user_completed_ids
from judge.utils.problems import user_editable_ids
2021-06-01 20:34:42 +00:00
from judge.utils.problem_data import get_problem_case
2021-05-24 20:00:36 +00:00
from judge.utils.raw_sql import join_sql_subquery, use_straight_join
2020-01-22 01:18:43 +00:00
from judge.utils.views import DiggPaginatorMixin
from judge.utils.views import TitleMixin
2022-06-01 05:28:56 +00:00
from judge.utils.timedelta import nice_repr
2020-01-21 17:42:16 +00:00
2020-01-21 06:35:58 +00:00
2022-10-29 03:34:12 +00:00
MAX_NUMBER_OF_QUERY_SUBMISSIONS = 50000
2020-01-21 06:35:58 +00:00
def submission_related(queryset):
2022-05-14 17:57:27 +00:00
return queryset.select_related("user__user", "problem", "language").only(
"id",
"user__user__username",
"user__display_rank",
"user__rating",
"problem__name",
"problem__code",
"problem__is_public",
"language__short_name",
"language__key",
"date",
"time",
"memory",
"points",
"result",
"status",
"case_points",
"case_total",
"current_testcase",
"contest_object",
)
2020-01-21 06:35:58 +00:00
class SubmissionMixin(object):
model = Submission
2022-05-14 17:57:27 +00:00
context_object_name = "submission"
pk_url_kwarg = "submission"
2020-01-21 06:35:58 +00:00
class SubmissionDetailBase(LoginRequiredMixin, TitleMixin, SubmissionMixin, DetailView):
def get_object(self, queryset=None):
submission = super(SubmissionDetailBase, self).get_object(queryset)
profile = self.request.profile
problem = submission.problem
2022-05-14 17:57:27 +00:00
if self.request.user.has_perm("judge.view_all_submission"):
2020-01-21 06:35:58 +00:00
return submission
2022-06-18 07:32:37 +00:00
if problem.is_public and self.request.user.has_perm(
"judge.view_public_submission"
):
2022-06-16 07:46:29 +00:00
return submission
2020-01-21 06:35:58 +00:00
if submission.user_id == profile.id:
return submission
if problem.is_editor(profile):
return submission
if problem.is_public or problem.testers.filter(id=profile.id).exists():
2022-05-14 17:57:27 +00:00
if Submission.objects.filter(
user_id=profile.id,
result="AC",
problem_id=problem.id,
points=problem.points,
).exists():
2020-01-21 06:35:58 +00:00
return submission
2022-05-14 17:57:27 +00:00
if hasattr(
submission, "contest"
) and submission.contest.participation.contest.is_editable_by(
self.request.user
):
2022-04-29 19:36:26 +00:00
return submission
2022-05-14 17:57:27 +00:00
2020-01-21 06:35:58 +00:00
raise PermissionDenied()
def get_title(self):
submission = self.object
2022-05-14 17:57:27 +00:00
return _("Submission of %(problem)s by %(user)s") % {
"problem": submission.problem.translated_name(self.request.LANGUAGE_CODE),
"user": submission.user.user.username,
2020-01-21 06:35:58 +00:00
}
def get_content_title(self):
submission = self.object
2022-05-14 17:57:27 +00:00
return mark_safe(
escape(_("Submission of %(problem)s by %(user)s"))
% {
"problem": format_html(
'<a href="{0}">{1}</a>',
reverse("problem_detail", args=[submission.problem.code]),
submission.problem.translated_name(self.request.LANGUAGE_CODE),
),
"user": format_html(
'<a href="{0}">{1}</a>',
reverse("user_page", args=[submission.user.user.username]),
submission.user.user.username,
),
}
)
2020-01-21 06:35:58 +00:00
class SubmissionSource(SubmissionDetailBase):
2022-05-14 17:57:27 +00:00
template_name = "submission/source.html"
2020-01-21 06:35:58 +00:00
def get_queryset(self):
2022-05-14 17:57:27 +00:00
return super().get_queryset().select_related("source")
2020-01-21 06:35:58 +00:00
def get_context_data(self, **kwargs):
context = super(SubmissionSource, self).get_context_data(**kwargs)
submission = self.object
2022-05-14 17:57:27 +00:00
context["raw_source"] = submission.source.source.rstrip("\n")
context["highlighted_source"] = highlight_code(
submission.source.source, submission.language.pygments, linenos=False
)
2020-01-21 06:35:58 +00:00
return context
def make_batch(batch, cases):
2022-05-14 17:57:27 +00:00
result = {"id": batch, "cases": cases}
2020-01-21 06:35:58 +00:00
if batch:
2022-05-14 17:57:27 +00:00
result["points"] = min(map(attrgetter("points"), cases))
result["total"] = max(map(attrgetter("total"), cases))
2020-01-21 06:35:58 +00:00
return result
def group_test_cases(cases):
result = []
buf = []
last = None
for case in cases:
if case.batch != last and buf:
result.append(make_batch(last, buf))
buf = []
buf.append(case)
last = case.batch
if buf:
result.append(make_batch(last, buf))
return result
2021-05-25 04:22:56 +00:00
def get_cases_data(submission):
2022-05-14 17:57:27 +00:00
testcases = ProblemTestCase.objects.filter(dataset=submission.problem).order_by(
"order"
)
if submission.is_pretested:
2020-05-29 20:46:17 +00:00
testcases = testcases.filter(is_pretest=True)
2021-05-25 04:22:56 +00:00
files = []
for case in testcases:
2022-05-14 17:57:27 +00:00
if case.input_file:
files.append(case.input_file)
if case.output_file:
files.append(case.output_file)
2021-05-25 04:22:56 +00:00
case_data = get_problem_case(submission.problem, files)
2020-05-29 20:46:17 +00:00
problem_data = {}
2021-05-26 17:10:19 +00:00
count = 0
for case in testcases:
2022-05-14 17:57:27 +00:00
if case.type != "C":
continue
2021-05-26 17:10:19 +00:00
count += 1
problem_data[count] = {
2022-05-14 17:57:27 +00:00
"input": case_data[case.input_file] if case.input_file else "",
"answer": case_data[case.output_file] if case.output_file else "",
}
2020-01-21 17:42:16 +00:00
return problem_data
2020-01-22 01:18:43 +00:00
2020-01-21 06:35:58 +00:00
class SubmissionStatus(SubmissionDetailBase):
2022-05-14 17:57:27 +00:00
template_name = "submission/status.html"
2020-01-21 06:35:58 +00:00
def access_testcases_in_contest(self):
contest = self.object.contest_or_none
if contest is None:
return False
if contest.problem.problem.is_editable_by(self.request.user):
return True
if contest.problem.contest.is_in_contest(self.request.user):
return False
if contest.participation.ended:
return True
return False
2020-01-21 06:35:58 +00:00
def get_context_data(self, **kwargs):
context = super(SubmissionStatus, self).get_context_data(**kwargs)
submission = self.object
2022-05-14 17:57:27 +00:00
context["last_msg"] = event.last()
context["batches"] = group_test_cases(submission.test_cases.all())
context["time_limit"] = submission.problem.time_limit
context["can_see_testcases"] = False
2022-08-19 03:47:41 +00:00
context["raw_source"] = submission.source.source.rstrip("\n")
context["highlighted_source"] = highlight_code(
submission.source.source, submission.language.pygments, linenos=False
)
2022-05-14 17:57:27 +00:00
2020-08-05 13:31:43 +00:00
contest = submission.contest_or_none
prefix_length = 0
can_see_testcases = self.access_testcases_in_contest()
2022-05-14 17:57:27 +00:00
if contest is not None:
2020-08-05 13:31:43 +00:00
prefix_length = contest.problem.output_prefix_override
2022-05-14 17:57:27 +00:00
if contest is None or prefix_length > 0 or can_see_testcases:
2022-05-14 17:57:27 +00:00
context["cases_data"] = get_cases_data(submission)
context["can_see_testcases"] = True
2020-01-21 06:35:58 +00:00
try:
2020-01-21 17:42:16 +00:00
lang_limit = submission.problem.language_limits.get(
2022-05-14 17:57:27 +00:00
language=submission.language
)
2020-01-21 06:35:58 +00:00
except ObjectDoesNotExist:
pass
else:
2022-05-14 17:57:27 +00:00
context["time_limit"] = lang_limit.time_limit
2020-01-21 06:35:58 +00:00
return context
class SubmissionTestCaseQuery(SubmissionStatus):
2022-05-14 17:57:27 +00:00
template_name = "submission/status-testcases.html"
2020-01-21 06:35:58 +00:00
def get(self, request, *args, **kwargs):
2022-05-14 17:57:27 +00:00
if "id" not in request.GET or not request.GET["id"].isdigit():
2020-01-21 06:35:58 +00:00
return HttpResponseBadRequest()
2020-01-21 17:42:16 +00:00
self.kwargs[self.pk_url_kwarg] = kwargs[self.pk_url_kwarg] = int(
2022-05-14 17:57:27 +00:00
request.GET["id"]
)
2020-01-21 06:35:58 +00:00
return super(SubmissionTestCaseQuery, self).get(request, *args, **kwargs)
class SubmissionSourceRaw(SubmissionSource):
def get(self, request, *args, **kwargs):
submission = self.get_object()
2022-05-14 17:57:27 +00:00
return HttpResponse(submission.source.source, content_type="text/plain")
2020-01-21 06:35:58 +00:00
@require_POST
def abort_submission(request, submission):
submission = get_object_or_404(Submission, id=int(submission))
2020-12-01 23:18:17 +00:00
# if (not request.user.is_authenticated or (submission.was_rejudged or (request.profile != submission.user)) and
# not request.user.has_perm('abort_any_submission')):
# raise PermissionDenied()
2022-05-14 17:57:27 +00:00
if not request.user.is_authenticated or not request.user.has_perm(
"abort_any_submission"
):
2020-01-21 06:35:58 +00:00
raise PermissionDenied()
submission.abort()
2022-05-14 17:57:27 +00:00
return HttpResponseRedirect(reverse("submission_status", args=(submission.id,)))
2020-01-21 06:35:58 +00:00
class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
model = Submission
paginate_by = 50
show_problem = True
2022-05-14 17:57:27 +00:00
title = gettext_lazy("All submissions")
content_title = gettext_lazy("All submissions")
2022-06-06 16:36:35 +00:00
page_type = "all_submissions_list"
2022-05-14 17:57:27 +00:00
template_name = "submission/list.html"
context_object_name = "submissions"
2020-01-21 06:35:58 +00:00
first_page_href = None
def get_result_data(self):
result = self._get_result_data()
2022-05-14 17:57:27 +00:00
for category in result["categories"]:
category["name"] = _(category["name"])
2020-01-21 06:35:58 +00:00
return result
def _get_result_data(self):
2022-10-29 03:34:12 +00:00
return get_result_data(self._get_queryset().order_by())
2020-01-21 06:35:58 +00:00
def access_check(self, request):
pass
@cached_property
def in_contest(self):
2022-05-14 17:57:27 +00:00
return (
self.request.user.is_authenticated
and self.request.profile.current_contest is not None
2022-01-10 11:13:46 +00:00
and self.request.in_contest_mode
2022-05-14 17:57:27 +00:00
)
2020-01-21 06:35:58 +00:00
@cached_property
def contest(self):
return self.request.profile.current_contest.contest
2022-10-29 03:34:12 +00:00
def _get_entire_queryset(self):
2020-01-21 06:35:58 +00:00
queryset = Submission.objects.all()
use_straight_join(queryset)
2022-05-14 17:57:27 +00:00
queryset = submission_related(queryset.order_by("-id"))
2020-01-21 06:35:58 +00:00
if self.show_problem:
2022-05-14 17:57:27 +00:00
queryset = queryset.prefetch_related(
Prefetch(
"problem__translations",
queryset=ProblemTranslation.objects.filter(
language=self.request.LANGUAGE_CODE
),
to_attr="_trans",
)
)
2020-01-21 06:35:58 +00:00
if self.in_contest:
2021-05-24 20:00:36 +00:00
queryset = queryset.filter(contest_object=self.contest)
if not self.contest.can_see_full_scoreboard(self.request.user):
queryset = queryset.filter(user=self.request.profile)
2020-01-21 06:35:58 +00:00
else:
2022-05-14 17:57:27 +00:00
queryset = queryset.select_related("contest_object").defer(
"contest_object__description"
)
2020-01-21 06:35:58 +00:00
# This is not technically correct since contest organizers *should* see these, but
# the join would be far too messy
2022-05-14 17:57:27 +00:00
if not self.request.user.has_perm("judge.see_private_contest"):
2021-05-24 20:00:36 +00:00
# Show submissions for any contest you can edit or visible scoreboard
2022-05-14 17:57:27 +00:00
contest_queryset = Contest.objects.filter(
Q(authors=self.request.profile)
| Q(curators=self.request.profile)
| Q(scoreboard_visibility=Contest.SCOREBOARD_VISIBLE)
| Q(end_time__lt=timezone.now())
).distinct()
queryset = queryset.filter(
Q(user=self.request.profile)
| Q(contest_object__in=contest_queryset)
| Q(contest_object__isnull=True)
)
2020-01-21 06:35:58 +00:00
if self.selected_languages:
2020-01-21 17:42:16 +00:00
queryset = queryset.filter(
2022-05-14 17:57:27 +00:00
language__in=Language.objects.filter(key__in=self.selected_languages)
)
2020-01-21 06:35:58 +00:00
if self.selected_statuses:
2022-05-30 23:23:31 +00:00
queryset = queryset.filter(
2022-06-01 05:28:56 +00:00
Q(result__in=self.selected_statuses)
| Q(status__in=self.selected_statuses)
2022-05-30 23:23:31 +00:00
)
2020-01-21 06:35:58 +00:00
return queryset
2022-10-29 03:34:12 +00:00
def _get_queryset(self):
queryset = self._get_entire_queryset()
2020-01-21 06:35:58 +00:00
if not self.in_contest:
2021-05-24 20:00:36 +00:00
join_sql_subquery(
queryset,
2022-05-14 17:57:27 +00:00
subquery=str(
Problem.get_visible_problems(self.request.user)
.distinct()
.only("id")
.query
),
2021-05-24 20:00:36 +00:00
params=[],
2022-05-14 17:57:27 +00:00
join_fields=[("problem_id", "id")],
alias="visible_problems",
2022-11-01 01:43:06 +00:00
related_model=Problem,
2021-05-24 20:00:36 +00:00
)
2020-01-21 06:35:58 +00:00
return queryset
2022-10-29 03:34:12 +00:00
def get_queryset(self):
return self._get_queryset()[:MAX_NUMBER_OF_QUERY_SUBMISSIONS]
2020-01-21 06:35:58 +00:00
def get_my_submissions_page(self):
return None
def get_all_submissions_page(self):
2022-05-14 17:57:27 +00:00
return reverse("all_submissions")
2020-01-21 06:35:58 +00:00
def get_searchable_status_codes(self):
2022-05-30 23:23:31 +00:00
all_statuses = list(Submission.RESULT)
all_statuses.extend([i for i in Submission.STATUS if i not in all_statuses])
hidden_codes = ["SC", "D", "G"]
2020-01-21 06:35:58 +00:00
if not self.request.user.is_superuser and not self.request.user.is_staff:
2022-05-14 17:57:27 +00:00
hidden_codes += ["IE"]
2022-06-01 05:28:56 +00:00
return [(key, value) for key, value in all_statuses if key not in hidden_codes]
2020-01-21 06:35:58 +00:00
def get_context_data(self, **kwargs):
context = super(SubmissionsListBase, self).get_context_data(**kwargs)
authenticated = self.request.user.is_authenticated
2022-05-14 17:57:27 +00:00
context["dynamic_update"] = False
context["show_problem"] = self.show_problem
context["completed_problem_ids"] = (
user_completed_ids(self.request.profile) if authenticated else []
)
context["authored_problem_ids"] = (
user_authored_ids(self.request.profile) if authenticated else []
)
context["editable_problem_ids"] = (
user_editable_ids(self.request.profile) if authenticated else []
)
context["all_languages"] = Language.objects.all().values_list("key", "name")
context["selected_languages"] = self.selected_languages
context["all_statuses"] = self.get_searchable_status_codes()
context["selected_statuses"] = self.selected_statuses
context["results_json"] = mark_safe(json.dumps(self.get_result_data()))
context["results_colors_json"] = mark_safe(
json.dumps(settings.DMOJ_STATS_SUBMISSION_RESULT_COLORS)
)
context["page_suffix"] = suffix = (
("?" + self.request.GET.urlencode()) if self.request.GET else ""
)
context["first_page_href"] = (self.first_page_href or ".") + suffix
context["my_submissions_link"] = self.get_my_submissions_page()
context["all_submissions_link"] = self.get_all_submissions_page()
2022-06-06 16:36:35 +00:00
context["page_type"] = self.page_type
2022-04-14 20:40:48 +00:00
2020-01-21 06:35:58 +00:00
return context
def get(self, request, *args, **kwargs):
check = self.access_check(request)
if check is not None:
return check
2022-05-14 17:57:27 +00:00
self.selected_languages = set(request.GET.getlist("language"))
self.selected_statuses = set(request.GET.getlist("status"))
2020-01-21 06:35:58 +00:00
2022-05-14 17:57:27 +00:00
if "results" in request.GET:
2020-01-21 06:35:58 +00:00
return JsonResponse(self.get_result_data())
return super(SubmissionsListBase, self).get(request, *args, **kwargs)
class UserMixin(object):
def get(self, request, *args, **kwargs):
2022-05-14 17:57:27 +00:00
if "user" not in kwargs:
raise ImproperlyConfigured("Must pass a user")
self.profile = get_object_or_404(Profile, user__username=kwargs["user"])
self.username = kwargs["user"]
2020-01-21 06:35:58 +00:00
return super(UserMixin, self).get(request, *args, **kwargs)
class ConditionalUserTabMixin(object):
def get_context_data(self, **kwargs):
2022-05-14 17:57:27 +00:00
context = super(ConditionalUserTabMixin, self).get_context_data(**kwargs)
2020-01-21 06:35:58 +00:00
if self.request.user.is_authenticated and self.request.profile == self.profile:
2022-06-06 16:36:35 +00:00
context["page_type"] = "my_submissions_tab"
2020-01-21 06:35:58 +00:00
else:
2022-06-06 16:36:35 +00:00
context["page_type"] = "user_submissions_tab"
2022-05-14 17:57:27 +00:00
context["tab_username"] = self.profile.user.username
2020-01-21 06:35:58 +00:00
return context
class AllUserSubmissions(ConditionalUserTabMixin, UserMixin, SubmissionsListBase):
2022-10-29 03:34:12 +00:00
def _get_queryset(self):
2022-05-14 17:57:27 +00:00
return (
super(AllUserSubmissions, self)
2022-10-29 03:34:12 +00:00
._get_queryset()
2022-05-14 17:57:27 +00:00
.filter(user_id=self.profile.id)
)
2020-01-21 06:35:58 +00:00
def get_title(self):
if self.request.user.is_authenticated and self.request.profile == self.profile:
2022-05-14 17:57:27 +00:00
return _("All my submissions")
return _("All submissions by %s") % self.username
2020-01-21 06:35:58 +00:00
def get_content_title(self):
if self.request.user.is_authenticated and self.request.profile == self.profile:
2022-05-14 17:57:27 +00:00
return format_html("All my submissions")
return format_html(
'All submissions by <a href="{1}">{0}</a>',
self.username,
reverse("user_page", args=[self.username]),
)
2020-01-21 06:35:58 +00:00
def get_my_submissions_page(self):
if self.request.user.is_authenticated:
2022-05-14 17:57:27 +00:00
return reverse(
"all_user_submissions", kwargs={"user": self.request.user.username}
)
2020-01-21 06:35:58 +00:00
def get_context_data(self, **kwargs):
context = super(AllUserSubmissions, self).get_context_data(**kwargs)
2022-05-14 17:57:27 +00:00
context["dynamic_update"] = context["page_obj"].number == 1
context["dynamic_user_id"] = self.profile.id
context["last_msg"] = event.last()
2020-01-21 06:35:58 +00:00
return context
class ProblemSubmissionsBase(SubmissionsListBase):
show_problem = False
dynamic_update = True
2022-06-01 19:31:20 +00:00
check_contest_in_access_check = False
2020-01-21 06:35:58 +00:00
2022-10-29 03:34:12 +00:00
def _get_queryset(self):
2022-05-14 17:57:27 +00:00
if (
self.in_contest
and not self.contest.contest_problems.filter(
problem_id=self.problem.id
).exists()
):
2020-01-21 06:35:58 +00:00
raise Http404()
2022-05-14 17:57:27 +00:00
return (
super(ProblemSubmissionsBase, self)
2022-10-29 03:34:12 +00:00
._get_entire_queryset()
2022-05-14 17:57:27 +00:00
.filter(problem_id=self.problem.id)
)
2020-01-21 06:35:58 +00:00
def get_title(self):
2022-05-14 17:57:27 +00:00
return _("All submissions for %s") % self.problem_name
2020-01-21 06:35:58 +00:00
def get_content_title(self):
2022-05-14 17:57:27 +00:00
return format_html(
'All submissions for <a href="{1}">{0}</a>',
self.problem_name,
reverse("problem_detail", args=[self.problem.code]),
)
2020-01-21 06:35:58 +00:00
def access_check_contest(self, request):
2022-06-03 03:14:01 +00:00
if self.in_contest:
if not self.contest.can_see_own_scoreboard(request.user):
raise Http404()
if not self.contest.is_accessible_by(request.user):
raise Http404()
2020-01-21 06:35:58 +00:00
2022-06-01 19:31:20 +00:00
def access_check(self, request):
2020-01-21 06:35:58 +00:00
if self.check_contest_in_access_check:
self.access_check_contest(request)
2022-06-01 19:31:20 +00:00
else:
2022-06-02 05:20:45 +00:00
is_own = hasattr(self, "is_own") and self.is_own
if not is_own and not self.problem.is_accessible_by(
request.user, request.in_contest_mode
):
2022-06-01 19:31:20 +00:00
raise Http404()
2020-01-21 06:35:58 +00:00
def get(self, request, *args, **kwargs):
2022-05-14 17:57:27 +00:00
if "problem" not in kwargs:
raise ImproperlyConfigured(_("Must pass a problem"))
self.problem = get_object_or_404(Problem, code=kwargs["problem"])
self.problem_name = self.problem.translated_name(self.request.LANGUAGE_CODE)
2020-01-21 06:35:58 +00:00
return super(ProblemSubmissionsBase, self).get(request, *args, **kwargs)
def get_all_submissions_page(self):
2022-05-14 17:57:27 +00:00
return reverse(
"chronological_submissions", kwargs={"problem": self.problem.code}
)
2020-01-21 06:35:58 +00:00
def get_context_data(self, **kwargs):
2022-05-14 17:57:27 +00:00
context = super(ProblemSubmissionsBase, self).get_context_data(**kwargs)
2020-01-21 06:35:58 +00:00
if self.dynamic_update:
2022-05-14 17:57:27 +00:00
context["dynamic_update"] = context["page_obj"].number == 1
context["dynamic_problem_id"] = self.problem.id
context["last_msg"] = event.last()
context["best_submissions_link"] = reverse(
"ranked_submissions", kwargs={"problem": self.problem.code}
)
2020-01-21 06:35:58 +00:00
return context
class ProblemSubmissions(ProblemSubmissionsBase):
def get_my_submissions_page(self):
if self.request.user.is_authenticated:
2022-05-14 17:57:27 +00:00
return reverse(
"user_submissions",
kwargs={
"problem": self.problem.code,
"user": self.request.user.username,
},
)
2020-01-21 06:35:58 +00:00
class UserProblemSubmissions(ConditionalUserTabMixin, UserMixin, ProblemSubmissions):
check_contest_in_access_check = False
@cached_property
def is_own(self):
2022-05-14 17:57:27 +00:00
return (
self.request.user.is_authenticated and self.request.profile == self.profile
)
2020-01-21 06:35:58 +00:00
def access_check(self, request):
super(UserProblemSubmissions, self).access_check(request)
if not self.is_own:
self.access_check_contest(request)
2022-10-29 03:34:12 +00:00
def _get_queryset(self):
2022-05-14 17:57:27 +00:00
return (
super(UserProblemSubmissions, self)
2022-10-29 03:34:12 +00:00
._get_queryset()
2022-05-14 17:57:27 +00:00
.filter(user_id=self.profile.id)
)
2020-01-21 06:35:58 +00:00
def get_title(self):
if self.is_own:
2022-05-14 17:57:27 +00:00
return _("My submissions for %(problem)s") % {"problem": self.problem_name}
return _("%(user)s's submissions for %(problem)s") % {
"user": self.username,
"problem": self.problem_name,
}
2020-01-21 06:35:58 +00:00
def get_content_title(self):
if self.request.user.is_authenticated and self.request.profile == self.profile:
2022-05-14 17:57:27 +00:00
return format_html(
"""My submissions for <a href="{3}">{2}</a>""",
self.username,
reverse("user_page", args=[self.username]),
self.problem_name,
reverse("problem_detail", args=[self.problem.code]),
)
return format_html(
"""<a href="{1}">{0}</a>'s submissions for <a href="{3}">{2}</a>""",
self.username,
reverse("user_page", args=[self.username]),
self.problem_name,
reverse("problem_detail", args=[self.problem.code]),
)
2020-01-21 06:35:58 +00:00
def get_context_data(self, **kwargs):
2022-05-14 17:57:27 +00:00
context = super(UserProblemSubmissions, self).get_context_data(**kwargs)
context["dynamic_user_id"] = self.profile.id
2020-01-21 06:35:58 +00:00
return context
def single_submission(request, submission_id, show_problem=True):
request.no_profile_update = True
authenticated = request.user.is_authenticated
2022-05-14 17:57:27 +00:00
submission = get_object_or_404(
submission_related(Submission.objects.all()), id=int(submission_id)
)
2020-01-21 06:35:58 +00:00
if not submission.problem.is_accessible_by(request.user):
raise Http404()
2022-05-14 17:57:27 +00:00
return render(
request,
"submission/row.html",
{
"submission": submission,
"authored_problem_ids": user_authored_ids(request.profile)
if authenticated
else [],
"completed_problem_ids": user_completed_ids(request.profile)
if authenticated
else [],
"editable_problem_ids": user_editable_ids(request.profile)
if authenticated
else [],
"show_problem": show_problem,
"problem_name": show_problem
and submission.problem.translated_name(request.LANGUAGE_CODE),
"profile_id": request.profile.id if authenticated else 0,
},
)
2020-01-21 06:35:58 +00:00
def single_submission_query(request):
request.no_profile_update = True
2022-05-14 17:57:27 +00:00
if "id" not in request.GET or not request.GET["id"].isdigit():
2020-01-21 06:35:58 +00:00
return HttpResponseBadRequest()
try:
2022-05-14 17:57:27 +00:00
show_problem = int(request.GET.get("show_problem", "1"))
2020-01-21 06:35:58 +00:00
except ValueError:
return HttpResponseBadRequest()
2022-05-14 17:57:27 +00:00
return single_submission(request, int(request.GET["id"]), bool(show_problem))
2020-01-21 06:35:58 +00:00
class AllSubmissions(SubmissionsListBase):
stats_update_interval = 3600
def get_my_submissions_page(self):
if self.request.user.is_authenticated:
2022-05-14 17:57:27 +00:00
return reverse(
"all_user_submissions", kwargs={"user": self.request.user.username}
)
2020-01-21 06:35:58 +00:00
def get_context_data(self, **kwargs):
context = super(AllSubmissions, self).get_context_data(**kwargs)
2022-05-14 17:57:27 +00:00
context["dynamic_update"] = context["page_obj"].number == 1
context["last_msg"] = event.last()
context["stats_update_interval"] = self.stats_update_interval
2020-01-21 06:35:58 +00:00
return context
def _get_result_data(self):
if self.in_contest or self.selected_languages or self.selected_statuses:
return super(AllSubmissions, self)._get_result_data()
2022-05-14 17:57:27 +00:00
key = "global_submission_result_data"
2020-01-21 06:35:58 +00:00
result = cache.get(key)
if result:
return result
result = super(AllSubmissions, self)._get_result_data()
cache.set(key, result, self.stats_update_interval)
return result
class ForceContestMixin(object):
@property
def in_contest(self):
return True
@property
def contest(self):
return self._contest
def access_check(self, request):
super(ForceContestMixin, self).access_check(request)
2022-05-14 17:57:27 +00:00
if not request.user.has_perm("judge.see_private_contest"):
2020-01-21 06:35:58 +00:00
if not self.contest.is_visible:
raise Http404()
2022-05-14 17:57:27 +00:00
if (
self.contest.start_time is not None
and self.contest.start_time > timezone.now()
):
2020-01-21 06:35:58 +00:00
raise Http404()
def get_problem_number(self, problem):
2022-05-14 17:57:27 +00:00
return (
self.contest.contest_problems.select_related("problem")
.get(problem=problem)
.order
)
2020-01-21 06:35:58 +00:00
def get(self, request, *args, **kwargs):
2022-05-14 17:57:27 +00:00
if "contest" not in kwargs:
raise ImproperlyConfigured(_("Must pass a contest"))
self._contest = get_object_or_404(Contest, key=kwargs["contest"])
2020-01-21 06:35:58 +00:00
return super(ForceContestMixin, self).get(request, *args, **kwargs)
class UserContestSubmissions(ForceContestMixin, UserProblemSubmissions):
2022-06-01 19:31:20 +00:00
check_contest_in_access_check = True
2020-01-21 06:35:58 +00:00
def get_title(self):
if self.problem.is_accessible_by(self.request.user):
2022-05-14 17:57:27 +00:00
return "%s's submissions for %s in %s" % (
self.username,
self.problem_name,
self.contest.name,
)
2020-01-21 06:35:58 +00:00
return "%s's submissions for problem %s in %s" % (
2022-05-14 17:57:27 +00:00
self.username,
self.get_problem_number(self.problem),
self.contest.name,
)
2020-01-21 06:35:58 +00:00
2022-06-01 16:59:58 +00:00
def access_check(self, request):
super(UserContestSubmissions, self).access_check(request)
if not self.contest.users.filter(user_id=self.profile.id).exists():
raise Http404()
2020-01-21 06:35:58 +00:00
def get_content_title(self):
if self.problem.is_accessible_by(self.request.user):
2022-05-14 17:57:27 +00:00
return format_html(
_(
'<a href="{1}">{0}</a>\'s submissions for '
'<a href="{3}">{2}</a> in <a href="{5}">{4}</a>'
),
self.username,
reverse("user_page", args=[self.username]),
self.problem_name,
reverse("problem_detail", args=[self.problem.code]),
self.contest.name,
reverse("contest_view", args=[self.contest.key]),
)
return format_html(
_(
'<a href="{1}">{0}</a>\'s submissions for '
'problem {2} in <a href="{4}">{3}</a>'
),
self.username,
reverse("user_page", args=[self.username]),
self.get_problem_number(self.problem),
self.contest.name,
reverse("contest_view", args=[self.contest.key]),
)
2022-06-01 05:28:56 +00:00
class UserContestSubmissionsAjax(UserContestSubmissions):
template_name = "submission/user-ajax.html"
def contest_time(self, s):
2022-11-01 18:52:14 +00:00
if s.contest.participation.live:
2022-11-01 18:45:58 +00:00
if self.contest.time_limit:
return s.date - s.contest.participation.real_start
return s.date - self.contest.start_time
2022-06-01 05:28:56 +00:00
return None
def get_context_data(self, **kwargs):
context = super(UserContestSubmissionsAjax, self).get_context_data(**kwargs)
context["contest"] = self.contest
context["problem"] = self.problem
context["profile"] = self.profile
2022-06-02 05:20:45 +00:00
context["profile_id"] = (
self.request.profile.id if self.request.profile else None
)
2022-06-01 05:28:56 +00:00
contest_problem = self.contest.contest_problems.get(problem=self.problem)
2022-11-01 18:52:14 +00:00
filtered_submissions = []
2022-06-01 05:28:56 +00:00
for s in context["submissions"]:
2022-11-01 18:52:14 +00:00
if not hasattr(s, "contest"):
continue
2022-06-01 05:28:56 +00:00
contest_time = self.contest_time(s)
if contest_time:
s.contest_time = nice_repr(contest_time, "noday")
else:
s.contest_time = None
points = floatformat(s.contest.points, -self.contest.points_precision)
total = floatformat(contest_problem.points, -self.contest.points_precision)
s.display_point = f"{points} / {total}"
2022-11-01 18:52:14 +00:00
filtered_submissions.append(s)
context["submissions"] = filtered_submissions
2022-06-01 05:28:56 +00:00
return context
2022-06-01 19:31:20 +00:00
def get(self, request, *args, **kwargs):
try:
return super(UserContestSubmissionsAjax, self).get(request, *args, **kwargs)
except Http404:
2022-06-02 05:20:45 +00:00
return HttpResponse(_("You don't have permission to access."))