diff --git a/apps/participation/forms.py b/apps/participation/forms.py
index f2912bc..bcd2751 100644
--- a/apps/participation/forms.py
+++ b/apps/participation/forms.py
@@ -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()
diff --git a/apps/participation/models.py b/apps/participation/models.py
index d032cba..363b3dd 100644
--- a/apps/participation/models.py
+++ b/apps/participation/models.py
@@ -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,))
diff --git a/apps/participation/templates/participation/pool_detail.html b/apps/participation/templates/participation/pool_detail.html
index 7f5662d..f80cac9 100644
--- a/apps/participation/templates/participation/pool_detail.html
+++ b/apps/participation/templates/participation/pool_detail.html
@@ -54,6 +54,7 @@
+
{% endif %}
@@ -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")
+ });
});
{% endblock %}
diff --git a/apps/participation/templates/participation/upload_notes.html b/apps/participation/templates/participation/upload_notes.html
new file mode 100644
index 0000000..2e88851
--- /dev/null
+++ b/apps/participation/templates/participation/upload_notes.html
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{% load crispy_forms_tags %}
+{% load i18n %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/apps/participation/urls.py b/apps/participation/urls.py
index 369a677..d37abb3 100644
--- a/apps/participation/urls.py
+++ b/apps/participation/urls.py
@@ -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//", PoolDetailView.as_view(), name="pool_detail"),
path("pools//update/", PoolUpdateView.as_view(), name="pool_update"),
path("pools//update-teams/", PoolUpdateTeamsView.as_view(), name="pool_update_teams"),
+ path("pools//upload-notes/", PoolUploadNotesView.as_view(), name="pool_upload_notes"),
path("pools/passages/add//", PassageCreateView.as_view(), name="passage_create"),
path("pools/passages//", PassageDetailView.as_view(), name="passage_detail"),
path("pools/passages//update/", PassageUpdateView.as_view(), name="passage_update"),
diff --git a/apps/participation/views.py b/apps/participation/views.py
index b04e718..e10ab8a 100644
--- a/apps/participation/views.py
+++ b/apps/participation/views.py
@@ -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
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index fe019db..d044ccb 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -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 \n"
"Language-Team: LANGUAGE \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 our Hello Asso page. To make the validation of the "
"payment easier, please use the same e-mail "
"address that you use on this platform. 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 notre page Hello Asso. Pour rendre la validation du "
"paiement plus facile, merci d'utiliser la même "
"adresse e-mail que vous utilisez sur cette plateforme. 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 contact@tfjm.org to find "
-"a solution to your difficulties."
+"If any payment mean is available to you, please contact us at contact@tfjm.org "
+"to find a solution to your difficulties."
msgstr ""
"Si aucun moyen de paiement ne vous convient, merci de nous contecter à "
-"l'adresse contact@tfjm.org pour que nous puissions trouver une solution à vos "
-"difficultés."
+"l'adresse contact@tfjm.org 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 this link."
+"by email. You can resend a mail by clicking on this link."
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 ce lien."
+"avez reçu par mail. Vous pouvez renvoyer un mail en cliquant sur ce lien."
#: 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 :"