Add extra access to juries
This commit is contained in:
parent
522ed088ef
commit
3d9e7136ac
|
@ -1,6 +1,6 @@
|
|||
import random
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, AccessMixin
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db.models import Q
|
||||
|
@ -13,7 +13,7 @@ from django.views import View
|
|||
from django.views.generic import CreateView, UpdateView, DetailView, FormView
|
||||
from django_tables2 import SingleTableView
|
||||
from tournament.forms import TeamForm, JoinTeam
|
||||
from tournament.models import Team, Tournament
|
||||
from tournament.models import Team, Tournament, Pool
|
||||
from tournament.views import AdminMixin, TeamMixin, OrgaMixin
|
||||
|
||||
from .forms import SignUpForm, TFJMUserForm, AdminUserForm, CoachUserForm
|
||||
|
@ -177,7 +177,7 @@ class MyTeamView(TeamMixin, View):
|
|||
return redirect("tournament:team_detail", pk=request.user.team.pk)
|
||||
|
||||
|
||||
class DocumentView(LoginRequiredMixin, View):
|
||||
class DocumentView(AccessMixin, View):
|
||||
"""
|
||||
View a PDF document, if we have the right.
|
||||
|
||||
|
@ -194,14 +194,11 @@ class DocumentView(LoginRequiredMixin, View):
|
|||
raise Http404(_("No %(verbose_name)s found matching the query") %
|
||||
{'verbose_name': Document._meta.verbose_name})
|
||||
|
||||
if request.user.is_authenticated:
|
||||
grant = request.user.admin
|
||||
|
||||
if isinstance(doc, Solution) or isinstance(doc, Synthesis) or isinstance(doc, MotivationLetter):
|
||||
grant = grant or doc.team == request.user.team or request.user in doc.team.tournament.organizers.all()
|
||||
grant = grant or (doc.team.selected_for_final and request.user in Tournament.get_final().organizers.all())
|
||||
|
||||
if isinstance(doc, Synthesis) and request.user.organizes:
|
||||
grant = True
|
||||
grant = grant or doc.team == request.user.team or request.user in doc.tournament.organizers.all()
|
||||
|
||||
if isinstance(doc, Solution):
|
||||
for pool in doc.pools.all():
|
||||
|
@ -212,6 +209,23 @@ class DocumentView(LoginRequiredMixin, View):
|
|||
continue
|
||||
if self.request.user.team in pool.teams.all():
|
||||
grant = True
|
||||
elif isinstance(doc, Synthesis):
|
||||
for pool in request.user.pools.all(): # If the user is a jury in the pool
|
||||
if doc.team in pool.teams.all() and doc.final == pool.tournament.final:
|
||||
grant = True
|
||||
break
|
||||
else:
|
||||
pool = Pool.objects.filter(extra_access_token=self.request.session["extra_access_token"])
|
||||
if pool.exists():
|
||||
pool = pool.get()
|
||||
if isinstance(doc, Solution):
|
||||
grant = doc in pool.solutions.all()
|
||||
elif isinstance(doc, Synthesis):
|
||||
grant = doc.team in pool.teams.all() and doc.final == pool.tournament.final
|
||||
else:
|
||||
grant = False
|
||||
else:
|
||||
grant = False
|
||||
|
||||
if not grant:
|
||||
raise PermissionDenied
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import random
|
||||
|
||||
from django.core.mail import send_mail
|
||||
from django.db import models
|
||||
|
@ -338,6 +339,13 @@ class Pool(models.Model):
|
|||
verbose_name=_("juries"),
|
||||
)
|
||||
|
||||
extra_access_token = models.CharField(
|
||||
max_length=64,
|
||||
default="",
|
||||
verbose_name=_("extra access token"),
|
||||
help_text=_("Let other users access to the pool data without logging in."),
|
||||
)
|
||||
|
||||
@property
|
||||
def problems(self):
|
||||
"""
|
||||
|
@ -361,6 +369,13 @@ class Pool(models.Model):
|
|||
from member.models import Synthesis
|
||||
return Synthesis.objects.filter(team__in=self.teams.all(), round=self.round, final=self.tournament.final)
|
||||
|
||||
def save(self, **kwargs):
|
||||
if not self.extra_access_token:
|
||||
alphabet = "0123456789abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
code = "".join(random.choice(alphabet) for _ in range(64))
|
||||
self.extra_access_token = code
|
||||
super().save(**kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("pool")
|
||||
verbose_name_plural = _("pools")
|
||||
|
|
|
@ -3,7 +3,7 @@ import zipfile
|
|||
from datetime import timedelta
|
||||
from io import BytesIO
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, AccessMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.mail import send_mail
|
||||
from django.db.models import Q
|
||||
|
@ -34,13 +34,15 @@ class AdminMixin(LoginRequiredMixin):
|
|||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class OrgaMixin(LoginRequiredMixin):
|
||||
class OrgaMixin(AccessMixin):
|
||||
"""
|
||||
If a view extends this mixin, then the view will be only accessible to administrators or organizers.
|
||||
"""
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated or not request.user.organizes:
|
||||
if not request.user.is_authenticated and not request.session["extra_access_token"]:
|
||||
return self.handle_no_permission()
|
||||
elif request.user.is_authenticated and not request.user.organizes:
|
||||
raise PermissionDenied
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
@ -247,7 +249,7 @@ class TeamDetailView(LoginRequiredMixin, DetailView):
|
|||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context["title"] = _("Information about team")
|
||||
context["ordered_solutions"] = self.object.solutions.order_by('problem').all()
|
||||
context["ordered_solutions"] = self.object.solutions.order_by('final', 'problem',).all()
|
||||
context["team_users_emails"] = [user.email for user in self.object.users.all()]
|
||||
|
||||
return context
|
||||
|
@ -399,6 +401,7 @@ class SolutionsOrgaListView(OrgaMixin, SingleTableView):
|
|||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
if self.request.user.is_authenticated:
|
||||
context["tournaments"] = \
|
||||
Tournament.objects if self.request.user.admin else self.request.user.organized_tournaments
|
||||
|
||||
|
@ -406,12 +409,14 @@ class SolutionsOrgaListView(OrgaMixin, SingleTableView):
|
|||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
if not self.request.user.admin:
|
||||
if self.request.user.is_authenticated and not self.request.user.admin:
|
||||
if self.request.user in Tournament.get_final().organizers.all():
|
||||
qs = qs.filter(Q(team__tournament__organizers=self.request.user) | Q(pools__juries=self.request.user)
|
||||
| Q(final=True))
|
||||
else:
|
||||
qs = qs.filter(Q(team__tournament__organizers=self.request.user) | Q(pools__juries=self.request.user))
|
||||
elif not self.request.user.is_authenticated:
|
||||
qs = qs.filter(pools__extra_access_token=self.request.session["extra_access_token"])
|
||||
return qs.order_by('final', 'team__tournament__date_start', 'team__tournament__name', 'team__trigram',
|
||||
'problem',).distinct()
|
||||
|
||||
|
@ -529,6 +534,7 @@ class SynthesesOrgaListView(OrgaMixin, SingleTableView):
|
|||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
if self.request.user.is_authenticated:
|
||||
context["tournaments"] = \
|
||||
Tournament.objects if self.request.user.admin else self.request.user.organized_tournaments
|
||||
|
||||
|
@ -536,7 +542,7 @@ class SynthesesOrgaListView(OrgaMixin, SingleTableView):
|
|||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
if not self.request.user.admin:
|
||||
if self.request.user.is_authenticated and not self.request.user.admin:
|
||||
if self.request.user in Tournament.get_final().organizers.all():
|
||||
qs = qs.filter(Q(team__tournament__organizers=self.request.user)
|
||||
| Q(team__pools__juries=self.request.user)
|
||||
|
@ -544,11 +550,18 @@ class SynthesesOrgaListView(OrgaMixin, SingleTableView):
|
|||
else:
|
||||
qs = qs.filter(Q(team__tournament__organizers=self.request.user)
|
||||
| Q(team__pools__juries=self.request.user))
|
||||
elif not self.request.user.is_authenticated:
|
||||
pool = Pool.objects.filter(extra_access_token=self.request.session["extra_access_token"])
|
||||
if pool.exists():
|
||||
pool = pool.get()
|
||||
qs = qs.filter(team__pools=pool, final=pool.tournament.final)
|
||||
else:
|
||||
qs = qs.none()
|
||||
return qs.order_by('final', 'team__tournament__date_start', 'team__tournament__name', 'team__trigram',
|
||||
'round', 'source',).distinct()
|
||||
|
||||
|
||||
class PoolListView(LoginRequiredMixin, SingleTableView):
|
||||
class PoolListView(SingleTableView):
|
||||
"""
|
||||
View the list of visible pools.
|
||||
Admins see all, juries see their own pools, organizers see the pools of their tournaments.
|
||||
|
@ -560,10 +573,13 @@ class PoolListView(LoginRequiredMixin, SingleTableView):
|
|||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
user = self.request.user
|
||||
if user.is_authenticated:
|
||||
if not user.admin and user.organizes:
|
||||
qs = qs.filter(Q(juries=user) | Q(teams__tournament__organizers=user))
|
||||
elif user.participates:
|
||||
qs = qs.filter(teams=user.team)
|
||||
else:
|
||||
qs = qs.filter(extra_access_token=self.request.session["extra_access_token"])
|
||||
qs = qs.distinct().order_by('id')
|
||||
return qs
|
||||
|
||||
|
@ -581,7 +597,7 @@ class PoolCreateView(AdminMixin, CreateView):
|
|||
return reverse_lazy("tournament:pools")
|
||||
|
||||
|
||||
class PoolDetailView(LoginRequiredMixin, DetailView):
|
||||
class PoolDetailView(DetailView):
|
||||
"""
|
||||
See the detail of a pool.
|
||||
Teams and juries can download here defended solutions of the pool.
|
||||
|
@ -597,10 +613,13 @@ class PoolDetailView(LoginRequiredMixin, DetailView):
|
|||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
user = self.request.user
|
||||
if user.is_authenticated:
|
||||
if not user.admin and user.organizes:
|
||||
qs = qs.filter(Q(juries=user) | Q(teams__tournament__organizers=user))
|
||||
elif user.participates:
|
||||
qs = qs.filter(teams=user.team)
|
||||
else:
|
||||
qs = qs.filter(extra_access_token=self.request.session["extra_access_token"])
|
||||
return qs.distinct()
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
@ -608,7 +627,8 @@ class PoolDetailView(LoginRequiredMixin, DetailView):
|
|||
pool = self.get_object()
|
||||
|
||||
if "solutions_zip" in request.POST:
|
||||
if user.participates and pool.round == 2 and pool.tournament.date_solutions_2 > timezone.now():
|
||||
if user.is_authenticated and user.participates and pool.round == 2\
|
||||
and pool.tournament.date_solutions_2 > timezone.now():
|
||||
raise PermissionDenied
|
||||
|
||||
out = BytesIO()
|
||||
|
@ -624,10 +644,7 @@ class PoolDetailView(LoginRequiredMixin, DetailView):
|
|||
.format(_("Solutions of a pool for the round {round} of the tournament {tournament}.zip")
|
||||
.format(round=pool.round, tournament=str(pool.tournament)).replace(" ", "%20"))
|
||||
return resp
|
||||
elif "syntheses_zip" in request.POST and user.organizes:
|
||||
if user.participates and pool.round == 2 and pool.tournament.date_solutions_2 > timezone.now():
|
||||
raise PermissionDenied
|
||||
|
||||
elif "syntheses_zip" in request.POST and (not user.is_authenticated or user.organizes):
|
||||
out = BytesIO()
|
||||
zf = zipfile.ZipFile(out, "w")
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: TFJM2\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-05-12 18:25+0200\n"
|
||||
"POT-Creation-Date: 2020-05-25 18:23+0200\n"
|
||||
"PO-Revision-Date: 2020-04-29 02:30+0000\n"
|
||||
"Last-Translator: Yohann D'ANELLO <yohann.danello@animath.fr>\n"
|
||||
"Language-Team: fr <LL@li.org>\n"
|
||||
|
@ -44,11 +44,11 @@ msgstr "Adresse électronique"
|
|||
|
||||
#: apps/member/models.py:22
|
||||
msgid "This should be valid and will be controlled."
|
||||
msgstr ""
|
||||
msgstr "Elle doit être valide et sera contrôlée."
|
||||
|
||||
#: apps/member/models.py:30 apps/member/models.py:244 apps/member/models.py:263
|
||||
#: apps/member/models.py:306 apps/tournament/models.py:285
|
||||
#: apps/tournament/models.py:385 templates/member/tfjmuser_detail.html:16
|
||||
#: apps/member/models.py:306 apps/tournament/models.py:286
|
||||
#: apps/tournament/models.py:400 templates/member/tfjmuser_detail.html:16
|
||||
msgid "team"
|
||||
msgstr "équipe"
|
||||
|
||||
|
@ -124,7 +124,7 @@ msgstr "téléphone du responsable"
|
|||
msgid "responsible email"
|
||||
msgstr "email du responsable"
|
||||
|
||||
#: apps/member/models.py:129 apps/tournament/models.py:44
|
||||
#: apps/member/models.py:129 apps/tournament/models.py:45
|
||||
#: templates/member/tfjmuser_detail.html:67
|
||||
#: templates/tournament/tournament_detail.html:42
|
||||
msgid "description"
|
||||
|
@ -138,13 +138,13 @@ msgstr "Administrateur"
|
|||
msgid "Organizer"
|
||||
msgstr "Organisateur"
|
||||
|
||||
#: apps/member/models.py:144 apps/tournament/models.py:89
|
||||
#: apps/tournament/models.py:214
|
||||
#: apps/member/models.py:144 apps/tournament/models.py:90
|
||||
#: apps/tournament/models.py:215
|
||||
msgid "year"
|
||||
msgstr "année"
|
||||
|
||||
#: apps/member/models.py:171 apps/member/models.py:214
|
||||
#: apps/tournament/models.py:378
|
||||
#: apps/tournament/models.py:393
|
||||
msgid "user"
|
||||
msgstr "utilisateur"
|
||||
|
||||
|
@ -180,7 +180,7 @@ msgstr "Autorisation de droit à l'image"
|
|||
msgid "Sanitary plug"
|
||||
msgstr "Fiche sanitaire"
|
||||
|
||||
#: apps/member/models.py:223 apps/tournament/models.py:396
|
||||
#: apps/member/models.py:223 apps/tournament/models.py:411
|
||||
msgid "Scholarship"
|
||||
msgstr "Bourse"
|
||||
|
||||
|
@ -226,7 +226,7 @@ msgstr "solution pour la finale"
|
|||
msgid "solution"
|
||||
msgstr "solution"
|
||||
|
||||
#: apps/member/models.py:286 apps/tournament/models.py:324
|
||||
#: apps/member/models.py:286 apps/tournament/models.py:325
|
||||
msgid "solutions"
|
||||
msgstr "solutions"
|
||||
|
||||
|
@ -253,15 +253,15 @@ msgstr "Rapporteur"
|
|||
msgid "source"
|
||||
msgstr "source"
|
||||
|
||||
#: apps/member/models.py:320 apps/tournament/models.py:329
|
||||
#: apps/member/models.py:320 apps/tournament/models.py:330
|
||||
msgid "Round 1"
|
||||
msgstr "Tour 1"
|
||||
|
||||
#: apps/member/models.py:321 apps/tournament/models.py:330
|
||||
#: apps/member/models.py:321 apps/tournament/models.py:331
|
||||
msgid "Round 2"
|
||||
msgstr "Tour 2"
|
||||
|
||||
#: apps/member/models.py:323 apps/tournament/models.py:332
|
||||
#: apps/member/models.py:323 apps/tournament/models.py:333
|
||||
#: templates/tournament/pool_detail.html:18
|
||||
msgid "round"
|
||||
msgstr "tour"
|
||||
|
@ -303,40 +303,45 @@ msgstr "configuration"
|
|||
msgid "configurations"
|
||||
msgstr "configurations"
|
||||
|
||||
#: apps/member/views.py:100 apps/member/views.py:140
|
||||
#: apps/member/views.py:105 apps/member/views.py:145
|
||||
msgid "You can't organize and participate at the same time."
|
||||
msgstr "Vous ne pouvez pas organiser et participer en même temps."
|
||||
|
||||
#: apps/member/views.py:104 apps/member/views.py:144
|
||||
#: apps/member/views.py:109 apps/member/views.py:149
|
||||
msgid "You are already in a team."
|
||||
msgstr "Vous êtes déjà dans une équipe."
|
||||
|
||||
#: apps/member/views.py:148
|
||||
#: apps/member/views.py:153
|
||||
msgid "This team is full of coachs."
|
||||
msgstr "Cette équipe est pleine en encadrants."
|
||||
|
||||
#: apps/member/views.py:152
|
||||
#: apps/member/views.py:157
|
||||
msgid "This team is full of participants."
|
||||
msgstr "Cette équipe est pleine en participants."
|
||||
|
||||
#: apps/member/views.py:156
|
||||
#: apps/member/views.py:161
|
||||
msgid "This team is already validated or waiting for validation."
|
||||
msgstr "L'équipe est déjà en attente de validation."
|
||||
|
||||
#: apps/member/views.py:220 templates/base.html:81
|
||||
#: apps/member/views.py:194
|
||||
#, python-format
|
||||
msgid "No %(verbose_name)s found matching the query"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/views.py:244 templates/base.html:81
|
||||
msgid "All profiles"
|
||||
msgstr "Tous les profils"
|
||||
|
||||
#: apps/member/views.py:232 templates/base.html:80
|
||||
#: apps/member/views.py:256 templates/base.html:80
|
||||
msgid "Orphaned profiles"
|
||||
msgstr "Profils orphelins"
|
||||
|
||||
#: apps/member/views.py:244 apps/tournament/forms.py:23 templates/base.html:83
|
||||
#: apps/member/views.py:268 apps/tournament/forms.py:23 templates/base.html:83
|
||||
msgid "Organizers"
|
||||
msgstr "Organisateurs"
|
||||
|
||||
#: apps/tournament/apps.py:10 apps/tournament/models.py:134
|
||||
#: apps/tournament/models.py:182 apps/tournament/tables.py:110
|
||||
#: apps/tournament/apps.py:10 apps/tournament/models.py:135
|
||||
#: apps/tournament/models.py:183 apps/tournament/tables.py:110
|
||||
#: templates/tournament/pool_detail.html:21
|
||||
#: templates/tournament/team_detail.html:21
|
||||
msgid "tournament"
|
||||
|
@ -400,7 +405,8 @@ msgstr "Problème n°%(problem)d"
|
|||
msgid ""
|
||||
"Please keep filesize under %(max_size)s. Current filesize %(current_size)s"
|
||||
msgstr ""
|
||||
"Merci de ne pas dépasser les %(max_size)s. Le fichier envoyé pèse %(current_size)s."
|
||||
"Merci de ne pas dépasser les %(max_size)s. Le fichier envoyé pèse "
|
||||
"%(current_size)s."
|
||||
|
||||
#: apps/tournament/forms.py:157 apps/tournament/forms.py:181
|
||||
msgid "The file should be a PDF file."
|
||||
|
@ -435,16 +441,16 @@ msgstr "Équipe 3"
|
|||
msgid "Problem defended by team 3"
|
||||
msgstr "Problème défendu par l'équipe 3"
|
||||
|
||||
#: apps/tournament/models.py:18 apps/tournament/models.py:169
|
||||
#: apps/tournament/models.py:19 apps/tournament/models.py:170
|
||||
#: templates/tournament/team_detail.html:12
|
||||
msgid "name"
|
||||
msgstr "nom"
|
||||
|
||||
#: apps/tournament/models.py:24 templates/tournament/tournament_detail.html:12
|
||||
#: apps/tournament/models.py:25 templates/tournament/tournament_detail.html:12
|
||||
msgid "organizers"
|
||||
msgstr "organisateurs"
|
||||
|
||||
#: apps/tournament/models.py:25
|
||||
#: apps/tournament/models.py:26
|
||||
msgid ""
|
||||
"List of all organizers that can see and manipulate data of the tournament "
|
||||
"and the teams."
|
||||
|
@ -452,71 +458,71 @@ msgstr ""
|
|||
"Liste des organisateurs qui peuvent manipuler les données du tournoi et des "
|
||||
"équipes."
|
||||
|
||||
#: apps/tournament/models.py:29 templates/tournament/tournament_detail.html:15
|
||||
#: apps/tournament/models.py:30 templates/tournament/tournament_detail.html:15
|
||||
msgid "size"
|
||||
msgstr "taille"
|
||||
|
||||
#: apps/tournament/models.py:30
|
||||
#: apps/tournament/models.py:31
|
||||
msgid "Number of teams that are allowed to join the tournament."
|
||||
msgstr "Nombre d'équipes qui sont autorisées à rejoindre le tournoi."
|
||||
|
||||
#: apps/tournament/models.py:35 templates/tournament/tournament_detail.html:18
|
||||
#: apps/tournament/models.py:36 templates/tournament/tournament_detail.html:18
|
||||
msgid "place"
|
||||
msgstr "lieu"
|
||||
|
||||
#: apps/tournament/models.py:39 templates/tournament/tournament_detail.html:21
|
||||
#: apps/tournament/models.py:40 templates/tournament/tournament_detail.html:21
|
||||
msgid "price"
|
||||
msgstr "prix"
|
||||
|
||||
#: apps/tournament/models.py:40
|
||||
#: apps/tournament/models.py:41
|
||||
msgid "Price asked to participants. Free with a scholarship."
|
||||
msgstr "Prix demandé par participant. Gratuit pour les boursiers."
|
||||
|
||||
#: apps/tournament/models.py:49
|
||||
#: apps/tournament/models.py:50
|
||||
msgid "date start"
|
||||
msgstr "date de début"
|
||||
|
||||
#: apps/tournament/models.py:54
|
||||
#: apps/tournament/models.py:55
|
||||
msgid "date end"
|
||||
msgstr "date de fin"
|
||||
|
||||
#: apps/tournament/models.py:59 templates/tournament/tournament_detail.html:27
|
||||
#: apps/tournament/models.py:60 templates/tournament/tournament_detail.html:27
|
||||
msgid "date of registration closing"
|
||||
msgstr "date de clôture des inscriptions"
|
||||
|
||||
#: apps/tournament/models.py:64 templates/tournament/tournament_detail.html:30
|
||||
#: apps/tournament/models.py:65 templates/tournament/tournament_detail.html:30
|
||||
msgid "date of maximal solution submission"
|
||||
msgstr "date d'envoi maximal des solutions"
|
||||
|
||||
#: apps/tournament/models.py:69 templates/tournament/tournament_detail.html:33
|
||||
#: apps/tournament/models.py:70 templates/tournament/tournament_detail.html:33
|
||||
msgid "date of maximal syntheses submission for the first round"
|
||||
msgstr "date d'envoi maximal des notes de synthèses du premier tour"
|
||||
|
||||
#: apps/tournament/models.py:74 templates/tournament/tournament_detail.html:36
|
||||
#: apps/tournament/models.py:75 templates/tournament/tournament_detail.html:36
|
||||
msgid "date when solutions of round 2 are available"
|
||||
msgstr "date à partir de laquelle les solutions du tour 2 sont disponibles"
|
||||
|
||||
#: apps/tournament/models.py:79 templates/tournament/tournament_detail.html:39
|
||||
#: apps/tournament/models.py:80 templates/tournament/tournament_detail.html:39
|
||||
msgid "date of maximal syntheses submission for the second round"
|
||||
msgstr "date d'envoi maximal des notes de synthèses pour le second tour"
|
||||
|
||||
#: apps/tournament/models.py:83
|
||||
#: apps/tournament/models.py:84
|
||||
msgid "final tournament"
|
||||
msgstr "finale"
|
||||
|
||||
#: apps/tournament/models.py:84
|
||||
#: apps/tournament/models.py:85
|
||||
msgid "It should be only one final tournament."
|
||||
msgstr "Il ne doit y avoir qu'une seule finale."
|
||||
|
||||
#: apps/tournament/models.py:135
|
||||
#: apps/tournament/models.py:136
|
||||
msgid "tournaments"
|
||||
msgstr "tournois"
|
||||
|
||||
#: apps/tournament/models.py:174 templates/tournament/team_detail.html:15
|
||||
#: apps/tournament/models.py:175 templates/tournament/team_detail.html:15
|
||||
msgid "trigram"
|
||||
msgstr "trigramme"
|
||||
|
||||
#: apps/tournament/models.py:175
|
||||
#: apps/tournament/models.py:176
|
||||
msgid ""
|
||||
"The trigram should be composed of 3 capitalize letters, that is a funny "
|
||||
"acronym for the team."
|
||||
|
@ -524,89 +530,97 @@ msgstr ""
|
|||
"Le trigramme doit être composé de trois lettres en majuscule, qui doit être "
|
||||
"un acronyme amusant représentant l'équipe."
|
||||
|
||||
#: apps/tournament/models.py:183
|
||||
#: apps/tournament/models.py:184
|
||||
msgid "The tournament where the team is registered."
|
||||
msgstr "Le tournoi où l'équipe est inscrite."
|
||||
|
||||
#: apps/tournament/models.py:188
|
||||
#: apps/tournament/models.py:189
|
||||
msgid "inscription date"
|
||||
msgstr "date d'inscription"
|
||||
|
||||
#: apps/tournament/models.py:194 apps/tournament/models.py:405
|
||||
#: apps/tournament/models.py:195 apps/tournament/models.py:420
|
||||
msgid "Registration not validated"
|
||||
msgstr "Inscription non validée"
|
||||
|
||||
#: apps/tournament/models.py:195 apps/tournament/models.py:406
|
||||
#: apps/tournament/models.py:196 apps/tournament/models.py:421
|
||||
msgid "Waiting for validation"
|
||||
msgstr "En attente de validation"
|
||||
|
||||
#: apps/tournament/models.py:196 apps/tournament/models.py:407
|
||||
#: apps/tournament/models.py:197 apps/tournament/models.py:422
|
||||
msgid "Registration validated"
|
||||
msgstr "Inscription validée"
|
||||
|
||||
#: apps/tournament/models.py:198 apps/tournament/models.py:409
|
||||
#: apps/tournament/models.py:199 apps/tournament/models.py:424
|
||||
#: templates/tournament/team_detail.html:32
|
||||
msgid "validation status"
|
||||
msgstr "statut de validation"
|
||||
|
||||
#: apps/tournament/models.py:203
|
||||
#: apps/tournament/models.py:204
|
||||
msgid "selected for final"
|
||||
msgstr "sélectionnée pour la finale"
|
||||
|
||||
#: apps/tournament/models.py:209 templates/tournament/team_detail.html:18
|
||||
#: apps/tournament/models.py:210 templates/tournament/team_detail.html:18
|
||||
msgid "access code"
|
||||
msgstr "code d'accès"
|
||||
|
||||
#: apps/tournament/models.py:286 apps/tournament/models.py:318
|
||||
#: apps/tournament/models.py:287 apps/tournament/models.py:319
|
||||
#: templates/tournament/pool_detail.html:15
|
||||
msgid "teams"
|
||||
msgstr "équipes"
|
||||
|
||||
#: apps/tournament/models.py:338 templates/tournament/pool_detail.html:12
|
||||
#: apps/tournament/models.py:339 templates/tournament/pool_detail.html:12
|
||||
msgid "juries"
|
||||
msgstr "jurys"
|
||||
|
||||
#: apps/tournament/models.py:365
|
||||
#: apps/tournament/models.py:345
|
||||
msgid "extra access token"
|
||||
msgstr "code d'accès spécial"
|
||||
|
||||
#: apps/tournament/models.py:346
|
||||
msgid "Let other users access to the pool data without logging in."
|
||||
msgstr "Permet à d'autres utilisateurs d'accéder au contenu de la poule sans connexion."
|
||||
|
||||
#: apps/tournament/models.py:380
|
||||
msgid "pool"
|
||||
msgstr "poule"
|
||||
|
||||
#: apps/tournament/models.py:366
|
||||
#: apps/tournament/models.py:381
|
||||
msgid "pools"
|
||||
msgstr "poules"
|
||||
|
||||
#: apps/tournament/models.py:391
|
||||
#: apps/tournament/models.py:406
|
||||
msgid "Not paid"
|
||||
msgstr "Non payé"
|
||||
|
||||
#: apps/tournament/models.py:392
|
||||
#: apps/tournament/models.py:407
|
||||
msgid "Credit card"
|
||||
msgstr "Carte bancaire"
|
||||
|
||||
#: apps/tournament/models.py:393
|
||||
#: apps/tournament/models.py:408
|
||||
msgid "Bank check"
|
||||
msgstr "Chèque bancaire"
|
||||
|
||||
#: apps/tournament/models.py:394
|
||||
#: apps/tournament/models.py:409
|
||||
msgid "Bank transfer"
|
||||
msgstr "Virement bancaire"
|
||||
|
||||
#: apps/tournament/models.py:395
|
||||
#: apps/tournament/models.py:410
|
||||
msgid "Cash"
|
||||
msgstr "Espèces"
|
||||
|
||||
#: apps/tournament/models.py:399
|
||||
#: apps/tournament/models.py:414
|
||||
msgid "payment method"
|
||||
msgstr "moyen de paiement"
|
||||
|
||||
#: apps/tournament/models.py:413
|
||||
#: apps/tournament/models.py:428
|
||||
msgid "payment"
|
||||
msgstr "paiement"
|
||||
|
||||
#: apps/tournament/models.py:414
|
||||
#: apps/tournament/models.py:429
|
||||
msgid "payments"
|
||||
msgstr "paiements"
|
||||
|
||||
#: apps/tournament/models.py:417
|
||||
#: apps/tournament/models.py:432
|
||||
#, python-brace-format
|
||||
msgid "Payment of {user}"
|
||||
msgstr "Paiement de {user}"
|
||||
|
@ -633,71 +647,71 @@ msgstr "Télécharger"
|
|||
msgid "Problems"
|
||||
msgstr "Problèmes"
|
||||
|
||||
#: apps/tournament/views.py:66
|
||||
#: apps/tournament/views.py:68
|
||||
msgid "Tournaments list"
|
||||
msgstr "Liste des tournois"
|
||||
|
||||
#: apps/tournament/views.py:89
|
||||
#: apps/tournament/views.py:91
|
||||
msgid "Add tournament"
|
||||
msgstr "Ajouter un tournoi"
|
||||
|
||||
#: apps/tournament/views.py:106
|
||||
#: apps/tournament/views.py:108
|
||||
#, python-brace-format
|
||||
msgid "Tournament of {name}"
|
||||
msgstr "Tournoi de {name}"
|
||||
|
||||
#: apps/tournament/views.py:140
|
||||
#: apps/tournament/views.py:146
|
||||
msgid "Update tournament"
|
||||
msgstr "Modifier le tournoi"
|
||||
|
||||
#: apps/tournament/views.py:187 apps/tournament/views.py:315
|
||||
#: apps/tournament/views.py:195 apps/tournament/views.py:323
|
||||
#, python-brace-format
|
||||
msgid "Solutions for team {team}.zip"
|
||||
msgstr "Solutions pour l'équipe {team}.zip"
|
||||
|
||||
#: apps/tournament/views.py:243
|
||||
#: apps/tournament/views.py:251
|
||||
msgid "Information about team"
|
||||
msgstr "Informations sur l'équipe"
|
||||
|
||||
#: apps/tournament/views.py:258
|
||||
#: apps/tournament/views.py:266
|
||||
msgid "Update team"
|
||||
msgstr "Modifier l'équipe"
|
||||
|
||||
#: apps/tournament/views.py:276
|
||||
#: apps/tournament/views.py:284
|
||||
msgid "Add organizer"
|
||||
msgstr "Ajouter un organisateur"
|
||||
|
||||
#: apps/tournament/views.py:299 templates/base.html:108 templates/base.html:126
|
||||
#: templates/tournament/pool_detail.html:31
|
||||
#: apps/tournament/views.py:307 templates/base.html:108 templates/base.html:118
|
||||
#: templates/base.html:132 templates/tournament/pool_detail.html:31
|
||||
msgid "Solutions"
|
||||
msgstr "Solutions"
|
||||
|
||||
#: apps/tournament/views.py:339
|
||||
#: apps/tournament/views.py:347
|
||||
msgid ""
|
||||
"You can't publish your solution anymore. Deadline: {date:%m-%d-%Y %H:%M}."
|
||||
msgstr ""
|
||||
"Vous ne pouvez plus publier vos solutions. Deadline : {date:%d/%m/%Y %H:%M}."
|
||||
|
||||
#: apps/tournament/views.py:368
|
||||
#: apps/tournament/views.py:376
|
||||
msgid "All solutions"
|
||||
msgstr "Toutes les solutions"
|
||||
|
||||
#: apps/tournament/views.py:387
|
||||
#: apps/tournament/views.py:395
|
||||
#, python-brace-format
|
||||
msgid "Solutions for tournament {tournament}.zip"
|
||||
msgstr "Solutions pour le tournoi {tournament}.zip"
|
||||
|
||||
#: apps/tournament/views.py:417 templates/base.html:111 templates/base.html:129
|
||||
#: templates/tournament/pool_detail.html:57
|
||||
#: apps/tournament/views.py:432 templates/base.html:111 templates/base.html:121
|
||||
#: templates/base.html:135 templates/tournament/pool_detail.html:57
|
||||
msgid "Syntheses"
|
||||
msgstr "Synthèses"
|
||||
|
||||
#: apps/tournament/views.py:433
|
||||
#: apps/tournament/views.py:448
|
||||
#, python-brace-format
|
||||
msgid "Syntheses for team {team}.zip"
|
||||
msgstr "Notes de synthèse de l'équipe {team}.zip"
|
||||
|
||||
#: apps/tournament/views.py:458
|
||||
#: apps/tournament/views.py:473
|
||||
msgid ""
|
||||
"You can't publish your synthesis anymore for the first round. Deadline: "
|
||||
"{date:%m-%d-%Y %H:%M}."
|
||||
|
@ -705,7 +719,7 @@ msgstr ""
|
|||
"Vous ne pouvez plus envoyer vos notes de synthèse pour le premier tour. "
|
||||
"Deadline : {date:%d/%m/%Y %h:%M}."
|
||||
|
||||
#: apps/tournament/views.py:464
|
||||
#: apps/tournament/views.py:479
|
||||
msgid ""
|
||||
"You can't publish your synthesis anymore for the second round. Deadline: "
|
||||
"{date:%m-%d-%Y %H:%M}."
|
||||
|
@ -713,34 +727,34 @@ msgstr ""
|
|||
"Vous ne pouvez plus envoyer vos notes de synthèse pour le second tour. "
|
||||
"Deadline : {date:%d/%m/%Y %h:%M}."
|
||||
|
||||
#: apps/tournament/views.py:494
|
||||
#: apps/tournament/views.py:509
|
||||
msgid "All syntheses"
|
||||
msgstr "Toutes les notes de synthèses"
|
||||
|
||||
#: apps/tournament/views.py:513
|
||||
#: apps/tournament/views.py:528
|
||||
#, python-brace-format
|
||||
msgid "Syntheses for tournament {tournament}.zip"
|
||||
msgstr "Notes de synthèse pour le tournoi {tournament}.zip"
|
||||
|
||||
#: apps/tournament/views.py:542 templates/base.html:133
|
||||
#: apps/tournament/views.py:571 templates/base.html:125 templates/base.html:138
|
||||
msgid "Pools"
|
||||
msgstr "Poules"
|
||||
|
||||
#: apps/tournament/views.py:563
|
||||
#: apps/tournament/views.py:594
|
||||
msgid "Create pool"
|
||||
msgstr "Créer une poule"
|
||||
|
||||
#: apps/tournament/views.py:580
|
||||
#: apps/tournament/views.py:611
|
||||
msgid "Pool detail"
|
||||
msgstr "Détails d'une poule"
|
||||
|
||||
#: apps/tournament/views.py:609
|
||||
#: apps/tournament/views.py:644
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Solutions of a pool for the round {round} of the tournament {tournament}.zip"
|
||||
msgstr "Solutions d'une poule du tour {round} du tournoi {tournament}.zip"
|
||||
|
||||
#: apps/tournament/views.py:626
|
||||
#: apps/tournament/views.py:658
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Syntheses of a pool for the round {round} of the tournament {tournament}.zip"
|
||||
|
@ -826,38 +840,30 @@ msgstr "Rejoindre une équipe"
|
|||
msgid "My team"
|
||||
msgstr "Mon équipe"
|
||||
|
||||
#: templates/base.html:118
|
||||
msgid "Add a tournament"
|
||||
msgstr "Ajouter un tournoi"
|
||||
|
||||
#: templates/base.html:121
|
||||
msgid "Add an organizer"
|
||||
msgstr "Ajouter un organisateur"
|
||||
|
||||
#: templates/base.html:138
|
||||
#: templates/base.html:144
|
||||
msgid "Make a gift"
|
||||
msgstr "Faire un don"
|
||||
|
||||
#: templates/base.html:142
|
||||
#: templates/base.html:148
|
||||
msgid "Administration"
|
||||
msgstr "Administration"
|
||||
|
||||
#: templates/base.html:149
|
||||
#: templates/base.html:155
|
||||
msgid "Return to admin view"
|
||||
msgstr "Retour à l'interface administrateur"
|
||||
|
||||
#: templates/base.html:154 templates/registration/login.html:7
|
||||
#: templates/base.html:160 templates/registration/login.html:7
|
||||
#: templates/registration/login.html:8 templates/registration/login.html:22
|
||||
#: templates/registration/password_reset_complete.html:10
|
||||
msgid "Log in"
|
||||
msgstr "Connexion"
|
||||
|
||||
#: templates/base.html:157 templates/registration/signup.html:5
|
||||
#: templates/base.html:163 templates/registration/signup.html:5
|
||||
#: templates/registration/signup.html:8 templates/registration/signup.html:14
|
||||
msgid "Sign up"
|
||||
msgstr "S'inscrire"
|
||||
|
||||
#: templates/base.html:161
|
||||
#: templates/base.html:167
|
||||
msgid "Log out"
|
||||
msgstr "Déconnexion"
|
||||
|
||||
|
@ -867,7 +873,7 @@ msgid "Field filters"
|
|||
msgstr "Filtres"
|
||||
|
||||
#: templates/django_filters/rest_framework/form.html:5
|
||||
#: templates/member/my_account.html:8 templates/tournament/add_organizer.html:9
|
||||
#: templates/member/my_account.html:9 templates/tournament/add_organizer.html:9
|
||||
#: templates/tournament/pool_form.html:9
|
||||
#: templates/tournament/solutions_list.html:24
|
||||
#: templates/tournament/syntheses_list.html:40
|
||||
|
@ -876,10 +882,14 @@ msgstr "Filtres"
|
|||
msgid "Submit"
|
||||
msgstr "Envoyer"
|
||||
|
||||
#: templates/member/my_account.html:13
|
||||
#: templates/member/my_account.html:14
|
||||
msgid "Update my password"
|
||||
msgstr "Changer mon mot de passe"
|
||||
|
||||
#: templates/member/profile_list.html:9
|
||||
msgid "Add an organizer"
|
||||
msgstr "Ajouter un organisateur"
|
||||
|
||||
#: templates/member/tfjmuser_detail.html:12
|
||||
msgid "role"
|
||||
msgstr "rôle"
|
||||
|
@ -1045,7 +1055,7 @@ msgid "Solutions will be available here for teams from:"
|
|||
msgstr "Les solutions seront disponibles ici pour les équipes à partir du :"
|
||||
|
||||
#: templates/tournament/pool_detail.html:49
|
||||
#: templates/tournament/pool_detail.html:74
|
||||
#: templates/tournament/pool_detail.html:73
|
||||
msgid "Download ZIP archive"
|
||||
msgstr "Télécharger l'archive ZIP"
|
||||
|
||||
|
@ -1058,6 +1068,15 @@ msgstr "Le modèle de note de synthèse est disponible ici :"
|
|||
msgid "Pool list"
|
||||
msgstr "Liste des poules"
|
||||
|
||||
#: templates/tournament/pool_detail.html:89
|
||||
msgid ""
|
||||
"Give this link to juries to access this page (warning: should stay "
|
||||
"confidential and only given to juries of this pool):"
|
||||
msgstr ""
|
||||
"Donnez ce lien aux jurys pour leur permettre d'accéder à cette page "
|
||||
"(attention : ce lien doit rester confidentiel et ne doit être donné "
|
||||
"exclusivement qu'à des jurys) :"
|
||||
|
||||
#: templates/tournament/pool_list.html:10
|
||||
msgid "Add pool"
|
||||
msgstr "Ajouter une poule"
|
||||
|
@ -1210,10 +1229,14 @@ msgstr "Envoyer un mail à toutes les personnes dans une équipe"
|
|||
msgid "Send a mail to all people that are in a valid team"
|
||||
msgstr "Envoyer un mail à toutes les personnes dans une équipe validée"
|
||||
|
||||
#: tfjm/settings.py:146
|
||||
#: templates/tournament/tournament_list.html:15
|
||||
msgid "Add a tournament"
|
||||
msgstr "Ajouter un tournoi"
|
||||
|
||||
#: tfjm/settings.py:147
|
||||
msgid "English"
|
||||
msgstr "Anglais"
|
||||
|
||||
#: tfjm/settings.py:147
|
||||
#: tfjm/settings.py:148
|
||||
msgid "French"
|
||||
msgstr "Français"
|
||||
|
|
|
@ -125,6 +125,20 @@
|
|||
<a class="nav-link" href="{% url "tournament:pools" %}"><i class="fas fa-swimming-pool"></i> {% trans "Pools" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if not user.is_authenticated and request.session.extra_access_token %}
|
||||
{# Juries can access to pool data without logging in. #}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "tournament:all_solutions" %}"><i class="fas fa-lightbulb"></i> {% trans "Solutions" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "tournament:all_syntheses" %}"><i class="fas fa-feather"></i> {% trans "Syntheses" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "tournament:pools" %}"><i class="fas fa-swimming-pool"></i> {% trans "Pools" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="https://www.helloasso.com/associations/animath/formulaires/5/widget"><i
|
||||
class="fas fa-hand-holding-heart"></i> {% trans "Make a gift" %}</a>
|
||||
|
|
|
@ -61,13 +61,12 @@
|
|||
{% trans "Templates for syntheses are available here:" %}
|
||||
<a data-turbolinks="false" href="{% static "Fiche synthèse.pdf" %}">PDF</a> -- <a data-turbolinks="false" href="{% static "Fiche synthèse.tex" %}">TEX</a>
|
||||
</div>
|
||||
{% if user.organizes %}
|
||||
{% if user.organizes or not user.is_authenticated %}
|
||||
<ul>
|
||||
{% for synthesis in pool.syntheses.all %}
|
||||
<li><a data-turbolinks="false" href="{{ synthesis.file.url }}">{{ synthesis }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-footer text-center">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
@ -76,10 +75,20 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="text-center">
|
||||
<a class="btn btn-block btn-primary" href="{% url "tournament:pools" %}">{% trans "Pool list" %}</a>
|
||||
</div>
|
||||
|
||||
{% if user.organizes or not user.is_authenticated %}
|
||||
<hr>
|
||||
<div class="alert alert-warning">
|
||||
{% trans "Give this link to juries to access this page (warning: should stay confidential and only given to juries of this pool):" %}<br>
|
||||
<a href="{% url "tournament:pool_detail" pk=pool.pk %}?extra_access_token={{ pool.extra_access_token }}">
|
||||
https://{{ request.get_host }}{% url "tournament:pool_detail" pk=pool.pk %}?extra_access_token={{ pool.extra_access_token }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -64,6 +64,22 @@ class SessionMiddleware(object):
|
|||
return response
|
||||
|
||||
|
||||
class ExtraAccessMiddleware(object):
|
||||
"""
|
||||
This middleware allows some non authenticated people to access to pool data.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
if "extra_access_token" in request.GET:
|
||||
request.session["extra_access_token"] = request.GET["extra_access_token"]
|
||||
else:
|
||||
request.session.setdefault("extra_access_token", "")
|
||||
return self.get_response(request)
|
||||
|
||||
|
||||
class TurbolinksMiddleware(object):
|
||||
"""
|
||||
Send the `Turbolinks-Location` header in response to a visit that was redirected,
|
||||
|
|
|
@ -71,6 +71,7 @@ MIDDLEWARE = [
|
|||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.contrib.sites.middleware.CurrentSiteMiddleware',
|
||||
'tfjm.middlewares.SessionMiddleware',
|
||||
'tfjm.middlewares.ExtraAccessMiddleware',
|
||||
'tfjm.middlewares.TurbolinksMiddleware',
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue