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:
parent
5f2cd16071
commit
d18f76cf80
@ -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()
|
||||
|
@ -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,))
|
||||
|
||||
|
@ -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 %}
|
||||
|
14
apps/participation/templates/participation/upload_notes.html
Normal file
14
apps/participation/templates/participation/upload_notes.html
Normal 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 %}
|
@ -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"),
|
||||
|
@ -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
|
||||
|
@ -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 :"
|
||||
|
Loading…
Reference in New Issue
Block a user