mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2024-12-25 07:42:22 +00:00
Add CSV export for tournaments
This commit is contained in:
parent
0fd9222055
commit
3e46d06817
@ -62,6 +62,7 @@
|
|||||||
{% if user.registration.is_admin or user.registration in tournament.organizers.all %}
|
{% if user.registration.is_admin or user.registration in tournament.organizers.all %}
|
||||||
<div class="card-footer text-center">
|
<div class="card-footer text-center">
|
||||||
<a href="{% url "participation:tournament_update" pk=tournament.pk %}"><button class="btn btn-secondary">{% trans "Edit tournament" %}</button></a>
|
<a href="{% url "participation:tournament_update" pk=tournament.pk %}"><button class="btn btn-secondary">{% trans "Edit tournament" %}</button></a>
|
||||||
|
<a href="{% url "participation:tournament_csv" pk=tournament.pk %}"><button class="btn btn-success">{% trans "Export as CSV" %}</button></a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,7 @@ from .views import CreateTeamView, JoinTeamView, MyParticipationDetailView, MyTe
|
|||||||
ParticipationDetailView, PassageCreateView, PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, \
|
ParticipationDetailView, PassageCreateView, PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, \
|
||||||
PoolUpdateTeamsView, PoolUpdateView, SolutionUploadView, SynthesisUploadView, TeamAuthorizationsView, \
|
PoolUpdateTeamsView, PoolUpdateView, SolutionUploadView, SynthesisUploadView, TeamAuthorizationsView, \
|
||||||
TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, TeamUploadMotivationLetterView, TournamentCreateView, \
|
TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, TeamUploadMotivationLetterView, TournamentCreateView, \
|
||||||
TournamentDetailView, TournamentListView, TournamentUpdateView
|
TournamentDetailView, TournamentExportCSVView, TournamentListView, TournamentUpdateView
|
||||||
|
|
||||||
|
|
||||||
app_name = "participation"
|
app_name = "participation"
|
||||||
@ -31,6 +31,7 @@ urlpatterns = [
|
|||||||
path("tournament/create/", TournamentCreateView.as_view(), name="tournament_create"),
|
path("tournament/create/", TournamentCreateView.as_view(), name="tournament_create"),
|
||||||
path("tournament/<int:pk>/", TournamentDetailView.as_view(), name="tournament_detail"),
|
path("tournament/<int:pk>/", TournamentDetailView.as_view(), name="tournament_detail"),
|
||||||
path("tournament/<int:pk>/update/", TournamentUpdateView.as_view(), name="tournament_update"),
|
path("tournament/<int:pk>/update/", TournamentUpdateView.as_view(), name="tournament_update"),
|
||||||
|
path("tournament/<int:pk>/csv/", TournamentExportCSVView.as_view(), name="tournament_csv"),
|
||||||
path("pools/create/", PoolCreateView.as_view(), name="pool_create"),
|
path("pools/create/", PoolCreateView.as_view(), name="pool_create"),
|
||||||
path("pools/<int:pk>/", PoolDetailView.as_view(), name="pool_detail"),
|
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/", PoolUpdateView.as_view(), name="pool_update"),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Copyright (C) 2020 by Animath
|
# Copyright (C) 2020 by Animath
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import csv
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import os
|
import os
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
@ -180,13 +180,13 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
|
|||||||
# A team is complete when there are at least 4 members plus a coache that have sent their authorizations,
|
# A team is complete when there are at least 4 members plus a coache that have sent their authorizations,
|
||||||
# their health sheet, they confirmed their email address and under-18 people sent their parental authorization.
|
# their health sheet, they confirmed their email address and under-18 people sent their parental authorization.
|
||||||
context["can_validate"] = team.students.count() >= 4 and team.coaches.exists() and \
|
context["can_validate"] = team.students.count() >= 4 and team.coaches.exists() and \
|
||||||
team.participation.tournament and \
|
team.participation.tournament and \
|
||||||
all(r.photo_authorization for r in team.participants.all()) and \
|
all(r.photo_authorization for r in team.participants.all()) and \
|
||||||
(team.participation.tournament.remote
|
(team.participation.tournament.remote
|
||||||
or all(r.health_sheet for r in team.students.all() if r.under_18)) and \
|
or all(r.health_sheet for r in team.students.all() if r.under_18)) and \
|
||||||
(team.participation.tournament.remote
|
(team.participation.tournament.remote
|
||||||
or all(r.parental_authorization for r in team.students.all() if r.under_18)) and \
|
or all(r.parental_authorization for r in team.students.all() if r.under_18)) and \
|
||||||
team.motivation_letter
|
team.motivation_letter
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@ -346,6 +346,7 @@ class MotivationLetterView(LoginRequiredMixin, View):
|
|||||||
"""
|
"""
|
||||||
Display the sent motivation letter.
|
Display the sent motivation letter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
filename = kwargs["filename"]
|
filename = kwargs["filename"]
|
||||||
path = f"media/authorization/motivation_letters/{filename}"
|
path = f"media/authorization/motivation_letters/{filename}"
|
||||||
@ -459,6 +460,7 @@ class MyParticipationDetailView(LoginRequiredMixin, RedirectView):
|
|||||||
"""
|
"""
|
||||||
Redirects to the detail view of the participation of the team.
|
Redirects to the detail view of the participation of the team.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
registration = user.registration
|
registration = user.registration
|
||||||
@ -557,6 +559,40 @@ class TournamentDetailView(DetailView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class TournamentExportCSVView(VolunteerMixin, DetailView):
|
||||||
|
"""
|
||||||
|
Export team information in a CSV file.
|
||||||
|
"""
|
||||||
|
model = Tournament
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
tournament = self.get_object()
|
||||||
|
|
||||||
|
resp = HttpResponse(
|
||||||
|
content_type='text/csv',
|
||||||
|
headers={'Content-Disposition': f'attachment; filename="Tournoi de {tournament.name}.csv"'},
|
||||||
|
)
|
||||||
|
writer = csv.DictWriter(resp, ('Tournoi', 'Équipe', 'Trigramme', 'Nom', 'Prénom', 'Genre', 'Date de naissance'))
|
||||||
|
writer.writeheader()
|
||||||
|
|
||||||
|
for participation in tournament.participations.filter(valid=True).order_by('team__trigram').all():
|
||||||
|
for registration in participation.team.participants\
|
||||||
|
.order_by('coachregistration', 'user__last_name').all():
|
||||||
|
writer.writerow({
|
||||||
|
'Tournoi': tournament.name,
|
||||||
|
'Équipe': participation.team.name,
|
||||||
|
'Trigramme': participation.team.trigram,
|
||||||
|
'Nom': registration.user.last_name,
|
||||||
|
'Prénom': registration.user.first_name,
|
||||||
|
'Genre': registration.get_gender_display() if isinstance(registration, StudentRegistration)
|
||||||
|
else 'Encandrant⋅e',
|
||||||
|
'Date de naissance': registration.birth_date if isinstance(registration, StudentRegistration)
|
||||||
|
else 'Encandrant⋅e',
|
||||||
|
})
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
class SolutionUploadView(LoginRequiredMixin, FormView):
|
class SolutionUploadView(LoginRequiredMixin, FormView):
|
||||||
template_name = "participation/upload_solution.html"
|
template_name = "participation/upload_solution.html"
|
||||||
form_class = SolutionForm
|
form_class = SolutionForm
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: TFJM\n"
|
"Project-Id-Version: TFJM\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-05-11 17:03+0200\n"
|
"POT-Creation-Date: 2022-04-22 18:01+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: Yohann D'ANELLO <yohann.danello@animath.fr>\n"
|
"Last-Translator: Yohann D'ANELLO <yohann.danello@animath.fr>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -104,59 +104,59 @@ msgstr "Changelog de type \"{action}\" pour le modèle {model} le {timestamp}"
|
|||||||
msgid "valid"
|
msgid "valid"
|
||||||
msgstr "valide"
|
msgstr "valide"
|
||||||
|
|
||||||
#: apps/participation/forms.py:24
|
#: apps/participation/forms.py:25
|
||||||
msgid "This name is already used."
|
msgid "This name is already used."
|
||||||
msgstr "Ce nom est déjà utilisé."
|
msgstr "Ce nom est déjà utilisé."
|
||||||
|
|
||||||
#: apps/participation/forms.py:31 apps/participation/models.py:41
|
#: apps/participation/forms.py:32 apps/participation/models.py:41
|
||||||
msgid "The trigram must be composed of three uppercase letters."
|
msgid "The trigram must be composed of three uppercase letters."
|
||||||
msgstr "Le trigramme doit être composé de trois lettres majuscules."
|
msgstr "Le trigramme doit être composé de trois lettres majuscules."
|
||||||
|
|
||||||
#: apps/participation/forms.py:34
|
#: apps/participation/forms.py:35
|
||||||
msgid "This trigram is already used."
|
msgid "This trigram is already used."
|
||||||
msgstr "Ce trigramme est déjà utilisé."
|
msgstr "Ce trigramme est déjà utilisé."
|
||||||
|
|
||||||
#: apps/participation/forms.py:49
|
#: apps/participation/forms.py:50
|
||||||
msgid "No team was found with this access code."
|
msgid "No team was found with this access code."
|
||||||
msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
|
msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
|
||||||
|
|
||||||
#: apps/participation/forms.py:78 apps/participation/forms.py:213
|
#: apps/participation/forms.py:79 apps/participation/forms.py:215
|
||||||
#: apps/registration/forms.py:117 apps/registration/forms.py:139
|
#: apps/registration/forms.py:117 apps/registration/forms.py:139
|
||||||
#: apps/registration/forms.py:161 apps/registration/forms.py:215
|
#: apps/registration/forms.py:161 apps/registration/forms.py:215
|
||||||
msgid "The uploaded file size must be under 2 Mo."
|
msgid "The uploaded file size must be under 2 Mo."
|
||||||
msgstr "Le fichier envoyé doit peser moins de 2 Mo."
|
msgstr "Le fichier envoyé doit peser moins de 2 Mo."
|
||||||
|
|
||||||
#: apps/participation/forms.py:80 apps/registration/forms.py:119
|
#: apps/participation/forms.py:81 apps/registration/forms.py:119
|
||||||
#: apps/registration/forms.py:141 apps/registration/forms.py:163
|
#: apps/registration/forms.py:141 apps/registration/forms.py:163
|
||||||
#: apps/registration/forms.py:217
|
#: apps/registration/forms.py:217
|
||||||
msgid "The uploaded file must be a PDF, PNG of JPEG file."
|
msgid "The uploaded file must be a PDF, PNG of JPEG file."
|
||||||
msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG."
|
msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG."
|
||||||
|
|
||||||
#: apps/participation/forms.py:98
|
#: apps/participation/forms.py:99
|
||||||
msgid "I engage myself to participate to the whole TFJM²."
|
msgid "I engage myself to participate to the whole TFJM²."
|
||||||
msgstr "Je m'engage à participer à l'intégralité du TFJM²."
|
msgstr "Je m'engage à participer à l'intégralité du TFJM²."
|
||||||
|
|
||||||
#: apps/participation/forms.py:113
|
#: apps/participation/forms.py:114
|
||||||
msgid "Message to address to the team:"
|
msgid "Message to address to the team:"
|
||||||
msgstr "Message à adresser à l'équipe :"
|
msgstr "Message à adresser à l'équipe :"
|
||||||
|
|
||||||
#: apps/participation/forms.py:150
|
#: apps/participation/forms.py:152
|
||||||
msgid "The uploaded file size must be under 5 Mo."
|
msgid "The uploaded file size must be under 5 Mo."
|
||||||
msgstr "Le fichier envoyé doit peser moins de 5 Mo."
|
msgstr "Le fichier envoyé doit peser moins de 5 Mo."
|
||||||
|
|
||||||
#: apps/participation/forms.py:152 apps/participation/forms.py:215
|
#: apps/participation/forms.py:154 apps/participation/forms.py:217
|
||||||
msgid "The uploaded file must be a PDF file."
|
msgid "The uploaded file must be a PDF file."
|
||||||
msgstr "Le fichier envoyé doit être au format PDF."
|
msgstr "Le fichier envoyé doit être au format PDF."
|
||||||
|
|
||||||
#: apps/participation/forms.py:156
|
#: apps/participation/forms.py:158
|
||||||
msgid "The PDF file must not have more than 30 pages."
|
msgid "The PDF file must not have more than 30 pages."
|
||||||
msgstr "Le fichier PDF ne doit pas avoir plus de 30 pages."
|
msgstr "Le fichier PDF ne doit pas avoir plus de 30 pages."
|
||||||
|
|
||||||
#: apps/participation/forms.py:196
|
#: apps/participation/forms.py:198
|
||||||
msgid "The defender, the opponent and the reporter must be different."
|
msgid "The defender, the opponent and the reporter must be different."
|
||||||
msgstr "Le défenseur, l'opposant et le rapporteur doivent être différents."
|
msgstr "Le défenseur, l'opposant et le rapporteur doivent être différents."
|
||||||
|
|
||||||
#: apps/participation/forms.py:200
|
#: apps/participation/forms.py:202
|
||||||
msgid "This defender did not work on this problem."
|
msgid "This defender did not work on this problem."
|
||||||
msgstr "Ce défenseur ne travaille pas sur ce problème."
|
msgstr "Ce défenseur ne travaille pas sur ce problème."
|
||||||
|
|
||||||
@ -750,7 +750,7 @@ msgid "BigBlueButton link:"
|
|||||||
msgstr "Lien BigBlueButton :"
|
msgstr "Lien BigBlueButton :"
|
||||||
|
|
||||||
#: apps/participation/templates/participation/pool_detail.html:41
|
#: apps/participation/templates/participation/pool_detail.html:41
|
||||||
#: apps/participation/templates/participation/tournament_detail.html:94
|
#: apps/participation/templates/participation/tournament_detail.html:95
|
||||||
msgid "Ranking"
|
msgid "Ranking"
|
||||||
msgstr "Classement"
|
msgstr "Classement"
|
||||||
|
|
||||||
@ -769,7 +769,7 @@ msgid "Passages"
|
|||||||
msgstr "Passages"
|
msgstr "Passages"
|
||||||
|
|
||||||
#: apps/participation/templates/participation/pool_detail.html:68
|
#: apps/participation/templates/participation/pool_detail.html:68
|
||||||
#: apps/participation/templates/participation/tournament_detail.html:108
|
#: apps/participation/templates/participation/tournament_detail.html:109
|
||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr "Ajouter"
|
msgstr "Ajouter"
|
||||||
|
|
||||||
@ -910,7 +910,7 @@ msgid "Update team"
|
|||||||
msgstr "Modifier l'équipe"
|
msgstr "Modifier l'équipe"
|
||||||
|
|
||||||
#: apps/participation/templates/participation/team_detail.html:181
|
#: apps/participation/templates/participation/team_detail.html:181
|
||||||
#: apps/participation/views.py:429
|
#: apps/participation/views.py:430
|
||||||
msgid "Leave team"
|
msgid "Leave team"
|
||||||
msgstr "Quitter l'équipe"
|
msgstr "Quitter l'équipe"
|
||||||
|
|
||||||
@ -984,20 +984,24 @@ msgstr "Pour contacter les équipes valides"
|
|||||||
msgid "Edit tournament"
|
msgid "Edit tournament"
|
||||||
msgstr "Modifier le tournoi"
|
msgstr "Modifier le tournoi"
|
||||||
|
|
||||||
#: apps/participation/templates/participation/tournament_detail.html:71
|
#: apps/participation/templates/participation/tournament_detail.html:65
|
||||||
|
msgid "Export as CSV"
|
||||||
|
msgstr "Exporter en CSV"
|
||||||
|
|
||||||
|
#: apps/participation/templates/participation/tournament_detail.html:72
|
||||||
#: tfjm/templates/base.html:65
|
#: tfjm/templates/base.html:65
|
||||||
msgid "Teams"
|
msgid "Teams"
|
||||||
msgstr "Équipes"
|
msgstr "Équipes"
|
||||||
|
|
||||||
#: apps/participation/templates/participation/tournament_detail.html:79
|
#: apps/participation/templates/participation/tournament_detail.html:80
|
||||||
msgid "Pools"
|
msgid "Pools"
|
||||||
msgstr "Poules"
|
msgstr "Poules"
|
||||||
|
|
||||||
#: apps/participation/templates/participation/tournament_detail.html:86
|
#: apps/participation/templates/participation/tournament_detail.html:87
|
||||||
msgid "Add new pool"
|
msgid "Add new pool"
|
||||||
msgstr "Ajouter une nouvelle poule"
|
msgstr "Ajouter une nouvelle poule"
|
||||||
|
|
||||||
#: apps/participation/templates/participation/tournament_detail.html:107
|
#: apps/participation/templates/participation/tournament_detail.html:108
|
||||||
msgid "Add pool"
|
msgid "Add pool"
|
||||||
msgstr "Ajouter une poule"
|
msgstr "Ajouter une poule"
|
||||||
|
|
||||||
@ -1036,12 +1040,12 @@ msgstr "Vous êtes déjà dans une équipe."
|
|||||||
msgid "Join team"
|
msgid "Join team"
|
||||||
msgstr "Rejoindre une équipe"
|
msgstr "Rejoindre une équipe"
|
||||||
|
|
||||||
#: apps/participation/views.py:150 apps/participation/views.py:435
|
#: apps/participation/views.py:150 apps/participation/views.py:436
|
||||||
#: apps/participation/views.py:468
|
#: apps/participation/views.py:470
|
||||||
msgid "You are not in a team."
|
msgid "You are not in a team."
|
||||||
msgstr "Vous n'êtes pas dans une équipe."
|
msgstr "Vous n'êtes pas dans une équipe."
|
||||||
|
|
||||||
#: apps/participation/views.py:151 apps/participation/views.py:469
|
#: apps/participation/views.py:151 apps/participation/views.py:471
|
||||||
msgid "You don't participate, so you don't have any team."
|
msgid "You don't participate, so you don't have any team."
|
||||||
msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe."
|
msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe."
|
||||||
|
|
||||||
@ -1086,49 +1090,49 @@ msgstr "Vous devez spécifier si vous validez l'inscription ou non."
|
|||||||
msgid "Update team {trigram}"
|
msgid "Update team {trigram}"
|
||||||
msgstr "Mise à jour de l'équipe {trigram}"
|
msgstr "Mise à jour de l'équipe {trigram}"
|
||||||
|
|
||||||
#: apps/participation/views.py:365 apps/participation/views.py:415
|
#: apps/participation/views.py:366 apps/participation/views.py:416
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Motivation letter of {team}.{ext}"
|
msgid "Motivation letter of {team}.{ext}"
|
||||||
msgstr "Lettre de motivation de {team}.{ext}"
|
msgstr "Lettre de motivation de {team}.{ext}"
|
||||||
|
|
||||||
#: apps/participation/views.py:396
|
#: apps/participation/views.py:397
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Photo authorization of {participant}.{ext}"
|
msgid "Photo authorization of {participant}.{ext}"
|
||||||
msgstr "Autorisation de droit à l'image de {participant}.{ext}"
|
msgstr "Autorisation de droit à l'image de {participant}.{ext}"
|
||||||
|
|
||||||
#: apps/participation/views.py:402
|
#: apps/participation/views.py:403
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Parental authorization of {participant}.{ext}"
|
msgid "Parental authorization of {participant}.{ext}"
|
||||||
msgstr "Autorisation parentale de {participant}.{ext}"
|
msgstr "Autorisation parentale de {participant}.{ext}"
|
||||||
|
|
||||||
#: apps/participation/views.py:409
|
#: apps/participation/views.py:410
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Health sheet of {participant}.{ext}"
|
msgid "Health sheet of {participant}.{ext}"
|
||||||
msgstr "Fiche sanitaire de {participant}.{ext}"
|
msgstr "Fiche sanitaire de {participant}.{ext}"
|
||||||
|
|
||||||
#: apps/participation/views.py:419
|
#: apps/participation/views.py:420
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Photo authorizations of team {trigram}.zip"
|
msgid "Photo authorizations of team {trigram}.zip"
|
||||||
msgstr "Autorisations de droit à l'image de l'équipe {trigram}.zip"
|
msgstr "Autorisations de droit à l'image de l'équipe {trigram}.zip"
|
||||||
|
|
||||||
#: apps/participation/views.py:437
|
#: apps/participation/views.py:438
|
||||||
msgid "The team is already validated or the validation is pending."
|
msgid "The team is already validated or the validation is pending."
|
||||||
msgstr "La validation de l'équipe est déjà faite ou en cours."
|
msgstr "La validation de l'équipe est déjà faite ou en cours."
|
||||||
|
|
||||||
#: apps/participation/views.py:483
|
#: apps/participation/views.py:485
|
||||||
msgid "The team is not validated yet."
|
msgid "The team is not validated yet."
|
||||||
msgstr "L'équipe n'est pas encore validée."
|
msgstr "L'équipe n'est pas encore validée."
|
||||||
|
|
||||||
#: apps/participation/views.py:497
|
#: apps/participation/views.py:499
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Participation of team {trigram}"
|
msgid "Participation of team {trigram}"
|
||||||
msgstr "Participation de l'équipe {trigram}"
|
msgstr "Participation de l'équipe {trigram}"
|
||||||
|
|
||||||
#: apps/participation/views.py:589
|
#: apps/participation/views.py:625
|
||||||
msgid "You can't upload a solution after the deadline."
|
msgid "You can't upload a solution after the deadline."
|
||||||
msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
|
msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
|
||||||
|
|
||||||
#: apps/participation/views.py:775
|
#: apps/participation/views.py:811
|
||||||
msgid "You can't upload a synthesis after the deadline."
|
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."
|
msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user