Source code for oioioi.contests.views

from operator import itemgetter  # pylint: disable=E0611

import six

import django
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from django.views.decorators.http import require_POST
from oioioi.base.main_page import register_main_page_view
from oioioi.base.menu import menu_registry
from oioioi.base.permissions import enforce_condition, not_anonymous
from oioioi.base.utils.redirect import safe_redirect
from oioioi.base.utils.user_selection import get_user_hints_view
from oioioi.contests.attachment_registration import attachment_registry
from oioioi.contests.controllers import submission_template_context
from oioioi.contests.forms import GetUserInfoForm, SubmissionForm
from oioioi.contests.models import (
    Contest,
    ContestAttachment,
    ProblemInstance,
    Submission,
    SubmissionReport,
    UserResultForProblem,
)
from oioioi.contests.processors import recent_contests
from oioioi.contests.utils import (
    can_admin_contest,
    can_enter_contest,
    can_see_personal_data,
    contest_exists,
    get_submission_or_error,
    has_any_submittable_problem,
    is_contest_admin,
    is_contest_basicadmin,
    is_contest_observer,
    visible_contests,
    visible_problem_instances,
    visible_rounds,
)
from oioioi.filetracker.utils import stream_file
from oioioi.problems.models import ProblemAttachment, ProblemStatement
from oioioi.problems.utils import (
    can_admin_problem_instance,
    copy_problem_instance,
    filter_my_all_visible_submissions,
    get_new_problem_instance,
    query_statement,
    query_zip,
    update_tests_from_main_pi,
)
from oioioi.status.registry import status_registry


