233 lines
8.4 KiB
Python
233 lines
8.4 KiB
Python
# Copyright (C) 2020 by Animath
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
import re
|
|
|
|
from bootstrap_datepicker_plus import DatePickerInput, DateTimePickerInput
|
|
from django import forms
|
|
from django.core.exceptions import ValidationError
|
|
from django.utils import formats
|
|
from django.utils.translation import gettext_lazy as _
|
|
from PyPDF3 import PdfFileReader
|
|
|
|
from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament
|
|
|
|
|
|
class TeamForm(forms.ModelForm):
|
|
"""
|
|
Form to create a team, with the name and the trigram,...
|
|
"""
|
|
def clean_name(self):
|
|
if "name" in self.cleaned_data:
|
|
name = self.cleaned_data["name"].upper()
|
|
if not self.instance.pk and Team.objects.filter(name=name).exists():
|
|
raise ValidationError(_("This name is already used."))
|
|
return name
|
|
|
|
def clean_trigram(self):
|
|
if "trigram" in self.cleaned_data:
|
|
trigram = self.cleaned_data["trigram"].upper()
|
|
if not re.match("[A-Z]{3}", trigram):
|
|
raise ValidationError(_("The trigram must be composed of three uppercase letters."))
|
|
|
|
if not self.instance.pk and Team.objects.filter(trigram=trigram).exists():
|
|
raise ValidationError(_("This trigram is already used."))
|
|
return trigram
|
|
|
|
class Meta:
|
|
model = Team
|
|
fields = ('name', 'trigram',)
|
|
|
|
|
|
class JoinTeamForm(forms.ModelForm):
|
|
"""
|
|
Form to join a team by the access code.
|
|
"""
|
|
def clean_access_code(self):
|
|
access_code = self.cleaned_data["access_code"]
|
|
if not Team.objects.filter(access_code=access_code).exists():
|
|
raise ValidationError(_("No team was found with this access code."))
|
|
return access_code
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
if "access_code" in cleaned_data:
|
|
team = Team.objects.get(access_code=cleaned_data["access_code"])
|
|
self.instance = team
|
|
return cleaned_data
|
|
|
|
class Meta:
|
|
model = Team
|
|
fields = ('access_code',)
|
|
|
|
|
|
class ParticipationForm(forms.ModelForm):
|
|
"""
|
|
Form to update the problem of a team participation.
|
|
"""
|
|
class Meta:
|
|
model = Participation
|
|
fields = ('tournament', 'final',)
|
|
|
|
|
|
class MotivationLetterForm(forms.ModelForm):
|
|
def clean_file(self):
|
|
if "motivation_letter" in self.files:
|
|
file = self.files["motivation_letter"]
|
|
if file.size > 2e6:
|
|
raise ValidationError(_("The uploaded file size must be under 2 Mo."))
|
|
if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]:
|
|
raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file."))
|
|
return self.cleaned_data["motivation_letter"]
|
|
|
|
class Meta:
|
|
model = Team
|
|
fields = ('motivation_letter',)
|
|
|
|
|
|
class RequestValidationForm(forms.Form):
|
|
"""
|
|
Form to ask about validation.
|
|
"""
|
|
_form_type = forms.CharField(
|
|
initial="RequestValidationForm",
|
|
widget=forms.HiddenInput(),
|
|
)
|
|
|
|
engagement = forms.BooleanField(
|
|
label=_("I engage myself to participate to the whole TFJM²."),
|
|
required=True,
|
|
)
|
|
|
|
|
|
class ValidateParticipationForm(forms.Form):
|
|
"""
|
|
Form to let administrators to accept or refuse a team.
|
|
"""
|
|
_form_type = forms.CharField(
|
|
initial="ValidateParticipationForm",
|
|
widget=forms.HiddenInput(),
|
|
)
|
|
|
|
message = forms.CharField(
|
|
label=_("Message to address to the team:"),
|
|
widget=forms.Textarea(),
|
|
)
|
|
|
|
|
|
class TournamentForm(forms.ModelForm):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.fields["date_start"].widget = DatePickerInput(
|
|
format=formats.get_format_lazy(format_type="DATE_INPUT_FORMATS", use_l10n=True)[0])
|
|
self.fields["date_end"].widget = DatePickerInput(
|
|
format=formats.get_format_lazy(format_type="DATE_INPUT_FORMATS", use_l10n=True)[0])
|
|
self.fields["inscription_limit"].widget = DateTimePickerInput(
|
|
format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0])
|
|
self.fields["solution_limit"].widget = DateTimePickerInput(
|
|
format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0])
|
|
self.fields["solutions_draw"].widget = DateTimePickerInput(
|
|
format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0])
|
|
self.fields["syntheses_first_phase_limit"].widget = DateTimePickerInput(
|
|
format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0])
|
|
self.fields["solutions_available_second_phase"].widget = DateTimePickerInput(
|
|
format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0])
|
|
self.fields["syntheses_second_phase_limit"].widget = DateTimePickerInput(
|
|
format=formats.get_format_lazy(format_type="DATETIME_INPUT_FORMATS", use_l10n=True)[0])
|
|
self.fields["organizers"].widget = forms.CheckboxSelectMultiple()
|
|
|
|
class Meta:
|
|
model = Tournament
|
|
fields = '__all__'
|
|
|
|
|
|
class SolutionForm(forms.ModelForm):
|
|
def clean_file(self):
|
|
if "file" in self.files:
|
|
file = self.files["file"]
|
|
if file.size > 5e6:
|
|
raise ValidationError(_("The uploaded file size must be under 5 Mo."))
|
|
if file.content_type != "application/pdf":
|
|
raise ValidationError(_("The uploaded file must be a PDF file."))
|
|
pdf_reader = PdfFileReader(file)
|
|
pages = len(pdf_reader.pages)
|
|
if pages > 30:
|
|
raise ValidationError(_("The PDF file must not have more than 30 pages."))
|
|
return self.cleaned_data["file"]
|
|
|
|
def save(self, commit=True):
|
|
"""
|
|
Don't save a solution with this way. Use a view instead
|
|
"""
|
|
|
|
class Meta:
|
|
model = Solution
|
|
fields = ('problem', 'file',)
|
|
|
|
|
|
class PoolForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Pool
|
|
fields = ('tournament', 'round', 'bbb_url', 'results_available', 'juries',)
|
|
widgets = {
|
|
"juries": forms.CheckboxSelectMultiple,
|
|
}
|
|
|
|
|
|
class PoolTeamsForm(forms.ModelForm):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields["participations"].queryset = self.instance.tournament.participations.all()
|
|
|
|
class Meta:
|
|
model = Pool
|
|
fields = ('participations',)
|
|
widgets = {
|
|
"participations": forms.CheckboxSelectMultiple,
|
|
}
|
|
|
|
|
|
class PassageForm(forms.ModelForm):
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
if "defender" in cleaned_data and "opponent" in cleaned_data and "reporter" in cleaned_data \
|
|
and len({cleaned_data["defender"], cleaned_data["opponent"], cleaned_data["reporter"]}) < 3:
|
|
self.add_error(None, _("The defender, the opponent and the reporter must be different."))
|
|
if "defender" in self.cleaned_data and "solution_number" in self.cleaned_data \
|
|
and not Solution.objects.filter(participation=cleaned_data["defender"],
|
|
problem=cleaned_data["solution_number"]).exists():
|
|
self.add_error("solution_number", _("This defender did not work on this problem."))
|
|
return cleaned_data
|
|
|
|
class Meta:
|
|
model = Passage
|
|
fields = ('solution_number', 'place', 'defender', 'opponent', 'reporter', 'defender_penalties',)
|
|
|
|
|
|
class SynthesisForm(forms.ModelForm):
|
|
def clean_file(self):
|
|
if "file" in self.files:
|
|
file = self.files["file"]
|
|
if file.size > 2e6:
|
|
raise ValidationError(_("The uploaded file size must be under 2 Mo."))
|
|
if file.content_type != "application/pdf":
|
|
raise ValidationError(_("The uploaded file must be a PDF file."))
|
|
return self.cleaned_data["file"]
|
|
|
|
def save(self, commit=True):
|
|
"""
|
|
Don't save a synthesis with this way. Use a view instead
|
|
"""
|
|
|
|
class Meta:
|
|
model = Synthesis
|
|
fields = ('file',)
|
|
|
|
|
|
class NoteForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Note
|
|
fields = ('defender_writing', 'defender_oral', 'opponent_writing',
|
|
'opponent_oral', 'reporter_writing', 'reporter_oral', )
|