Source code for oioioi.contests.forms

import json

from django import forms
from django.contrib.admin import widgets
from django.contrib.auth.models import User
from django.forms import ValidationError
from django.forms.widgets import Media as FormMedia
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from oioioi.base.utils.inputs import narrow_input_field, narrow_input_fields
from oioioi.base.utils.user_selection import UserSelectionField
from oioioi.contests.models import Contest, ProblemInstance, Round
from oioioi.contests.utils import is_contest_basicadmin, submittable_problem_instances
from oioioi.programs.models import Test


[docs]class SimpleContestForm(forms.ModelForm):
[docs] class Meta(object):
[docs] model = Contest
# Order of fields is important - focus after sending incomplete # form should not be on the 'name' field, otherwise the 'id' field, # as prepopulated with 'name' in ContestAdmin model, is cleared by # javascript with prepopulated fields functionality.
[docs] fields = ['controller_name', 'name', 'id']
[docs] start_date = forms.SplitDateTimeField( label=_("Start date"), widget=widgets.AdminSplitDateTime() )
[docs] end_date = forms.SplitDateTimeField( required=False, label=_("End date"), widget=widgets.AdminSplitDateTime() )
[docs] results_date = forms.SplitDateTimeField( required=False, label=_("Results date"), widget=widgets.AdminSplitDateTime() )
[docs] def _generate_default_dates(self): now = timezone.now() self.initial['start_date'] = now self.initial['end_date'] = None self.initial['results_date'] = None
[docs] def _set_dates(self, round): for date in ['start_date', 'end_date', 'results_date']: setattr(round, date, self.cleaned_data.get(date))
def __init__(self, *args, **kwargs): super(SimpleContestForm, self).__init__(*args, **kwargs) instance = kwargs.get('instance', None) if instance is not None: rounds = instance.round_set.all() if len(rounds) > 1: raise ValueError( "SimpleContestForm does not support contests " "with more than one round." ) if len(rounds) == 1: round = rounds[0] self.initial['start_date'] = round.start_date self.initial['end_date'] = round.end_date self.initial['results_date'] = round.results_date else: self._generate_default_dates() else: self._generate_default_dates()
[docs] def clean(self): cleaned_data = super(SimpleContestForm, self).clean() round = Round() self._set_dates(round) round.clean() return cleaned_data
[docs] def save(self, commit=True): instance = super(SimpleContestForm, self).save(commit=False) rounds = instance.round_set.all() if len(rounds) > 1: raise ValueError( "SimpleContestForm does not support contests " "with more than one round." ) if len(rounds) == 1: round = rounds[0] else: instance.save() round = Round(contest=instance, name=_("Round 1")) self._set_dates(round) round.save() if commit: instance.save() return instance
[docs]class ProblemInstanceForm(forms.ModelForm): def __init__(self, *args, **kwargs): instance = kwargs.get('instance') super(ProblemInstanceForm, self).__init__(*args, **kwargs) if instance: self.fields['round'].queryset = instance.contest.round_set self.fields['round'].required = True
[docs]class SubmissionForm(forms.Form): """Represents base submission form containing task selector. Recognized optional ``**kwargs`` fields: * ``problem_filter`` Function filtering submittable tasks. * ``kind`` Kind of submission accessible with ``kind`` property. * ``problem_instance`` When SubmissionForm is used only for one problem_instance. Otherwise ``problem_instance`` is None. * ``add_kind_and_user_fields`` Option deciding whether form should add kind and user fields to itself. """
[docs] problem_instance_id = forms.ChoiceField(label=_("Problem"))
[docs] _js = ['common/submit.js',]
@property
[docs] def media(self): return FormMedia(self._js)
def __init__(self, request, *args, **kwargs): add_kind_and_user_fields = kwargs.pop('add_kind_and_user_fields', True) problem_instance = kwargs.pop('problem_instance', None) if problem_instance is None: # if problem_instance does not exist any from the current # contest is chosen. To change in future. # ALSO in mailsubmit.forms contest = request.contest assert contest is not None problem_instances = ProblemInstance.objects.filter(contest=contest) problem_instance = problem_instances[0] else: problem_instances = [problem_instance] contest = None self.all_problem_instances = problem_instances # Default kind is selected based on # the first problem_instance assigned to this form. # This is an arbitrary choice. self.kind = getattr(self, 'kind', None) or kwargs.pop( 'kind', problem_instance.controller.get_default_submission_kind( request, problem_instance=problem_instance ), ) problem_filter = kwargs.pop('problem_filter', None) self.request = request # taking the available problems pis = self.get_problem_instances() if problem_filter: pis = problem_filter(pis) pi_choices = [(pi.id, str(pi)) for pi in pis] # init form with previously sent data super(SubmissionForm, self).__init__(*args, **kwargs) # prepare problem instance selector pi_field = self.fields['problem_instance_id'] self._set_field_show_always('problem_instance_id') if len(pi_choices) > 1: pi_field.choices = [('', '')] + pi_choices else: pi_field.choices = pi_choices narrow_input_field(pi_field) # if contest admin, add kind and 'as other user' field if add_kind_and_user_fields and contest and is_contest_basicadmin(request): self.fields['user'] = UserSelectionField( label=_("User"), hints_url=reverse( 'contest_user_hints', kwargs={'contest_id': request.contest.id} ), initial=request.user, ) self._set_field_show_always('user') def clean_user(): try: user = self.cleaned_data['user'] if user == request.user: return user if not request.user.is_superuser: contest.controller.registration_controller().filter_participants( User.objects.filter(pk=user.pk) ).get() return user except User.DoesNotExist: raise forms.ValidationError( _("User does not exist or you do not have enough privileges") ) self.clean_user = clean_user self.fields['kind'] = forms.ChoiceField( choices=[('NORMAL', _("Normal")), ('IGNORED', _("Ignored"))], initial=self.kind, label=_("Kind"), ) self._set_field_show_always('kind') narrow_input_fields([self.fields['kind'], self.fields['user']]) self.hide_default_fields_pi_ids = [] # adding additional fields, etc for pi in pis: pi.controller.adjust_submission_form(request, self, pi) self._set_default_fields_attributes() hide_default_fields_script = ( "startShowingFields(" + json.dumps(self.hide_default_fields_pi_ids) + ")" ) self.additional_scripts = [hide_default_fields_script] # fix field order: self.move_field_to_end('user') self.move_field_to_end('kind')
[docs] def move_field_to_end(self, field_name): if field_name in self.fields: # delete field from fields and readdd it to put it at the end tmp = self.fields[field_name] del self.fields[field_name] self.fields[field_name] = tmp
[docs] def set_custom_field_attributes(self, field_name, problem_instance): """ Prepare custom field to be displayed only for a specific problems. Still all custom fields need to have unique names (best practice is to prefix them with `problem_instance.id`). :param field_name: Name of custom field :param problem_instance: Problem instance which they are assigned to """ self.fields[field_name].widget.attrs['data-submit'] = str(problem_instance.id)
[docs] def hide_default_fields(self, problem_instance): """ Hide default form fields for a given problem instance. :param problem_instance: Problem instance which will have fields hidden """ self.hide_default_fields_pi_ids.append(problem_instance.id)
[docs] def _set_field_show_always(self, field_name): self.fields[field_name].widget.attrs['data-submit'] = 'always'
[docs] def _set_default_fields_attributes(self): for field_name in self.fields: field = self.fields[field_name] if 'data-submit' not in field.widget.attrs: # If no attribute was set, set it to default. # This is for backwards compatibility with contests that # have only one submission form and don't need to bother. field.widget.attrs['data-submit'] = 'default'
[docs] def get_problem_instances(self): return submittable_problem_instances(self.request)
[docs] def is_valid(self): return forms.Form.is_valid(self)
[docs] def clean(self, check_submission_limit=True, check_round_times=True): cleaned_data = super(SubmissionForm, self).clean() if 'kind' not in cleaned_data: cleaned_data['kind'] = self.kind if 'problem_instance_id' not in cleaned_data: return cleaned_data try: pi = ProblemInstance.objects.get(id=cleaned_data['problem_instance_id']) cleaned_data['problem_instance'] = pi except ProblemInstance.DoesNotExist: self._errors['problem_instance_id'] = self.error_class( [_("Invalid problem")] ) del cleaned_data['problem_instance_id'] return cleaned_data kind = cleaned_data['kind'] if check_submission_limit and pi.controller.is_submissions_limit_exceeded( self.request, pi, kind ): raise ValidationError( _("Submission limit for the problem '%s' exceeded.") % pi.problem.name ) decision = pi.controller.can_submit(self.request, pi, check_round_times) if not decision: raise ValidationError(str(getattr(decision, 'exc', _("Permission denied")))) return pi.controller.validate_submission_form( self.request, pi, self, cleaned_data )
[docs]class SubmissionFormForProblemInstance(SubmissionForm): def __init__(self, request, problem_instance, *args, **kwargs): self.problem_instance = problem_instance kwargs['problem_instance'] = problem_instance super(SubmissionFormForProblemInstance, self).__init__(request, *args, **kwargs) pis = self.fields['problem_instance_id'] pis.widget.attrs['readonly'] = 'True' pis.widget.attrs['data-submit'] = 'hidden'
[docs] def get_problem_instances(self): return [self.problem_instance]
[docs]class GetUserInfoForm(forms.Form):
[docs] user = UserSelectionField(label=_("Username"))
def __init__(self, request, *args, **kwargs): super(GetUserInfoForm, self).__init__(*args, **kwargs) self.fields['user'].hints_url = reverse( 'contest_user_hints', kwargs={'contest_id': request.contest.id} )
[docs]class TestsSelectionForm(forms.Form): def __init__(self, request, queryset, pis_count, uses_is_active, *args, **kwargs): super(TestsSelectionForm, self).__init__(*args, **kwargs) problem_instance = queryset[0].problem_instance tests = Test.objects.filter(problem_instance=problem_instance, is_active=True) widget = forms.RadioSelect(attrs={'onChange': 'rejudgeTypeOnChange(this)'}) self.fields['rejudge_type'] = forms.ChoiceField(widget=widget) if uses_is_active: self.fields['rejudge_type'].choices = [ ('FULL', _("Rejudge submissions on all current active tests")), ( 'NEW', _( "Rejudge submissions on active tests which " "haven't been judged yet" ), ), ] else: self.fields['rejudge_type'].choices = [ ('FULL', _("Rejudge submissions on all tests")) ] self.initial['rejudge_type'] = 'FULL' if pis_count == 1: self.fields['rejudge_type'].choices.append( ('JUDGED', _("Rejudge submissions on judged tests only")) ) self.fields['tests'] = forms.MultipleChoiceField( widget=forms.CheckboxSelectMultiple(attrs={'disabled': 'disabled'}), choices=[(test.name, test.name) for test in tests], ) self.fields['submissions'] = forms.ModelMultipleChoiceField( widget=forms.CheckboxSelectMultiple, queryset=queryset, initial=queryset )