NDOJ/judge/views/problem_manage.py

233 lines
7.8 KiB
Python
Raw Normal View History

2020-01-21 06:35:58 +00:00
from operator import itemgetter
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-03-26 03:11:56 +00:00
context["in_contest"] = False
if (
self.request.in_contest_mode
and self.object in self.request.participation.contest.problems.all()
):
context["in_contest"] = True
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")))
2020-01-21 06:35:58 +00:00
except ValueError:
return HttpResponseBadRequest()
2023-03-26 03:11:56 +00:00
contest = None
try:
in_contest = bool(self.request.POST.get("in_contest", False))
if in_contest:
contest = self.request.participation.contest
except (KeyError, ValueError):
return HttpResponseBadRequest()
2022-05-14 17:57:27 +00:00
return self.generate_response(
2023-03-26 03:11:56 +00:00
id_range, languages, self.request.POST.getlist("result"), contest
2022-05-14 17:57:27 +00:00
)
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()
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):
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
)
with tempfile.SpooledTemporaryFile() as tmp:
2022-05-14 17:57:27 +00:00
with zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as archive:
for submission in submissions:
2022-05-14 17:57:27 +00:00
file_name = (
str(submission.id)
+ "_"
+ str(submission.user)
+ "."
+ str(submission.language.key)
)
archive.writestr(file_name, submission.source.source)
2022-05-14 17:57:27 +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"
)
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)
else:
return Http404()
2020-01-21 06:35:58 +00:00
2022-05-14 17:57:27 +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]))