2020-01-21 06:35:58 +00:00
|
|
|
from operator import itemgetter
|
|
|
|
|
2020-11-25 19:21:53 +00:00
|
|
|
import zipfile, tempfile
|
|
|
|
|
2020-01-21 06:35:58 +00:00
|
|
|
from celery.result import AsyncResult
|
|
|
|
from django.contrib import messages
|
|
|
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
2022-05-14 17:57:27 +00:00
|
|
|
from django.http import (
|
|
|
|
Http404,
|
|
|
|
HttpResponse,
|
|
|
|
HttpResponseBadRequest,
|
|
|
|
HttpResponseRedirect,
|
|
|
|
)
|
2020-01-21 06:35:58 +00:00
|
|
|
from django.urls import reverse
|
|
|
|
from django.utils.html import escape, format_html
|
|
|
|
from django.utils.safestring import mark_safe
|
|
|
|
from django.utils.translation import gettext as _, ngettext
|
|
|
|
from django.views.generic import DetailView
|
|
|
|
from django.views.generic.detail import BaseDetailView
|
|
|
|
|
|
|
|
from judge.models import Language, Submission
|
|
|
|
from judge.tasks import apply_submission_filter, rejudge_problem_filter, rescore_problem
|
|
|
|
from judge.utils.celery import redirect_to_task_status
|
|
|
|
from judge.utils.views import TitleMixin
|
|
|
|
from judge.views.problem import ProblemMixin
|
|
|
|
|
|
|
|
|
|
|
|
class ManageProblemSubmissionMixin(ProblemMixin):
|
|
|
|
def get_object(self, queryset=None):
|
|
|
|
problem = super().get_object(queryset)
|
|
|
|
user = self.request.user
|
|
|
|
if not problem.is_subs_manageable_by(user):
|
|
|
|
raise Http404()
|
|
|
|
return problem
|
|
|
|
|
|
|
|
|
|
|
|
class ManageProblemSubmissionActionMixin(ManageProblemSubmissionMixin):
|
|
|
|
def perform_action(self):
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
raise Http404()
|
|
|
|
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
|
|
try:
|
|
|
|
self.object = self.get_object()
|
|
|
|
except Http404:
|
|
|
|
return self.no_such_problem()
|
|
|
|
else:
|
|
|
|
return self.perform_action()
|
|
|
|
|
|
|
|
|
|
|
|
class ManageProblemSubmissionView(TitleMixin, ManageProblemSubmissionMixin, DetailView):
|
2022-05-14 17:57:27 +00:00
|
|
|
template_name = "problem/manage_submission.html"
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
def get_title(self):
|
2022-05-14 17:57:27 +00:00
|
|
|
return _("Managing submissions for %s") % (self.object.name,)
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
def get_content_title(self):
|
2022-05-14 17:57:27 +00:00
|
|
|
return mark_safe(
|
|
|
|
escape(_("Managing submissions for %s"))
|
|
|
|
% (
|
|
|
|
format_html(
|
|
|
|
'<a href="{1}">{0}</a>',
|
|
|
|
self.object.name,
|
|
|
|
reverse("problem_detail", args=[self.object.code]),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super().get_context_data(**kwargs)
|
2022-05-14 17:57:27 +00:00
|
|
|
context["submission_count"] = self.object.submission_set.count()
|
|
|
|
context["languages"] = [
|
|
|
|
(lang_id, short_name or key)
|
|
|
|
for lang_id, key, short_name in Language.objects.values_list(
|
|
|
|
"id", "key", "short_name"
|
|
|
|
)
|
|
|
|
]
|
|
|
|
context["results"] = sorted(map(itemgetter(0), Submission.RESULT))
|
2023-11-29 02:04:02 +00:00
|
|
|
context["current_contest"] = None
|
2023-03-26 03:11:56 +00:00
|
|
|
if (
|
|
|
|
self.request.in_contest_mode
|
|
|
|
and self.object in self.request.participation.contest.problems.all()
|
|
|
|
):
|
2023-11-29 02:04:02 +00:00
|
|
|
context["current_contest"] = self.request.participation.contest
|
2023-03-26 03:11:56 +00:00
|
|
|
|
2020-01-21 06:35:58 +00:00
|
|
|
return context
|
|
|
|
|
|
|
|
|
2022-05-14 17:57:27 +00:00
|
|
|
class BaseActionSubmissionsView(
|
|
|
|
PermissionRequiredMixin, ManageProblemSubmissionActionMixin, BaseDetailView
|
|
|
|
):
|
|
|
|
permission_required = "judge.rejudge_submission_lot"
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
def perform_action(self):
|
2022-05-14 17:57:27 +00:00
|
|
|
if self.request.POST.get("use_range", "off") == "on":
|
2020-01-21 06:35:58 +00:00
|
|
|
try:
|
2022-05-14 17:57:27 +00:00
|
|
|
start = int(self.request.POST.get("start"))
|
|
|
|
end = int(self.request.POST.get("end"))
|
2020-01-21 06:35:58 +00:00
|
|
|
except (KeyError, ValueError):
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
id_range = (start, end)
|
|
|
|
else:
|
|
|
|
id_range = None
|
|
|
|
|
|
|
|
try:
|
2022-05-14 17:57:27 +00:00
|
|
|
languages = list(map(int, self.request.POST.getlist("language")))
|
2023-11-29 02:04:02 +00:00
|
|
|
results = list(map(int, self.request.POST.getlist("result")))
|
|
|
|
contests = list(map(int, self.request.POST.getlist("contest")))
|
2020-01-21 06:35:58 +00:00
|
|
|
except ValueError:
|
|
|
|
return HttpResponseBadRequest()
|
|
|
|
|
2023-11-29 02:04:02 +00:00
|
|
|
return self.generate_response(id_range, languages, results, contests)
|
2020-01-21 06:35:58 +00:00
|
|
|
|
2023-03-26 03:11:56 +00:00
|
|
|
def generate_response(self, id_range, languages, results, contest):
|
2020-01-21 06:35:58 +00:00
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
2020-11-25 19:21:53 +00:00
|
|
|
class ActionSubmissionsView(BaseActionSubmissionsView):
|
2023-03-26 03:11:56 +00:00
|
|
|
def rejudge_response(self, id_range, languages, results, contest):
|
2022-05-14 17:57:27 +00:00
|
|
|
status = rejudge_problem_filter.delay(
|
2023-03-26 03:11:56 +00:00
|
|
|
self.object.id, id_range, languages, results, contest
|
2022-05-14 17:57:27 +00:00
|
|
|
)
|
2020-01-21 06:35:58 +00:00
|
|
|
return redirect_to_task_status(
|
2022-05-14 17:57:27 +00:00
|
|
|
status,
|
|
|
|
message=_("Rejudging selected submissions for %s...") % (self.object.name,),
|
|
|
|
redirect=reverse(
|
|
|
|
"problem_submissions_rejudge_success",
|
|
|
|
args=[self.object.code, status.id],
|
|
|
|
),
|
2020-01-21 06:35:58 +00:00
|
|
|
)
|
|
|
|
|
2023-03-26 03:11:56 +00:00
|
|
|
def download_response(self, id_range, languages, results, contest):
|
2020-11-25 19:21:53 +00:00
|
|
|
queryset = Submission.objects.filter(problem_id=self.object.id)
|
2023-03-26 03:11:56 +00:00
|
|
|
submissions = apply_submission_filter(
|
|
|
|
queryset, id_range, languages, results, contest
|
|
|
|
)
|
2020-11-25 19:21:53 +00:00
|
|
|
|
|
|
|
with tempfile.SpooledTemporaryFile() as tmp:
|
2022-05-14 17:57:27 +00:00
|
|
|
with zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as archive:
|
2020-11-25 19:21:53 +00:00
|
|
|
for submission in submissions:
|
2022-05-14 17:57:27 +00:00
|
|
|
file_name = (
|
|
|
|
str(submission.id)
|
|
|
|
+ "_"
|
|
|
|
+ str(submission.user)
|
|
|
|
+ "."
|
|
|
|
+ str(submission.language.key)
|
|
|
|
)
|
2020-11-25 19:21:53 +00:00
|
|
|
archive.writestr(file_name, submission.source.source)
|
2022-05-14 17:57:27 +00:00
|
|
|
|
2020-11-25 19:21:53 +00:00
|
|
|
# Reset file pointer
|
|
|
|
tmp.seek(0)
|
|
|
|
|
|
|
|
# Write file data to response
|
2022-05-14 17:57:27 +00:00
|
|
|
response = HttpResponse(
|
|
|
|
tmp.read(), content_type="application/x-zip-compressed"
|
|
|
|
)
|
|
|
|
response["Content-Disposition"] = 'attachment; filename="%s"' % (
|
|
|
|
str(self.object.code) + "_submissions.zip"
|
|
|
|
)
|
2020-11-25 19:21:53 +00:00
|
|
|
return response
|
|
|
|
|
2023-03-26 03:11:56 +00:00
|
|
|
def generate_response(self, id_range, languages, results, contest):
|
2022-05-14 17:57:27 +00:00
|
|
|
action = self.request.POST.get("action")
|
|
|
|
if action == "rejudge":
|
2023-03-26 03:11:56 +00:00
|
|
|
return self.rejudge_response(id_range, languages, results, contest)
|
2022-05-14 17:57:27 +00:00
|
|
|
elif action == "download":
|
2023-03-26 03:11:56 +00:00
|
|
|
return self.download_response(id_range, languages, results, contest)
|
2020-11-25 19:21:53 +00:00
|
|
|
else:
|
|
|
|
return Http404()
|
2020-01-21 06:35:58 +00:00
|
|
|
|
2022-05-14 17:57:27 +00:00
|
|
|
|
2020-11-25 19:21:53 +00:00
|
|
|
class PreviewActionSubmissionsView(BaseActionSubmissionsView):
|
2023-03-26 03:11:56 +00:00
|
|
|
def generate_response(self, id_range, languages, results, contest):
|
2022-05-14 17:57:27 +00:00
|
|
|
queryset = apply_submission_filter(
|
2023-03-26 03:11:56 +00:00
|
|
|
self.object.submission_set.all(), id_range, languages, results, contest
|
2022-05-14 17:57:27 +00:00
|
|
|
)
|
2020-01-21 06:35:58 +00:00
|
|
|
return HttpResponse(str(queryset.count()))
|
|
|
|
|
|
|
|
|
|
|
|
class RescoreAllSubmissionsView(ManageProblemSubmissionActionMixin, BaseDetailView):
|
|
|
|
def perform_action(self):
|
|
|
|
status = rescore_problem.delay(self.object.id)
|
|
|
|
return redirect_to_task_status(
|
2022-05-14 17:57:27 +00:00
|
|
|
status,
|
|
|
|
message=_("Rescoring all submissions for %s...") % (self.object.name,),
|
|
|
|
redirect=reverse(
|
|
|
|
"problem_submissions_rescore_success",
|
|
|
|
args=[self.object.code, status.id],
|
|
|
|
),
|
2020-01-21 06:35:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def rejudge_success(request, problem, task_id):
|
|
|
|
count = AsyncResult(task_id).result
|
|
|
|
if not isinstance(count, int):
|
|
|
|
raise Http404()
|
2022-05-14 17:57:27 +00:00
|
|
|
messages.success(
|
|
|
|
request,
|
|
|
|
ngettext(
|
|
|
|
"Successfully scheduled %d submission for rejudging.",
|
|
|
|
"Successfully scheduled %d submissions for rejudging.",
|
|
|
|
count,
|
|
|
|
)
|
|
|
|
% (count,),
|
|
|
|
)
|
|
|
|
return HttpResponseRedirect(reverse("problem_manage_submissions", args=[problem]))
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
def rescore_success(request, problem, task_id):
|
|
|
|
count = AsyncResult(task_id).result
|
|
|
|
if not isinstance(count, int):
|
|
|
|
raise Http404()
|
2022-05-14 17:57:27 +00:00
|
|
|
messages.success(
|
|
|
|
request,
|
|
|
|
ngettext(
|
|
|
|
"%d submission were successfully rescored.",
|
|
|
|
"%d submissions were successfully rescored.",
|
|
|
|
count,
|
|
|
|
)
|
|
|
|
% (count,),
|
|
|
|
)
|
|
|
|
return HttpResponseRedirect(reverse("problem_manage_submissions", args=[problem]))
|