1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2024-12-24 18:22:24 +00:00

Upload notes from a CSV sheet

This commit is contained in:
Yohann D'ANELLO 2022-05-15 12:23:17 +02:00
parent 5f2cd16071
commit d18f76cf80
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
7 changed files with 311 additions and 132 deletions

View File

@ -1,11 +1,16 @@
# Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
import csv
import re
from io import StringIO
from typing import Iterable
from bootstrap_datepicker_plus.widgets import DatePickerInput, DateTimePickerInput
from django import forms
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.validators import FileExtensionValidator
from django.utils import formats
from django.utils.translation import gettext_lazy as _
from PyPDF3 import PdfFileReader
@ -190,6 +195,69 @@ class PoolTeamsForm(forms.ModelForm):
}
class UploadNotesForm(forms.Form):
file = forms.FileField(
label=_("CSV file:"),
validators=[FileExtensionValidator(allowed_extensions=["csv"])],
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['file'].widget.attrs['accept'] = 'text/csv'
def clean(self):
cleaned_data = super().clean()
if 'file' in cleaned_data:
file = cleaned_data['file']
with file:
try:
csvfile = csv.reader(StringIO(file.read().decode()))
except UnicodeDecodeError:
self.add_error('file', _("This file contains non-UTF-8 content. "
"Please send your sheet as a CSV file."))
self.process(csvfile, cleaned_data)
return cleaned_data
def process(self, csvfile: Iterable[str], cleaned_data: dict):
parsed_notes = {}
for line in csvfile:
line = [s for s in line if s]
if len(line) < 19:
continue
name = line[0]
notes = line[1:19]
if not all(s.isnumeric() for s in notes):
continue
notes = list(map(int, notes))
if max(notes) < 3 or min(notes) < 0:
continue
max_notes = 3 * [20, 16, 9, 10, 9, 10]
for n, max_n in zip(notes, max_notes):
if n > max_n:
self.add_error('file',
_("The following note is higher of the maximum expected value:")
+ str(n) + " > " + str(max_n))
first_name, last_name = tuple(name.split(' ', 1))
jury = User.objects.filter(first_name=first_name, last_name=last_name)
if jury.count() != 1:
self.form.add_error('file', _("The following user was not found:") + " " + name)
continue
jury = jury.get()
vr = jury.registration
parsed_notes[vr] = notes
cleaned_data['parsed_notes'] = parsed_notes
return cleaned_data
class PassageForm(forms.ModelForm):
def clean(self):
cleaned_data = super().clean()

View File

@ -654,6 +654,15 @@ class Note(models.Model):
default=0,
)
def set_all(self, defender_writing: int, defender_oral: int, opponent_writing: int, opponent_oral: int,
reporter_writing: int, reporter_oral: int):
self.defender_writing = defender_writing
self.defender_oral = defender_oral
self.opponent_writing = opponent_writing
self.opponent_oral = opponent_oral
self.reporter_writing = reporter_writing
self.reporter_oral = reporter_oral
def get_absolute_url(self):
return reverse_lazy("participation:passage_detail", args=(self.passage.pk,))

View File