@register_main_page_view(order=900)
[docs]def main_page_view(request): if not Contest.objects.exists(): return TemplateResponse(request, 'contests/index-no-contests.html') return redirect('select_contest')
[docs]def select_contest_view(request): contests = visible_contests(request) contests = sorted(contests, key=lambda x: x.creation_date, reverse=True) context = { 'contests': contests, } return TemplateResponse( request, 'contests/select_contest.html', context )
@enforce_condition(contest_exists & can_enter_contest)
[docs]def default_contest_view(request): url = request.contest.controller.default_view(request) return HttpResponseRedirect(url)
@status_registry.register
[docs]def get_contest_permissions(request, response): response['is_contest_admin'] = is_contest_admin(request) response['is_contest_basicadmin'] = is_contest_basicadmin(request) return response
@menu_registry.register_decorator( _("Problems"), lambda request: reverse('problems_list'), order=100 ) @enforce_condition(contest_exists & can_enter_contest)
[docs]def problems_list_view(request): controller = request.contest.controller problem_instances = visible_problem_instances(request) # Problem statements in order # 1) problem instance # 2) statement_visible # 3) round end time # 4) user result # 5) number of submissions left # 6) submissions_limit # 7) can_submit # Sorted by (start_date, end_date, round name, problem name) problems_statements = sorted( [ ( pi, controller.can_see_statement(request, pi), controller.get_round_times(request, pi.round), # Because this view can be accessed by an anynomous user we can't # use `user=request.user` (it would cause TypeError). Surprisingly # using request.user.id is ok since for AnynomousUser id is set # to None. next( ( r for r in UserResultForProblem.objects.filter( user__id=request.user.id, problem_instance=pi ) if r and r.submission_report and controller.can_see_submission_score( request, r.submission_report.submission ) ), None, ), pi.controller.get_submissions_left(request, pi), pi.controller.get_submissions_limit(request, pi), controller.can_submit(request, pi), ) for pi in problem_instances ], key=lambda p: (p[2].get_key_for_comparison(), p[0].round.name, p[0].short_name), ) show_submissions_limit = any([p[5] for p in problems_statements]) show_submit_button = any([p[6] for p in problems_statements]) show_rounds = len(frozenset(pi.round_id for pi in problem_instances)) > 1 table_columns = 3 + int(show_submissions_limit) + int(show_submit_button) return TemplateResponse( request, 'contests/problems_list.html', { 'problem_instances': problems_statements, 'show_rounds': show_rounds, 'show_scores': request.user.is_authenticated, 'show_submissions_limit': show_submissions_limit, 'show_submit_button': show_submit_button, 'table_columns': table_columns, 'problems_on_page': getattr(settings, 'PROBLEMS_ON_PAGE', 100), }, )
@enforce_condition(contest_exists & can_enter_contest)
[docs]def problem_statement_view(request, problem_instance): controller = request.contest.controller pi = get_object_or_404( ProblemInstance, round__contest=request.contest, short_name=problem_instance ) if not controller.can_see_problem(request, pi) or not controller.can_see_statement( request, pi ): raise PermissionDenied if not pi.problem.controller.supports_problem_statement(): # if the problem doesn't support having a problem statement, # redirect to submission return redirect('submit', problem_instance_id=pi.id) statement = query_statement(pi.problem) if not statement: return TemplateResponse( request, 'contests/no_problem_statement.html', {'problem_instance': pi} ) if statement.extension == '.zip': return redirect( 'problem_statement_zip_index', contest_id=request.contest.id, problem_instance=problem_instance, statement_id=statement.id, ) return stream_file(statement.content, statement.download_name)
@enforce_condition(contest_exists & can_enter_contest)
[docs]def problem_statement_zip_index_view(request, problem_instance, statement_id): response = problem_statement_zip_view( request, problem_instance, statement_id, 'index.html' ) problem_statement = get_object_or_404(ProblemStatement, id=statement_id) return TemplateResponse( request, 'contests/html_statement.html', { 'content': mark_safe(six.ensure_str(response.content)), 'problem_name': problem_statement.problem.name, }, )
@enforce_condition(contest_exists & can_enter_contest)
[docs]def problem_statement_zip_view(request, problem_instance, statement_id, path): controller = request.contest.controller pi = get_object_or_404( ProblemInstance, round__contest=request.contest, short_name=problem_instance ) statement = get_object_or_404( ProblemStatement, problem__probleminstance=pi, id=statement_id ) if not controller.can_see_problem(request, pi) or not controller.can_see_statement( request, pi ): raise PermissionDenied return query_zip(statement, path)
@menu_registry.register_decorator( _("Submit"), lambda request: reverse('submit'), order=300 ) @enforce_condition(contest_exists & can_enter_contest) @enforce_condition( has_any_submittable_problem, template='contests/nothing_to_submit.html' )
[docs]def submit_view(request, problem_instance_id=None): if request.method == 'POST': form = SubmissionForm(request, request.POST, request.FILES) if form.is_valid(): request.contest.controller.create_submission( request, form.cleaned_data['problem_instance'], form.cleaned_data ) return redirect('my_submissions', contest_id=request.contest.id) else: initial = {} if problem_instance_id is not None: initial = {'problem_instance_id': int(problem_instance_id)} form = SubmissionForm(request, initial=initial) pis = form.get_problem_instances() submissions_left = { pi.id: pi.controller.get_submissions_left(request, pi) for pi in pis } return TemplateResponse( request, 'contests/submit.html', {'form': form, 'submissions_left': submissions_left}, )
@menu_registry.register_decorator( _("My submissions"), lambda request: reverse('my_submissions'), order=400 ) @enforce_condition(not_anonymous & contest_exists & can_enter_contest)
[docs]def my_submissions_view(request): queryset = ( Submission.objects.filter(problem_instance__contest=request.contest) .order_by('-date') .select_related( 'user', 'problem_instance', 'problem_instance__contest', 'problem_instance__round', 'problem_instance__problem', ) ) controller = request.contest.controller queryset = controller.filter_my_visible_submissions(request, queryset) header = controller.render_my_submissions_header(request, queryset.all()) submissions = [submission_template_context(request, s) for s in queryset] show_scores = any(s['can_see_score'] for s in submissions) return TemplateResponse( request, 'contests/my_submissions.html', { 'header': header, 'submissions': submissions, 'show_scores': show_scores, 'submissions_on_page': getattr(settings, 'SUBMISSIONS_ON_PAGE', 100), }, )
@enforce_condition(not_anonymous)
[docs]def all_submissions_view(request): submissions = [] if request.user.is_authenticated: queryset = Submission.objects.filter(user=request.user).select_related( 'user', 'problem_instance', 'problem_instance__contest', 'problem_instance__round', 'problem_instance__problem', ) submissions_list = filter_my_all_visible_submissions( request, queryset ).order_by('-date') for s in submissions_list: request.contest = s.problem_instance.contest submissions.append(submission_template_context(request, s)) request.contest = None show_scores = any(s['can_see_score'] for s in submissions) return TemplateResponse( request, 'contests/my_submissions_all.html', { 'submissions': submissions, 'show_scores': show_scores, 'submissions_on_page': getattr(settings, 'SUBMISSIONS_ON_PAGE', 100), }, )
@enforce_condition(~contest_exists | can_enter_contest)
[docs]def submission_view(request, submission_id): submission = get_submission_or_error(request, submission_id) pi = submission.problem_instance controller = pi.controller can_admin = can_admin_problem_instance(request, pi) header = controller.render_submission(request, submission) footer = controller.render_submission_footer(request, submission) reports = [] queryset = SubmissionReport.objects.filter(submission=submission).prefetch_related( 'scorereport_set' ) for report in controller.filter_visible_reports( request, submission, queryset.filter(status='ACTIVE') ): reports.append(controller.render_report(request, report)) if can_admin: all_reports = controller.filter_visible_reports(request, submission, queryset) else: all_reports = [] return TemplateResponse( request, 'contests/submission.html', { 'submission': submission, 'header': header, 'footer': footer, 'reports': reports, 'all_reports': all_reports, 'can_admin': can_admin, }, )
[docs]def report_view(request, submission_id, report_id): submission = get_submission_or_error(request, submission_id) pi = submission.problem_instance if not can_admin_problem_instance(request, pi): raise PermissionDenied queryset = SubmissionReport.objects.filter(submission=submission) report = get_object_or_404(queryset, id=report_id) return HttpResponse(pi.controller.render_report(request, report))
@require_POST
[docs]def rejudge_submission_view(request, submission_id): submission = get_submission_or_error(request, submission_id) pi = submission.problem_instance if not can_admin_problem_instance(request, pi): raise PermissionDenied extra_args = {} supported_extra_args = pi.controller.get_supported_extra_args(submission) for flag in request.GET: if flag in supported_extra_args: extra_args[flag] = True else: raise SuspiciousOperation pi.controller.judge(submission, extra_args, is_rejudge=True) messages.info(request, _("Rejudge request received.")) return redirect('submission', submission_id=submission_id)
@require_POST
[docs]def change_submission_kind_view(request, submission_id, kind): submission = get_submission_or_error(request, submission_id) pi = submission.problem_instance if not can_admin_problem_instance(request, pi): raise PermissionDenied controller = pi.controller if kind in controller.valid_kinds_for_submission(submission): controller.change_submission_kind(submission, kind) messages.success(request, _("Submission kind has been changed.")) else: messages.error( request, _("%(kind)s is not valid kind for submission %(submission_id)d.") % {'kind': kind, 'submission_id': submission.id}, ) return redirect('submission', submission_id=submission_id)
@menu_registry.register_decorator( _("Downloads"), lambda request: reverse('contest_files'), order=200 ) @enforce_condition(not_anonymous & contest_exists & can_enter_contest)
[docs]def contest_files_view(request): additional_files = attachment_registry.to_list(request=request) contest_files = ( ContestAttachment.objects.filter(contest=request.contest) .filter(Q(round__isnull=True) | Q(round__in=visible_rounds(request))) .select_related('round') ) if not is_contest_basicadmin(request): contest_files = contest_files.filter( Q(pub_date__isnull=True) | Q(pub_date__lte=request.timestamp) ) round_file_exists = contest_files.filter(round__isnull=False).exists() problem_instances = visible_problem_instances(request) problem_ids = [pi.problem_id for pi in problem_instances] problem_files = ProblemAttachment.objects.filter( problem_id__in=problem_ids ).select_related('problem') add_category_field = round_file_exists or problem_files.exists() rows = [ { 'category': cf.round if cf.round else '', 'name': cf.download_name, 'description': cf.description, 'link': reverse( 'contest_attachment', kwargs={'contest_id': request.contest.id, 'attachment_id': cf.id}, ), 'pub_date': cf.pub_date, } for cf in contest_files ] rows += [ { 'category': pf.problem, 'name': pf.download_name, 'description': pf.description, 'link': reverse( 'problem_attachment', kwargs={'contest_id': request.contest.id, 'attachment_id': pf.id}, ), 'pub_date': None, } for pf in problem_files ] rows += [ { 'category': af.get('category'), 'name': af.get('name'), 'description': af.get('description'), 'link': af.get('link'), 'pub_date': af.get('pub_date'), } for af in additional_files ] rows.sort(key=itemgetter('name')) return TemplateResponse( request, 'contests/files.html', { 'files': rows, 'files_on_page': getattr(settings, 'FILES_ON_PAGE', 100), 'add_category_field': add_category_field, 'show_pub_dates': True, }, )
@enforce_condition(contest_exists & can_enter_contest)
[docs]def contest_attachment_view(request, attachment_id): attachment = get_object_or_404( ContestAttachment, contest_id=request.contest.id, id=attachment_id ) if (attachment.round and attachment.round not in visible_rounds(request)) or ( not is_contest_basicadmin(request) and attachment.pub_date and attachment.pub_date > request.timestamp ): raise PermissionDenied return stream_file(attachment.content, attachment.download_name)
@enforce_condition(contest_exists & can_enter_contest)
[docs]def problem_attachment_view(request, attachment_id): attachment = get_object_or_404(ProblemAttachment, id=attachment_id) problem_instances = visible_problem_instances(request) problem_ids = [pi.problem_id for pi in problem_instances] if attachment.problem_id not in problem_ids: raise PermissionDenied return stream_file(attachment.content, attachment.download_name)
@enforce_condition( contest_exists & (is_contest_basicadmin | is_contest_observer | can_see_personal_data) )
[docs]def contest_user_hints_view(request): rcontroller = request.contest.controller.registration_controller() queryset = rcontroller.filter_participants(User.objects.all()) return get_user_hints_view(request, 'substr', queryset)
@enforce_condition(contest_exists & (is_contest_basicadmin | can_see_personal_data))
[docs]def user_info_view(request, user_id): controller = request.contest.controller rcontroller = controller.registration_controller() user = get_object_or_404(User, id=user_id) if not request.user.is_superuser and ( user not in rcontroller.filter_users_with_accessible_personal_data( User.objects.all() ) or user.is_superuser ): raise PermissionDenied infolist = sorted( controller.get_contest_participant_info_list(request, user) + rcontroller.get_contest_participant_info_list(request, user), reverse=True, ) info = "".join(html for (_p, html) in infolist) return TemplateResponse( request, 'contests/user_info.html', { 'target_user_name': controller.get_user_public_name(request, user), 'info': info, }, )
@enforce_condition(contest_exists & (is_contest_basicadmin | can_see_personal_data)) @require_POST
[docs]def user_info_redirect_view(request): form = GetUserInfoForm(request, request.POST) if not form.is_valid(): return TemplateResponse( request, 'simple-centered-form.html', { 'form': form, 'action': reverse( 'user_info_redirect', kwargs={'contest_id': request.contest.id} ), 'title': _("See user info page"), }, ) user = form.cleaned_data['user'] return safe_redirect( request, reverse( 'user_info', kwargs={'contest_id': request.contest.id, 'user_id': user.id} ), )
@enforce_condition(contest_exists & is_contest_basicadmin)
[docs]def rejudge_all_submissions_for_problem_view(request, problem_instance_id): problem_instance = get_object_or_404(ProblemInstance, id=problem_instance_id) count = problem_instance.submission_set.count() if request.POST: for submission in problem_instance.submission_set.all(): problem_instance.controller.judge( submission, request.GET.dict(), is_rejudge=True ) messages.info( request, ngettext_lazy( "%(count)d rejudge request received.", "%(count)d rejudge requests received.", count, ) % {'count': count}, ) problem_instance.needs_rejudge = False problem_instance.save(update_fields=["needs_rejudge"]) return safe_redirect( request, reverse('oioioiadmin:contests_probleminstance_changelist') ) return TemplateResponse(request, 'contests/confirm_rejudge.html', {'count': count})
@enforce_condition(contest_exists & is_contest_basicadmin)
[docs]def rejudge_not_needed_view(request, problem_instance_id): problem_instance = get_object_or_404(ProblemInstance, id=problem_instance_id) if request.POST: problem_instance.needs_rejudge = False problem_instance.save(update_fields=["needs_rejudge"]) messages.success(request, _("Needs rejudge flag turned off.")) return safe_redirect( request, reverse('oioioiadmin:contests_probleminstance_changelist'), ) return TemplateResponse(request, 'contests/confirm_rejudge_not_needed.html')
@enforce_condition(contest_exists & is_contest_basicadmin)
[docs]def reset_tests_limits_for_probleminstance_view(request, problem_instance_id): problem_instance = get_object_or_404(ProblemInstance, id=problem_instance_id) if request.POST: update_tests_from_main_pi(problem_instance) messages.success(request, _("Tests limits reset successfully")) return safe_redirect( request, reverse('oioioiadmin:contests_probleminstance_changelist') ) return TemplateResponse( request, 'contests/confirm_resetting_limits.html', {'probleminstance': problem_instance}, )
@enforce_condition(contest_exists & is_contest_basicadmin)
[docs]def reattach_problem_contest_list_view(request, problem_instance_id, full_list=False): problem_instance = get_object_or_404(ProblemInstance, id=problem_instance_id) if full_list: contests = Contest.objects.all() else: contests = recent_contests(request) or Contest.objects.all() contests = [c for c in contests if can_admin_contest(request.user, c)] return TemplateResponse( request, 'contests/reattach_problem_contest_list.html', { 'problem_instance': problem_instance, 'contest_list': contests, 'full_list': full_list, }, )
@enforce_condition(contest_exists & is_contest_basicadmin)
[docs]def reattach_problem_confirm_view(request, problem_instance_id, contest_id): contest = get_object_or_404(Contest, id=contest_id) if not can_admin_contest(request.user, contest): raise PermissionDenied problem_instance = get_object_or_404(ProblemInstance, id=problem_instance_id) if request.method == 'POST': if request.POST.get('copy-limits', '') == 'on': pi = copy_problem_instance(problem_instance, contest) else: pi = get_new_problem_instance(problem_instance.problem, contest) messages.success(request, _(u"Problem {} added successfully.".format(pi))) return safe_redirect( request, reverse( 'oioioiadmin:contests_probleminstance_changelist', kwargs={'contest_id': contest.id}, ), ) return TemplateResponse( request, 'contests/reattach_problem_confirm.html', {'problem_instance': problem_instance, 'destination_contest': contest}, )