@ -54,6 +54,7 @@
<button class="btn btn-success" data-toggle="modal" data-target="#addPassageModal">{% trans "Add passage" %}</button>
<button class="btn btn-primary" data-toggle="modal" data-target="#updatePoolModal">{% trans "Update" %}</button>
<button class="btn btn-primary" data-toggle="modal" data-target="#updateTeamsModal">{% trans "Update teams" %}</button>
<button class="btn btn-primary" data-toggle="modal" data-target="#uploadNotesModal">{% trans "Upload notes from a CSV file" %}</button>
</div>
{% endif %}
</div>
@ -78,6 +79,11 @@
{% trans "Update" as modal_button %}
{% url "participation:pool_update_teams" pk=pool.pk as modal_action %}
{% include "base_modal.html" with modal_id="updateTeams" %}
{% trans "Upload notes" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "participation:pool_upload_notes" pk=pool.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadNotes" modal_button_type="success" modal_enctype="multipart/form-data" %}
{% endblock %}
{% block extrajavascript %}
@ -100,6 +106,12 @@
if (!modalBody.html().trim())
modalBody.load("{% url "participation:passage_create" pk=pool.pk %} #form-content")
});
$('button[data-target="#uploadNotesModal"]').click(function() {
let modalBody = $("#uploadNotesModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "participation:pool_upload_notes" pk=pool.pk %} #form-content")
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load i18n %}
{% block content %}
<form method="post" enctype="multipart/form-data">
<div id="form-content">
{% csrf_token %}
{{ form|crispy }}
</div>
<button class="btn btn-primary" type="submit">{% trans "Upload" %}</button>
</form>
{% endblock %}

View File

@ -6,9 +6,10 @@ from django.views.generic import TemplateView
from .views import CreateTeamView, JoinTeamView, MyParticipationDetailView, MyTeamDetailView, NoteUpdateView, \
ParticipationDetailView, PassageCreateView, PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, \
PoolUpdateTeamsView, PoolUpdateView, SolutionUploadView, SynthesisUploadView, TeamAuthorizationsView, \
TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, TeamUploadMotivationLetterView, TournamentCreateView, \
TournamentDetailView, TournamentExportCSVView, TournamentListView, TournamentUpdateView
PoolUpdateTeamsView, PoolUpdateView, PoolUploadNotesView, SolutionUploadView, SynthesisUploadView,\
TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \
TournamentListView, TournamentUpdateView
app_name = "participation"
@ -36,6 +37,7 @@ urlpatterns = [
path("pools/<int:pk>/", PoolDetailView.as_view(), name="pool_detail"),
path("pools/<int:pk>/update/", PoolUpdateView.as_view(), name="pool_update"),
path("pools/<int:pk>/update-teams/", PoolUpdateTeamsView.as_view(), name="pool_update_teams"),
path("pools/<int:pk>/upload-notes/", PoolUploadNotesView.as_view(), name="pool_upload_notes"),
path("pools/passages/add/<int:pk>/", PassageCreateView.as_view(), name="passage_create"),
path("pools/passages/<int:pk>/", PassageDetailView.as_view(), name="passage_detail"),
path("pools/passages/<int:pk>/update/", PassageUpdateView.as_view(), name="passage_update"),

View File

@ -6,6 +6,7 @@ import os
from zipfile import ZipFile
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.sites.models import Site
from django.core.exceptions import PermissionDenied
@ -18,6 +19,7 @@ from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, FormView, RedirectView, TemplateView, UpdateView, View
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import FormMixin, ProcessFormView
from django_tables2 import SingleTableView
from magic import Magic
@ -28,7 +30,7 @@ from tfjm.views import AdminMixin, VolunteerMixin
from .forms import JoinTeamForm, MotivationLetterForm, NoteForm, ParticipationForm, PassageForm, PoolForm, \
PoolTeamsForm, RequestValidationForm, SolutionForm, SynthesisForm, TeamForm, TournamentForm, \
ValidateParticipationForm
UploadNotesForm, ValidateParticipationForm
from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament
from .tables import NoteTable, ParticipationTable, PassageTable, PoolTable, TeamTable, TournamentTable
@ -703,6 +705,43 @@ class PoolUpdateTeamsView(VolunteerMixin, UpdateView):
return self.handle_no_permission()
class PoolUploadNotesView(VolunteerMixin, FormView, DetailView):
model = Pool
form_class = UploadNotesForm
template_name = 'participation/upload_notes.html'
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
if request.user.registration.is_admin or request.user.registration.is_volunteer \
and (self.object.tournament in request.user.registration.organized_tournaments.all()
or request.user.registration in self.object.juries.all()):
return super().dispatch(request, *args, **kwargs)
return self.handle_no_permission()
@transaction.atomic
def form_valid(self, form):
pool = self.get_object()
parsed_notes = form.cleaned_data['parsed_notes']
for vr, notes in parsed_notes.items():
if vr not in pool.juries.all():
form.add_error('file', _("The following user is not registered as a jury:") + " " + str(vr))
for i, passage in enumerate(pool.passages.all()):
note = Note.objects.get(jury=vr, passage=passage)
passage_notes = notes[6 * i:6 * (i + 1)]
note.set_all(*passage_notes)
note.save()
messages.success(self.request, _("Notes were successfully uploaded."))
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy('participation:pool_detail', args=(self.kwargs['pk'],))
class PassageCreateView(VolunteerMixin, CreateView):
model = Passage
form_class = PassageForm

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-26 15:09+0200\n"
"POT-Creation-Date: 2022-05-15 12:23+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Yohann D'ANELLO <yohann.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -104,59 +104,78 @@ msgstr "Changelog de type \"{action}\" pour le modèle {model} le {timestamp}"
msgid "valid"
msgstr "valide"
#: apps/participation/forms.py:25
#: apps/participation/forms.py:30
msgid "This name is already used."
msgstr "Ce nom est déjà utilisé."
#: apps/participation/forms.py:32 apps/participation/models.py:41
#: apps/participation/forms.py:37 apps/participation/models.py:41
msgid "The trigram must be composed of three uppercase letters."
msgstr "Le trigramme doit être composé de trois lettres majuscules."
#: apps/participation/forms.py:35
#: apps/participation/forms.py:40
msgid "This trigram is already used."
msgstr "Ce trigramme est déjà utilisé."
#: apps/participation/forms.py:50
#: apps/participation/forms.py:55
msgid "No team was found with this access code."
msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
#: apps/participation/forms.py:79 apps/participation/forms.py:215
#: apps/participation/forms.py:84 apps/participation/forms.py:283
#: apps/registration/forms.py:117 apps/registration/forms.py:139
#: apps/registration/forms.py:161 apps/registration/forms.py:215
msgid "The uploaded file size must be under 2 Mo."
msgstr "Le fichier envoyé doit peser moins de 2 Mo."
#: apps/participation/forms.py:81 apps/registration/forms.py:119
#: apps/participation/forms.py:86 apps/registration/forms.py:119
#: apps/registration/forms.py:141 apps/registration/forms.py:163
#: apps/registration/forms.py:217
msgid "The uploaded file must be a PDF, PNG of JPEG file."
msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG."
#: apps/participation/forms.py:99
#: apps/participation/forms.py:104
msgid "I engage myself to participate to the whole TFJM²."
msgstr "Je m'engage à participer à l'intégralité du TFJM²."
#: apps/participation/forms.py:114
#: apps/participation/forms.py:119
msgid "Message to address to the team:"
msgstr "Message à adresser à l'équipe :"
#: apps/participation/forms.py:152
#: apps/participation/forms.py:157
msgid "The uploaded file size must be under 5 Mo."
msgstr "Le fichier envoyé doit peser moins de 5 Mo."
#: apps/participation/forms.py:154 apps/participation/forms.py:217
#: apps/participation/forms.py:159 apps/participation/forms.py:285
msgid "The uploaded file must be a PDF file."
msgstr "Le fichier envoyé doit être au format PDF."
#: apps/participation/forms.py:158
#: apps/participation/forms.py:163
msgid "The PDF file must not have more than 30 pages."
msgstr "Le fichier PDF ne doit pas avoir plus de 30 pages."
#: apps/participation/forms.py:198
#: apps/participation/forms.py:200
msgid "CSV file:"
msgstr "Tableur au format CSV :"
#: apps/participation/forms.py:217
msgid ""
"This file contains non-UTF-8 content. Please send your sheet as a CSV file."
msgstr ""
"Ce fichier contient des éléments non-UTF-8. Merci d'envoyer votre tableur au "
"format CSV."
#: apps/participation/forms.py:242
msgid "The following note is higher of the maximum expected value:"
msgstr "La note suivante est supérieure au maximum attendu :"
#: apps/participation/forms.py:249
msgid "The following user was not found:"
msgstr "L'utilisateur suivant n'a pas été trouvé :"
#: apps/participation/forms.py:266
msgid "The defender, the opponent and the reporter must be different."
msgstr "Le défenseur, l'opposant et le rapporteur doivent être différents."
#: apps/participation/forms.py:202
#: apps/participation/forms.py:270
msgid "This defender did not work on this problem."
msgstr "Ce défenseur ne travaille pas sur ce problème."
@ -203,7 +222,7 @@ msgstr "début"
msgid "end"
msgstr "fin"
#: apps/participation/models.py:147 apps/participation/models.py:408
#: apps/participation/models.py:147
#: apps/participation/templates/participation/tournament_detail.html:18
msgid "place"
msgstr "lieu"
@ -286,8 +305,8 @@ msgstr "L'équipe est sélectionnée pour la finale."
msgid "Participation of the team {name} ({trigram})"
msgstr "Participation de l'équipe {name} ({trigram})"
#: apps/participation/models.py:331 apps/participation/models.py:537
#: apps/participation/models.py:577
#: apps/participation/models.py:331 apps/participation/models.py:530
#: apps/participation/models.py:568
msgid "participation"
msgstr "participation"
@ -342,36 +361,32 @@ msgstr "poule"
msgid "pools"
msgstr "poules"
#: apps/participation/models.py:410
msgid "Where the solution is presented?"
msgstr "Où est-ce que les solutions sont défendues ?"
#: apps/participation/models.py:415
#: apps/participation/models.py:408
msgid "defended solution"
msgstr "solution défendue"
#: apps/participation/models.py:417 apps/participation/models.py:544
#: apps/participation/models.py:410 apps/participation/models.py:537
#, python-brace-format
msgid "Problem #{problem}"
msgstr "Problème n°{problem}"
#: apps/participation/models.py:424 apps/participation/tables.py:106
#: apps/participation/models.py:417 apps/participation/tables.py:106
msgid "defender"
msgstr "défenseur"
#: apps/participation/models.py:431 apps/participation/models.py:589
#: apps/participation/models.py:424 apps/participation/models.py:580
msgid "opponent"
msgstr "opposant"
#: apps/participation/models.py:438 apps/participation/models.py:590
#: apps/participation/models.py:431 apps/participation/models.py:581
msgid "reporter"
msgstr "rapporteur"
#: apps/participation/models.py:443
#: apps/participation/models.py:436
msgid "penalties"
msgstr "pénalités"
#: apps/participation/models.py:445
#: apps/participation/models.py:438
msgid ""
"Number of penalties for the defender. The defender will loose a 0.5 "
"coefficient per penalty."
@ -379,108 +394,108 @@ msgstr ""
"Nombre de pénalités pour le défenseur. Le défenseur perd un coefficient 0.5 "
"sur sa solution écrite par pénalité."
#: apps/participation/models.py:505 apps/participation/models.py:508
#: apps/participation/models.py:511
#: apps/participation/models.py:498 apps/participation/models.py:501
#: apps/participation/models.py:504
#, python-brace-format
msgid "Team {trigram} is not registered in the pool."
msgstr "L'équipe {trigram} n'est pas inscrite dans la poule."
#: apps/participation/models.py:516
#: apps/participation/models.py:509
#, python-brace-format
msgid "Passage of {defender} for problem {problem}"
msgstr "Passage de {defender} pour le problème {problem}"
#: apps/participation/models.py:520 apps/participation/models.py:584
#: apps/participation/models.py:628
#: apps/participation/models.py:513 apps/participation/models.py:575
#: apps/participation/models.py:617
msgid "passage"
msgstr "passage"
#: apps/participation/models.py:521
#: apps/participation/models.py:514
msgid "passages"
msgstr "passages"
#: apps/participation/models.py:542
#: apps/participation/models.py:535
msgid "problem"
msgstr "numéro de problème"
#: apps/participation/models.py:549
#: apps/participation/models.py:542
msgid "solution for the final tournament"
msgstr "solution pour la finale"
#: apps/participation/models.py:554 apps/participation/models.py:595
#: apps/participation/models.py:547 apps/participation/models.py:586
msgid "file"
msgstr "fichier"
#: apps/participation/models.py:562
#: apps/participation/models.py:553
#, python-brace-format
msgid "Solution of team {team} for problem {problem}"
msgstr "Solution de l'équipe {team} pour le problème {problem}"
#: apps/participation/models.py:564
#: apps/participation/models.py:555
msgid "for final"
msgstr "pour la finale"
#: apps/participation/models.py:567
#: apps/participation/models.py:558
msgid "solution"
msgstr "solution"
#: apps/participation/models.py:568
#: apps/participation/models.py:559
msgid "solutions"
msgstr "solutions"
#: apps/participation/models.py:603
#: apps/participation/models.py:592
#, python-brace-format
msgid "Synthesis of {team} as {type} for problem {problem} of {defender}"
msgstr ""
"Note de synthèse de l'équipe {team} en tant que {type} pour le problème "
"{problem} de {defender}"
#: apps/participation/models.py:611
#: apps/participation/models.py:600
msgid "synthesis"
msgstr "note de synthèse"
#: apps/participation/models.py:612
#: apps/participation/models.py:601
msgid "syntheses"
msgstr "notes de synthèse"
#: apps/participation/models.py:621
#: apps/participation/models.py:610
msgid "jury"
msgstr "jury"
#: apps/participation/models.py:633
#: apps/participation/models.py:622
msgid "defender writing note"
msgstr "note d'écrit du défenseur"
#: apps/participation/models.py:639
#: apps/participation/models.py:628
msgid "defender oral note"
msgstr "note d'oral du défenseur"
#: apps/participation/models.py:645
#: apps/participation/models.py:634
msgid "opponent writing note"
msgstr "note d'écrit de l'opposant"
#: apps/participation/models.py:651
#: apps/participation/models.py:640
msgid "opponent oral note"
msgstr "note d'oral de l'opposant"
#: apps/participation/models.py:657
#: apps/participation/models.py:646
msgid "reporter writing note"
msgstr "not d'écrit du rapporteur"
msgstr "note d'écrit du rapporteur"
#: apps/participation/models.py:663
#: apps/participation/models.py:652
msgid "reporter oral note"
msgstr "note d'oral du rapporteur"
#: apps/participation/models.py:672
#: apps/participation/models.py:670
#, python-brace-format
msgid "Notes of {jury} for {passage}"
msgstr "Notes de {jury} pour le {passage}"
#: apps/participation/models.py:679
#: apps/participation/models.py:677
msgid "note"
msgstr "note"
#: apps/participation/models.py:680
#: apps/participation/models.py:678
msgid "notes"
msgstr "notes"
@ -555,12 +570,12 @@ msgid "Join"
msgstr "Rejoindre"
#: apps/participation/templates/participation/note_form.html:11
#: apps/participation/templates/participation/passage_detail.html:49
#: apps/participation/templates/participation/passage_detail.html:105
#: apps/participation/templates/participation/passage_detail.html:111
#: apps/participation/templates/participation/passage_detail.html:46
#: apps/participation/templates/participation/passage_detail.html:102
#: apps/participation/templates/participation/passage_detail.html:108
#: apps/participation/templates/participation/pool_detail.html:55
#: apps/participation/templates/participation/pool_detail.html:73
#: apps/participation/templates/participation/pool_detail.html:78
#: apps/participation/templates/participation/pool_detail.html:74
#: apps/participation/templates/participation/pool_detail.html:79
#: apps/participation/templates/participation/team_detail.html:113
#: apps/participation/templates/participation/team_detail.html:177
#: apps/participation/templates/participation/tournament_form.html:12
@ -623,9 +638,11 @@ msgid "Upload solution"
msgstr "Envoyer une solution"
#: apps/participation/templates/participation/participation_detail.html:59
#: apps/participation/templates/participation/passage_detail.html:117
#: apps/participation/templates/participation/passage_detail.html:114
#: apps/participation/templates/participation/pool_detail.html:84
#: apps/participation/templates/participation/team_detail.html:172
#: apps/participation/templates/participation/upload_motivation_letter.html:13
#: apps/participation/templates/participation/upload_notes.html:12
#: apps/participation/templates/participation/upload_solution.html:11
#: apps/participation/templates/participation/upload_synthesis.html:16
#: apps/registration/templates/registration/upload_health_sheet.html:17
@ -659,72 +676,68 @@ msgid "Defended solution:"
msgstr "Solution défendue"
#: apps/participation/templates/participation/passage_detail.html:28
msgid "Place:"
msgstr "Lieu :"
#: apps/participation/templates/participation/passage_detail.html:31
msgid "Defender penalties count:"
msgstr "Nombre de pénalités :"
#: apps/participation/templates/participation/passage_detail.html:34
#: apps/participation/templates/participation/passage_detail.html:31
msgid "Syntheses:"
msgstr "Notes de synthèse :"
#: apps/participation/templates/participation/passage_detail.html:39
#: apps/participation/templates/participation/passage_detail.html:36
msgid "No synthesis was uploaded yet."
msgstr "Aucune note de synthèse n'a encore été envoyée."
#: apps/participation/templates/participation/passage_detail.html:47
#: apps/participation/templates/participation/passage_detail.html:110
#: apps/participation/templates/participation/passage_detail.html:44
#: apps/participation/templates/participation/passage_detail.html:107
msgid "Update notes"
msgstr "Modifier les notes"
#: apps/participation/templates/participation/passage_detail.html:53
#: apps/participation/templates/participation/passage_detail.html:116
#: apps/participation/templates/participation/passage_detail.html:50
#: apps/participation/templates/participation/passage_detail.html:113
msgid "Upload synthesis"
msgstr "Envoyer une note de synthèse"
#: apps/participation/templates/participation/passage_detail.html:61
#: apps/participation/templates/participation/passage_detail.html:58
msgid "Notes detail"
msgstr "Détails des notes"
#: apps/participation/templates/participation/passage_detail.html:68
#: apps/participation/templates/participation/passage_detail.html:65
msgid "Average points for the defender writing:"
msgstr "Moyenne de l'écrit du défenseur :"
#: apps/participation/templates/participation/passage_detail.html:71
#: apps/participation/templates/participation/passage_detail.html:68
msgid "Average points for the defender oral:"
msgstr "Moyenne de l'oral du défenseur :"
#: apps/participation/templates/participation/passage_detail.html:74
#: apps/participation/templates/participation/passage_detail.html:71
msgid "Average points for the opponent writing:"
msgstr "Moyenne de l'écrit de l'opposant :"
#: apps/participation/templates/participation/passage_detail.html:77
#: apps/participation/templates/participation/passage_detail.html:74
msgid "Average points for the opponent oral:"
msgstr "Moyenne de l'oral de l'opposant :"
#: apps/participation/templates/participation/passage_detail.html:80
#: apps/participation/templates/participation/passage_detail.html:77
msgid "Average points for the reporter writing:"
msgstr "Moyenne de l'écrit du rapporteur :"
#: apps/participation/templates/participation/passage_detail.html:83
#: apps/participation/templates/participation/passage_detail.html:80
msgid "Average points for the reporter oral:"
msgstr "Moyenne de l'oral du rapporteur :"
#: apps/participation/templates/participation/passage_detail.html:90
#: apps/participation/templates/participation/passage_detail.html:87
msgid "Defender points:"
msgstr "Points du défenseur :"
#: apps/participation/templates/participation/passage_detail.html:93
#: apps/participation/templates/participation/passage_detail.html:90
msgid "Opponent points:"
msgstr "Points de l'opposant :"
#: apps/participation/templates/participation/passage_detail.html:96
#: apps/participation/templates/participation/passage_detail.html:93
msgid "Reporter points:"
msgstr "Points du rapporteur :"
#: apps/participation/templates/participation/passage_detail.html:104
#: apps/participation/templates/participation/passage_detail.html:101
#: apps/participation/templates/participation/passage_form.html:11
msgid "Update passage"
msgstr "Modifier le passage"
@ -755,29 +768,37 @@ msgid "Ranking"
msgstr "Classement"
#: apps/participation/templates/participation/pool_detail.html:54
#: apps/participation/templates/participation/pool_detail.html:67
#: apps/participation/templates/participation/pool_detail.html:68
msgid "Add passage"
msgstr "Ajouter un passage"
#: apps/participation/templates/participation/pool_detail.html:56
#: apps/participation/templates/participation/pool_detail.html:77
#: apps/participation/templates/participation/pool_detail.html:78
msgid "Update teams"
msgstr "Modifier les équipes"
#: apps/participation/templates/participation/pool_detail.html:63
#: apps/participation/templates/participation/pool_detail.html:57
msgid "Upload notes from a CSV file"
msgstr "Soumettre les notes à partir d'un fichier CSV"
#: apps/participation/templates/participation/pool_detail.html:64
msgid "Passages"
msgstr "Passages"
#: apps/participation/templates/participation/pool_detail.html:68
#: apps/participation/templates/participation/pool_detail.html:69
#: apps/participation/templates/participation/tournament_detail.html:109
msgid "Add"
msgstr "Ajouter"
#: apps/participation/templates/participation/pool_detail.html:72
#: apps/participation/templates/participation/pool_detail.html:73
#: apps/participation/templates/participation/pool_form.html:11
msgid "Update pool"
msgstr "Modifier la poule"
#: apps/participation/templates/participation/pool_detail.html:83
msgid "Upload notes"
msgstr "Envoyer les notes"
#: apps/participation/templates/participation/team_detail.html:13
msgid "Name:"
msgstr "Nom :"
@ -901,7 +922,7 @@ msgid "Invalidate"
msgstr "Invalider"
#: apps/participation/templates/participation/team_detail.html:171
#: apps/participation/views.py:327
#: apps/participation/views.py:329
msgid "Upload motivation letter"
msgstr "Envoyer la lettre de motivation"
@ -910,7 +931,7 @@ msgid "Update team"
msgstr "Modifier l'équipe"
#: apps/participation/templates/participation/team_detail.html:181
#: apps/participation/views.py:430
#: apps/participation/views.py:432
msgid "Leave team"
msgstr "Quitter l'équipe"
@ -1022,49 +1043,49 @@ msgstr "Retour aux détails de l'utilisateur"
msgid "Templates:"
msgstr "Modèles :"
#: apps/participation/views.py:43 tfjm/templates/base.html:71
#: apps/participation/views.py:45 tfjm/templates/base.html:71
#: tfjm/templates/base.html:230
msgid "Create team"
msgstr "Créer une équipe"
#: apps/participation/views.py:52 apps/participation/views.py:97
#: apps/participation/views.py:54 apps/participation/views.py:99
msgid "You don't participate, so you can't create a team."
msgstr "Vous ne participez pas, vous ne pouvez pas créer d'équipe."
#: apps/participation/views.py:54 apps/participation/views.py:99
#: apps/participation/views.py:56 apps/participation/views.py:101
msgid "You are already in a team."
msgstr "Vous êtes déjà dans une équipe."
#: apps/participation/views.py:88 tfjm/templates/base.html:76
#: apps/participation/views.py:90 tfjm/templates/base.html:76
#: tfjm/templates/base.html:225
msgid "Join team"
msgstr "Rejoindre une équipe"
#: apps/participation/views.py:150 apps/participation/views.py:436
#: apps/participation/views.py:470
#: apps/participation/views.py:152 apps/participation/views.py:438
#: apps/participation/views.py:472
msgid "You are not in a team."
msgstr "Vous n'êtes pas dans une équipe."
#: apps/participation/views.py:151 apps/participation/views.py:471
#: apps/participation/views.py:153 apps/participation/views.py:473
msgid "You don't participate, so you don't have any team."
msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe."
#: apps/participation/views.py:177
#: apps/participation/views.py:179
#, python-brace-format
msgid "Detail of team {trigram}"
msgstr "Détails de l'équipe {trigram}"
#: apps/participation/views.py:213
#: apps/participation/views.py:215
msgid "You don't participate, so you can't request the validation of the team."
msgstr ""
"Vous ne participez pas, vous ne pouvez pas demander la validation de "
"l'équipe."
#: apps/participation/views.py:216
#: apps/participation/views.py:218
msgid "The validation of the team is already done or pending."
msgstr "La validation de l'équipe est déjà faite ou en cours."
#: apps/participation/views.py:219
#: apps/participation/views.py:221
msgid ""
"The team can't be validated: missing email address confirmations, "
"authorizations, people, motivation letter or the tournament is not set."
@ -1073,66 +1094,74 @@ msgstr ""
"d'adresse e-mail, soit une autorisation, soit des personnes, soit la lettre "
"de motivation, soit le tournoi n'a pas été choisi."
#: apps/participation/views.py:241
#: apps/participation/views.py:243
msgid "You are not an organizer of the tournament."
msgstr "Vous n'êtes pas un organisateur du tournoi."
#: apps/participation/views.py:244
#: apps/participation/views.py:246
msgid "This team has no pending validation."
msgstr "L'équipe n'a pas de validation en attente."
#: apps/participation/views.py:274
#: apps/participation/views.py:276
msgid "You must specify if you validate the registration or not."
msgstr "Vous devez spécifier si vous validez l'inscription ou non."
#: apps/participation/views.py:307
#: apps/participation/views.py:309
#, python-brace-format
msgid "Update team {trigram}"
msgstr "Mise à jour de l'équipe {trigram}"
#: apps/participation/views.py:366 apps/participation/views.py:416
#: apps/participation/views.py:368 apps/participation/views.py:418
#, python-brace-format
msgid "Motivation letter of {team}.{ext}"
msgstr "Lettre de motivation de {team}.{ext}"
#: apps/participation/views.py:397
#: apps/participation/views.py:399
#, python-brace-format
msgid "Photo authorization of {participant}.{ext}"
msgstr "Autorisation de droit à l'image de {participant}.{ext}"
#: apps/participation/views.py:403
#: apps/participation/views.py:405
#, python-brace-format
msgid "Parental authorization of {participant}.{ext}"
msgstr "Autorisation parentale de {participant}.{ext}"
#: apps/participation/views.py:410
#: apps/participation/views.py:412
#, python-brace-format
msgid "Health sheet of {participant}.{ext}"
msgstr "Fiche sanitaire de {participant}.{ext}"
#: apps/participation/views.py:420
#: apps/participation/views.py:422
#, python-brace-format
msgid "Photo authorizations of team {trigram}.zip"
msgstr "Autorisations de droit à l'image de l'équipe {trigram}.zip"
#: apps/participation/views.py:438
#: apps/participation/views.py:440
msgid "The team is already validated or the validation is pending."
msgstr "La validation de l'équipe est déjà faite ou en cours."
#: apps/participation/views.py:485
#: apps/participation/views.py:487
msgid "The team is not validated yet."
msgstr "L'équipe n'est pas encore validée."
#: apps/participation/views.py:499
#: apps/participation/views.py:501
#, python-brace-format
msgid "Participation of team {trigram}"
msgstr "Participation de l'équipe {trigram}"
#: apps/participation/views.py:625
#: apps/participation/views.py:627
msgid "You can't upload a solution after the deadline."
msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
#: apps/participation/views.py:811
#: apps/participation/views.py:724
msgid "The following user is not registered as a jury:"
msgstr "L'utilisateur suivant n'est pas inscrit en tant que juré⋅e :"
#: apps/participation/views.py:732
msgid "Notes were successfully uploaded."
msgstr "Les notes ont bien été envoyées."
#: apps/participation/views.py:844
msgid "You can't upload a synthesis after the deadline."
msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."
@ -1535,16 +1564,16 @@ msgstr ""
#: apps/registration/templates/registration/payment_form.html:17
msgid ""
"You can pay with a credit card through <a class=\"alert-link\" href="
"\"https://www.helloasso.com/associations/animath/evenements/tfjm-2022-"
"You can pay with a credit card through <a class=\"alert-link\" "
"href=\"https://www.helloasso.com/associations/animath/evenements/tfjm-2022-"
"tournois-regionaux\">our Hello Asso page</a>. To make the validation of the "
"payment easier, <span class=\"text-danger\">please use the same e-mail "
"address that you use on this platform.</span> The payment verification will "
"be checked automatically under 10 minutes, you don't necessary need to fill "
"this form."
msgstr ""
"Vous pouvez payer par carte bancaire sur <a class=\"alert-link\" href="
"\"https://www.helloasso.com/associations/animath/evenements/tfjm-2022-"
"Vous pouvez payer par carte bancaire sur <a class=\"alert-link\" "
"href=\"https://www.helloasso.com/associations/animath/evenements/tfjm-2022-"
"tournois-regionaux\">notre page Hello Asso</a>. Pour rendre la validation du "
"paiement plus facile, <span class=\"text-danger\">merci d'utiliser la même "
"adresse e-mail que vous utilisez sur cette plateforme.</span> La "
@ -1563,14 +1592,14 @@ msgstr ""
#: apps/registration/templates/registration/payment_form.html:39
msgid ""
"If any payment mean is available to you, please contact us at <a class="
"\"alert-link\" href=\"mailto:contact@tfjm.org\">contact@tfjm.org</a> to find "
"a solution to your difficulties."
"If any payment mean is available to you, please contact us at <a "
"class=\"alert-link\" href=\"mailto:contact@tfjm.org\">contact@tfjm.org</a> "
"to find a solution to your difficulties."
msgstr ""
"Si aucun moyen de paiement ne vous convient, merci de nous contecter à "
"l'adresse <a class=\"alert-link\" href=\"mailto:contact@tfjm.org"
"\">contact@tfjm.org</a> pour que nous puissions trouver une solution à vos "
"difficultés."
"l'adresse <a class=\"alert-link\" href=\"mailto:contact@tfjm."
"org\">contact@tfjm.org</a> pour que nous puissions trouver une solution à "
"vos difficultés."
#: apps/registration/templates/registration/signup.html:5
#: apps/registration/templates/registration/signup.html:12
@ -1902,12 +1931,12 @@ msgstr "Déconnexion"
#, python-format
msgid ""
"Your email address is not validated. Please click on the link you received "
"by email. You can resend a mail by clicking on <a href=\"%(send_email_url)s"
"\">this link</a>."
"by email. You can resend a mail by clicking on <a "
"href=\"%(send_email_url)s\">this link</a>."
msgstr ""
"Votre adresse mail n'est pas validée. Merci de cliquer sur le lien que vous "
"avez reçu par mail. Vous pouvez renvoyer un mail en cliquant sur <a href="
"\"%(send_email_url)s\">ce lien</a>."
"avez reçu par mail. Vous pouvez renvoyer un mail en cliquant sur <a "
"href=\"%(send_email_url)s\">ce lien</a>."
#: tfjm/templates/base.html:180
msgid "Contact us"
@ -1953,3 +1982,9 @@ msgstr "Résultats"
#: tfjm/templates/search/search.html:25
msgid "No results found."
msgstr "Aucun résultat."
#~ msgid "Where the solution is presented?"
#~ msgstr "Où est-ce que les solutions sont défendues ?"
#~ msgid "Place:"
#~ msgstr "Lieu :"