1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-07-02 05:18:35 +02:00

Compare commits

...

6 Commits

Author SHA1 Message Date
ae62e3daf7 Reorganize the cancel step code in order to make it more readable
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2023-04-06 18:15:14 +02:00
8778f58fe4 The draw is now fully reversible
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2023-04-06 00:19:24 +02:00
751e35ac62 Cancel draw problem
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2023-04-05 23:28:12 +02:00
f41b2e16ab Cancel choose problem
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2023-04-05 19:40:47 +02:00
1f6ce072bf Add cancel button to cancel the last step (works for the last problem acceptance for now)
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2023-04-05 19:22:48 +02:00
746aae464a Add confirmation modal before aborting a draw
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2023-04-05 18:41:28 +02:00
4 changed files with 679 additions and 202 deletions

View File

@ -2,13 +2,16 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from collections import OrderedDict
import json
from random import randint, shuffle
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.utils import translation
from django.utils.translation import gettext_lazy as _
from draw.models import Draw, Pool, Round, TeamDraw
from logs.models import Changelog
from participation.models import Participation, Tournament
from registration.models import Registration
@ -115,6 +118,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
case 'abort':
# Abort the current draw
await self.abort(**content)
case 'cancel':
# Cancel the last step
await self.cancel_last_step(**content)
case 'dice':
# Launch a dice
await self.process_dice(**content)
@ -345,11 +351,14 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
if values.count(v) > 1:
# v is a duplicate value
# Get all teams that have the same result
dups = [td for td in tds if td.passage_dice == v]
dups = [td for td in tds if (td.passage_dice if state == 'DICE_SELECT_POULES' else td.choice_dice) == v]
for dup in dups:
# Reset the dice
dup.passage_dice = None
if state == 'DICE_SELECT_POULES':
dup.passage_dice = None
else:
dup.choice_dice = None
await dup.asave()
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
@ -647,7 +656,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
:param pool: The pool to end.
"""
msg = self.tournament.draw.last_message
r = pool.round
r = self.tournament.draw.current_round
if pool.size == 5:
# Maybe reorder teams if the same problem is presented twice
@ -925,6 +934,402 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
@ensure_orga
async def cancel_last_step(self, **kwargs):
"""
Cancel the last step of the draw.
"""
if not await Draw.objects.filter(tournament=self.tournament).aexists():
return await self.alert(_("The draw has not started yet."), 'danger')
state = self.tournament.draw.get_state()
self.tournament.draw.last_message = ""
await self.tournament.draw.asave()
if state == 'DRAW_ENDED' or state == 'WAITING_FINAL':
await self.undo_end_draw()
elif state == 'WAITING_CHOOSE_PROBLEM':
await self.undo_draw_problem()
elif state == 'WAITING_DRAW_PROBLEM':
await self.undo_process_problem()
elif state == 'DICE_ORDER_POULE':
await self.undo_pool_dice()
elif state == 'DICE_SELECT_POULES':
await self.undo_order_dice()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
async def undo_end_draw(self) -> None:
"""
If the draw is ended, or if we are between the two rounds of the final,
then we cancel the last problem that was accepted.
"""
r = self.tournament.draw.current_round
td = r.current_pool.current_team
td.purposed = td.accepted
td.accepted = None
await td.asave()
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.continue_visibility', 'visible': False})
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_problem',
'round': r.number,
'team': td.participation.team.trigram,
'problem': td.accepted})
async def undo_draw_problem(self):
"""
A problem was drawn and we wait for the current team to accept or reject the problem.
Then, we just reset the problem draw.
:return:
"""
td = self.tournament.draw.current_round.current_pool.current_team
td.purposed = None
await td.asave()
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.buttons_visibility', 'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': False})
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.box_visibility', 'visible': True})
async def undo_process_problem(self):
"""
Now, a team must draw a new problem. Multiple cases are possible:
* In the same pool, a previous team accepted a problem ;
* In the same pool, a previous team rejected a problem ;
* The current team rejected a problem that was previously rejected ;
* The last team drawn its dice to choose the draw order.
In the two first cases, we explore the database history to fetch what team accepted or rejected
its problem at last.
The third case is ignored, because too hard and too useless to manage.
For the last case, we cancel the last dice.
"""
content_type = await ContentType.objects.aget(app_label=TeamDraw._meta.app_label,
model=TeamDraw._meta.model_name)
r = self.tournament.draw.current_round
p = r.current_pool
accepted_tds = {td.id: td async for td in p.team_draws.filter(accepted__isnull=False)
.prefetch_related('participation__team')}
has_rejected_one_tds = {td.id: td async for td in p.team_draws.exclude(rejected=[])
.prefetch_related('participation__team')}
last_td = None
if accepted_tds or has_rejected_one_tds:
# One team of the already accepted or its problem, we fetch the last one
changelogs = Changelog.objects.filter(
model=content_type,
action='edit',
instance_pk__in=set(accepted_tds.keys()).union(set(has_rejected_one_tds.keys()))
).order_by('-timestamp')
async for changelog in changelogs:
previous = json.loads(changelog.previous)
data = json.loads(changelog.data)
pk = int(changelog.instance_pk)
if 'accepted' in data and data['accepted'] and pk in accepted_tds:
# Undo the last acceptance
last_td = accepted_tds[pk]
last_td.purposed = last_td.accepted
last_td.accepted = None
await last_td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_problem',
'round': r.number,
'team': last_td.participation.team.trigram,
'problem': last_td.accepted})
break
if 'rejected' in data and len(data['rejected']) > len(previous['rejected']) \
and pk in has_rejected_one_tds:
# Undo the last reject
last_td = has_rejected_one_tds[pk]
rejected_problem = set(data['rejected']).difference(previous['rejected']).pop()
if rejected_problem not in last_td.rejected:
# This is an old diff
continue
last_td.rejected.remove(rejected_problem)
last_td.purposed = rejected_problem
await last_td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.reject_problem',
'round': r.number,
'team': last_td.participation.team.trigram,
'rejected': last_td.rejected})
break
r.current_pool.current_team = last_td
await r.current_pool.asave()
await self.channel_layer.group_send(f"team-{last_td.participation.team.trigram}",
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': True})
else:
# Return to the dice choice
pool_tds = {td.id: td async for td in p.team_draws.prefetch_related('participation__team')}
changelogs = Changelog.objects.filter(
model=content_type,
action='edit',
instance_pk__in=set(pool_tds.keys())
).order_by('-timestamp')
# Find the last dice that was launched
async for changelog in changelogs:
data = json.loads(changelog.data)
if 'choice_dice' in data and data['choice_dice']:
last_td = pool_tds[int(changelog.instance_pk)]
# Reset the dice
last_td.choice_dice = None
await last_td.asave()
# Reset the dice on the interface
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': last_td.participation.team.trigram,
'result': None})
break
p.current_team = None
await p.asave()
# Make dice box visible
for td in pool_tds.values():
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.box_visibility', 'visible': False})
async def undo_pool_dice(self):
"""
Teams of a pool are launching their dices to define the draw order.
We reset the last dice if possible, or we go to the last pool, or the last round,
or the passage dices.
"""
content_type = await ContentType.objects.aget(app_label=TeamDraw._meta.app_label,
model=TeamDraw._meta.model_name)
r = self.tournament.draw.current_round
p = r.current_pool
already_launched_tds = {td.id: td async for td in p.team_draws.filter(choice_dice__isnull=False)
.prefetch_related('participation__team')}
if already_launched_tds:
# Reset the last dice
changelogs = Changelog.objects.filter(
model=content_type,
action='edit',
instance_pk__in=set(already_launched_tds.keys())
).order_by('-timestamp')
# Find the last dice that was launched
async for changelog in changelogs:
data = json.loads(changelog.data)
if 'choice_dice' in data and data['choice_dice']:
last_td = already_launched_tds[int(changelog.instance_pk)]
# Reset the dice
last_td.choice_dice = None
await last_td.asave()
# Reset the dice on the interface
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': last_td.participation.team.trigram,
'result': None})
break
else:
# Go to the previous pool if possible
if p.letter > 1:
# Go to the previous pool
previous_pool = await r.pool_set.prefetch_related('current_team__participation__team') \
.aget(letter=p.letter - 1)
r.current_pool = previous_pool
await r.asave()
td = previous_pool.current_team
td.purposed = td.accepted
td.accepted = None
await td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': False})
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_problem',
'round': r.number,
'team': td.participation.team.trigram,
'problem': td.accepted})
elif r.number == 2:
if not self.tournament.final:
# Go to the previous round
r1 = await self.tournament.draw.round_set \
.prefetch_related('current_pool__current_team__participation__team').aget(number=1)
self.tournament.draw.current_round = r1
await self.tournament.draw.asave()
async for td in r1.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': td.participation.team.trigram,
'result': td.choice_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', 'round': r1})
previous_pool = r1.current_pool
td = previous_pool.current_team
td.purposed = td.accepted
td.accepted = None
await td.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': False})
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.buttons_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_problem',
'round': r1.number,
'team': td.participation.team.trigram,
'problem': td.accepted})
else:
# Don't continue the final tournament
r1 = await self.tournament.draw.round_set \
.prefetch_related('current_pool__current__team__participation__team').aget(number=1)
self.tournament.draw.current_round = r1
await self.tournament.draw.asave()
async for td in r.teamdraw_set.all():
td.pool = None
td.choose_index = None
td.choice_dice = None
await td.asave()
async for td in r1.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': td.participation.team.trigram,
'result': td.choice_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': False})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.continue_visibility', 'visible': True})
else:
# Go to the dice order
async for r0 in self.tournament.draw.round_set.all():
async for td in r0.teamdraw_set.all():
td.pool = None
td.passage_index = None
td.choose_index = None
td.choice_dice = None
await td.asave()
r.current_pool = None
await r.asave()
round_tds = {td.id: td async for td in r.team_draws.prefetch_related('participation__team')}
# Reset the last dice
changelogs = Changelog.objects.filter(
model=content_type,
action='edit',
instance_pk__in=set(round_tds.keys())
).order_by('-timestamp')
# Find the last dice that was launched
async for changelog in changelogs:
data = json.loads(changelog.data)
if 'passage_dice' in data and data['passage_dice']:
last_td = round_tds[int(changelog.instance_pk)]
# Reset the dice
last_td.passage_dice = None
await last_td.asave()
# Reset the dice on the interface
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': last_td.participation.team.trigram,
'result': None})
break
async for td in r.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': td.participation.team.trigram,
'result': td.passage_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True})
async def undo_order_dice(self):
"""
Undo the last dice for the passage order, or abort the draw if we are at the beginning.
"""
content_type = await ContentType.objects.aget(app_label=TeamDraw._meta.app_label,
model=TeamDraw._meta.model_name)
r = self.tournament.draw.current_round
already_launched_tds = {td.id: td async for td in r.team_draws.filter(passage_dice__isnull=False)
.prefetch_related('participation__team')}
if already_launched_tds:
# Reset the last dice
changelogs = Changelog.objects.filter(
model=content_type,
action='edit',
instance_pk__in=set(already_launched_tds.keys())
).order_by('-timestamp')
# Find the last dice that was launched
async for changelog in changelogs:
data = json.loads(changelog.data)
if 'passage_dice' in data and data['passage_dice']:
last_td = already_launched_tds[int(changelog.instance_pk)]
# Reset the dice
last_td.passage_dice = None
await last_td.asave()
# Reset the dice on the interface
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}", {'type': 'draw.dice',
'team': last_td.participation.team.trigram,
'result': None})
break
else:
await self.abort()
async def draw_alert(self, content):
"""
Send alert to the current user.

View File

@ -20,6 +20,15 @@ function abortDraw(tid) {
sockets[tid].send(JSON.stringify({'type': 'abort'}))
}
/**
* Request to cancel the last step.
* Only volunteers are allowed to do this.
* @param tid The tournament id
*/
function cancelLastStep(tid) {
sockets[tid].send(JSON.stringify({'type': 'cancel'}))
}
/**
* Request to launch a dice between 1 and 100, for the two first steps.
* The parameter `trigram` can be specified (by volunteers) to launch a dice for a specific team.
@ -583,13 +592,19 @@ document.addEventListener('DOMContentLoaded', () => {
function setProblemAccepted(round, team, problem) {
// Update recap
let recapDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-accepted`)
recapDiv.classList.remove('text-bg-warning')
recapDiv.classList.add('text-bg-success')
recapDiv.textContent = `${team} 📃 ${problem}`
if (problem !== null) {
recapDiv.classList.remove('text-bg-warning')
recapDiv.classList.add('text-bg-success')
}
else {
recapDiv.classList.add('text-bg-warning')
recapDiv.classList.remove('text-bg-success')
}
recapDiv.textContent = `${team} 📃 ${problem ? problem : '?'}`
// Update table
let tableSpan = document.getElementById(`table-${tournament.id}-round-${round}-problem-${team}`)
tableSpan.textContent = problem
tableSpan.textContent = problem ? problem : '?'
}
/**
@ -603,9 +618,9 @@ document.addEventListener('DOMContentLoaded', () => {
let recapDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-rejected`)
recapDiv.textContent = `🗑️ ${rejected.join(', ')}`
let penaltyDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-penalty`)
if (rejected.length > problems_count - 5) {
// If more than P - 5 problems were rejected, add a penalty of 0.5 of the coefficient of the oral defender
let penaltyDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-penalty`)
if (penaltyDiv === null) {
penaltyDiv = document.createElement('div')
penaltyDiv.id = `recap-${tournament.id}-round-${round}-team-${team}-penalty`
@ -614,6 +629,11 @@ document.addEventListener('DOMContentLoaded', () => {
}
penaltyDiv.textContent = `${0.5 * (rejected.length - (problems_count - 5))}`
}
else {
// Eventually remove this div
if (penaltyDiv !== null)
penaltyDiv.remove()
}
}
/**

View File

@ -55,9 +55,10 @@
<div class="card-header">
Recap
{% if user.registration.is_volunteer %}
{# Volunteers can click on this button to abort the draw #}
<button id="abort-{{ tournament.id }}" class="badge rounded-pill text-bg-danger" onclick="abortDraw({{ tournament.id }})">
{% trans "Abort" %}
<button id="cancel-last-step-{{ tournament.id }}"
class="badge rounded-pill text-bg-warning"
onclick="cancelLastStep({{ tournament.id }})">
🔙 {% trans "Cancel last step" %}
</button>
{% endif %}
</div>
@ -326,4 +327,33 @@
{% endfor %}
</div>
</div>
{% if user.registration.is_volunteer %}
{# Volunteers can click on this button to abort the draw #}
<div class="text-center mt-3">
<button id="abort-{{ tournament.id }}" class="badge rounded-pill text-bg-danger" data-bs-toggle="modal" data-bs-target="#abort{{ tournament.id }}Modal">
{% trans "Abort" %}
</button>
</div>
{% endif %}
</div>
<div id="abort{{ tournament.id }}Modal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{% trans "Are you sure?" %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans "This will reset the draw from the beginning." %}
{% trans "This operation is irreversible." %}
{% trans "Are you sure you want to abort this draw?" %}
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-danger" onclick="abortDraw({{ tournament.id }})">{% trans "Abort" %}</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
</div>
</div>
</div>
</div>

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-05 16:46+0200\n"
"POT-Creation-Date: 2023-04-05 18:54+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -26,15 +26,15 @@ msgstr "API"
msgid "teams"
msgstr "équipes"
#: draw/admin.py:40 draw/admin.py:56 draw/models.py:25
#: draw/admin.py:40 draw/admin.py:56 draw/models.py:24
#: participation/admin.py:16 participation/admin.py:73
#: participation/admin.py:104 participation/models.py:296
#: participation/models.py:320 participation/models.py:352
#: participation/admin.py:104 participation/models.py:295
#: participation/models.py:319 participation/models.py:351
msgid "tournament"
msgstr "tournoi"
#: draw/admin.py:60 draw/models.py:229 draw/models.py:415
#: participation/models.py:356
#: draw/admin.py:60 draw/models.py:228 draw/models.py:414
#: participation/models.py:355
msgid "round"
msgstr "tour"
@ -42,237 +42,237 @@ msgstr "tour"
msgid "Draw"
msgstr "Tirage au sort"
#: draw/consumers.py:26
#: draw/consumers.py:25
msgid "You are not an organizer."
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice."
#: draw/consumers.py:146
#: draw/consumers.py:148
msgid "The draw is already started."
msgstr "Le tirage a déjà commencé."
#: draw/consumers.py:152
#: draw/consumers.py:154
msgid "Invalid format"
msgstr "Format invalide"
#: draw/consumers.py:157
#: draw/consumers.py:159
#, python-brace-format
msgid "The sum must be equal to the number of teams: expected {len}, got {sum}"
msgstr ""
"La somme doit être égale au nombre d'équipes : attendu {len}, obtenu {sum}"
#: draw/consumers.py:162
#: draw/consumers.py:164
msgid "There can be at most one pool with 5 teams."
msgstr "Il ne peut y avoir au plus qu'une seule poule de 5 équipes."
#: draw/consumers.py:190
#: draw/consumers.py:192
msgid "Draw started!"
msgstr "Le tirage a commencé !"
#: draw/consumers.py:210
#: draw/consumers.py:212
#, python-brace-format
msgid "The draw for the tournament {tournament} will start."
msgstr "Le tirage au sort du tournoi {tournament} va commencer."
#: draw/consumers.py:221 draw/consumers.py:247 draw/consumers.py:578
#: draw/consumers.py:749 draw/consumers.py:832 draw/consumers.py:849
#: draw/templates/draw/tournament_content.html:5
#: draw/consumers.py:223 draw/consumers.py:248 draw/consumers.py:579
#: draw/consumers.py:768 draw/consumers.py:850 draw/consumers.py:867
#: draw/consumers.py:937 draw/templates/draw/tournament_content.html:5
msgid "The draw has not started yet."
msgstr "Le tirage au sort n'a pas encore commencé."
#: draw/consumers.py:233
#: draw/consumers.py:235
#, python-brace-format
msgid "The draw for the tournament {tournament} is aborted."
msgstr "Le tirage au sort du tournoi {tournament} est annulé."
#: draw/consumers.py:274 draw/consumers.py:295 draw/consumers.py:524
#: draw/consumers.py:583 draw/consumers.py:754
#: draw/consumers.py:275 draw/consumers.py:296 draw/consumers.py:525
#: draw/consumers.py:584 draw/consumers.py:773
msgid "This is not the time for this."
msgstr "Ce n'est pas le moment pour cela."
#: draw/consumers.py:287 draw/consumers.py:290
#: draw/consumers.py:288 draw/consumers.py:291
msgid "You've already launched the dice."
msgstr "Vous avez déjà lancé le dé."
#: draw/consumers.py:293
#: draw/consumers.py:294
msgid "It is not your turn."
msgstr "Ce n'est pas votre tour."
#: draw/consumers.py:371
#: draw/consumers.py:372
#, python-brace-format
msgid "Dices from teams {teams} are identical. Please relaunch your dices."
msgstr ""
"Les dés des équipes {teams} sont identiques. Merci de relancer vos dés."
#: draw/consumers.py:852
#: draw/consumers.py:870
msgid "This is only available for the final tournament."
msgstr "Cela n'est possible que pour la finale."
#: draw/models.py:26
#: draw/models.py:25
msgid "The associated tournament."
msgstr "Le tournoi associé."
#: draw/models.py:35
#: draw/models.py:34
msgid "current round"
msgstr "tour actuel"
#: draw/models.py:36
#: draw/models.py:35
msgid "The current round where teams select their problems."
msgstr "Le tour en cours où les équipes choisissent leurs problèmes."
#: draw/models.py:42
#: draw/models.py:41
msgid "last message"
msgstr "dernier message"
#: draw/models.py:43
#: draw/models.py:42
msgid "The last message that is displayed on the drawing interface."
msgstr "Le dernier message qui est affiché sur l'interface de tirage."
#: draw/models.py:172
#: draw/models.py:171
#, python-brace-format
msgid "Draw of tournament {tournament}"
msgstr "Tirage au sort du tournoi {tournament}"
#: draw/models.py:175 draw/models.py:187
#: draw/models.py:174 draw/models.py:186
msgid "draw"
msgstr "tirage au sort"
#: draw/models.py:176
#: draw/models.py:175
msgid "draws"
msgstr "tirages au sort"
#: draw/models.py:192
#: draw/models.py:191
msgid "Round 1"
msgstr "Tour 1"
#: draw/models.py:193
#: draw/models.py:192
msgid "Round 2"
msgstr "Tour 2"
#: draw/models.py:195
#: draw/models.py:194
msgid "number"
msgstr "numéro"
#: draw/models.py:196
#: draw/models.py:195
msgid "The number of the round, 1 or 2"
msgstr "Le numéro du tour, 1 ou 2"
#: draw/models.py:206
#: draw/models.py:205
msgid "current pool"
msgstr "poule actuelle"
#: draw/models.py:207
#: draw/models.py:206
msgid "The current pool where teams select their problems."
msgstr "La poule en cours, où les équipes choisissent leurs problèmes"
#: draw/models.py:230
#: draw/models.py:229
msgid "rounds"
msgstr "tours"
#: draw/models.py:252 participation/models.py:370
#: draw/models.py:251 participation/models.py:369
msgid "letter"
msgstr "lettre"
#: draw/models.py:253
#: draw/models.py:252
msgid "The letter of the pool: A, B, C or D."
msgstr "La lettre de la poule : A, B, C ou D."
#: draw/models.py:257
#: draw/models.py:256
#: participation/templates/participation/tournament_detail.html:15
msgid "size"
msgstr "taille"
#: draw/models.py:259
#: draw/models.py:258
msgid "The number of teams in this pool, between 3 and 5."
msgstr "Le nombre d'équipes dans la poule, entre 3 et 5."
#: draw/models.py:268
#: draw/models.py:267
msgid "current team"
msgstr "équipe actuelle"
#: draw/models.py:269
#: draw/models.py:268
msgid "The current team that is selecting its problem."
msgstr "L'équipe qui est en train de choisir son problème."
#: draw/models.py:278
#: draw/models.py:277
msgid "associated pool"
msgstr "poule associée"
#: draw/models.py:279
#: draw/models.py:278
msgid "The full pool instance."
msgstr "L'instance complète de la poule."
#: draw/models.py:393
#: draw/models.py:392
#, python-brace-format
msgid "Pool {letter}{number}"
msgstr "Poule {letter}{number}"
#: draw/models.py:396 draw/models.py:423 participation/admin.py:69
#: participation/admin.py:88 participation/models.py:422
#: participation/models.py:431 participation/tables.py:82
#: draw/models.py:395 draw/models.py:422 participation/admin.py:69
#: participation/admin.py:88 participation/models.py:421
#: participation/models.py:430 participation/tables.py:82
msgid "pool"
msgstr "poule"
#: draw/models.py:397 participation/models.py:423
#: draw/models.py:396 participation/models.py:422
msgid "pools"
msgstr "poules"
#: draw/models.py:409 participation/models.py:343 participation/models.py:555
#: participation/models.py:585 participation/models.py:623
#: draw/models.py:408 participation/models.py:342 participation/models.py:554
#: participation/models.py:584 participation/models.py:622
msgid "participation"
msgstr "participation"
#: draw/models.py:430
#: draw/models.py:429
msgid "passage index"
msgstr "numéro de passage"
#: draw/models.py:431
#: draw/models.py:430
msgid ""
"The passage order in the pool, between 0 and the size of the pool minus 1."
msgstr ""
"L'ordre de passage dans la poule, de 0 à la taille de la poule moins 1."
#: draw/models.py:439
#: draw/models.py:438
msgid "choose index"
msgstr "numéro de choix"
#: draw/models.py:440
#: draw/models.py:439
msgid ""
"The choice order in the pool, between 0 and the size of the pool minus 1."
msgstr ""
"L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1."
#: draw/models.py:446 draw/models.py:469 participation/models.py:438
#: participation/models.py:592
#: draw/models.py:445 draw/models.py:468 participation/models.py:437
#: participation/models.py:591
#, python-brace-format
msgid "Problem #{problem}"
msgstr "Problème n°{problem}"
#: draw/models.py:450 draw/models.py:473
#: draw/models.py:449 draw/models.py:472
msgid "accepted problem"
msgstr "problème accepté"
#: draw/models.py:457
#: draw/models.py:456
msgid "passage dice"
msgstr "dé d'ordre de passage"
#: draw/models.py:464
#: draw/models.py:463
msgid "choice dice"
msgstr "dé d'ordre de choix"
#: draw/models.py:478
#: draw/models.py:477
msgid "rejected problems"
msgstr "problèmes rejetés"
#: draw/models.py:504
#: draw/models.py:503
#, python-brace-format
msgid "Draw of the team {trigram} for the pool {letter}{number}"
msgstr "Tirage de l'équipe {trigram} pour la poule {letter}{number}"
#: draw/models.py:510
#: draw/models.py:509
msgid "team draw"
msgstr "tirage d'équipe"
#: draw/models.py:511
#: draw/models.py:510
msgid "team draws"
msgstr "tirages d'équipe"
@ -288,36 +288,36 @@ msgstr "Démarrer !"
msgid "Last dices"
msgstr "Derniers jets de dés"
#: draw/templates/draw/tournament_content.html:60
msgid "Abort"
msgstr "Annuler"
#: draw/templates/draw/tournament_content.html:135
#: draw/templates/draw/tournament_content.html:129
msgid "Launch dice"
msgstr "Lancer le dé"
#: draw/templates/draw/tournament_content.html:149
#: draw/templates/draw/tournament_content.html:143
msgid "Draw a problem"
msgstr "Tirer un problème"
#: draw/templates/draw/tournament_content.html:160
#: draw/templates/draw/tournament_content.html:154
msgid "Accept"
msgstr "Accepter"
#: draw/templates/draw/tournament_content.html:163
#: draw/templates/draw/tournament_content.html:157
msgid "Decline"
msgstr "Refuser"
#: draw/templates/draw/tournament_content.html:174
#: draw/templates/draw/tournament_content.html:168
msgid "Export"
msgstr "Exporter"
#: draw/templates/draw/tournament_content.html:182
#: draw/templates/draw/tournament_content.html:176
msgid "Continue draw"
msgstr "Continuer le tirage"
#: draw/templates/draw/tournament_content.html:183
msgid "Cancel last step"
msgstr "Annuler la dernière étape"
#: draw/templates/draw/tournament_content.html:215 participation/admin.py:100
#: participation/models.py:125 participation/models.py:311
#: participation/models.py:125 participation/models.py:310
#: registration/models.py:127
msgid "team"
msgstr "équipe"
@ -330,6 +330,32 @@ msgstr "équipe"
msgid "Room"
msgstr "Salle"
#: draw/templates/draw/tournament_content.html:335
#: draw/templates/draw/tournament_content.html:353
msgid "Abort"
msgstr "Annuler"
#: draw/templates/draw/tournament_content.html:344
msgid "Are you sure?"
msgstr "Êtes-vous sûr⋅e ?"
#: draw/templates/draw/tournament_content.html:348
msgid "This will reset the draw from the beginning."
msgstr "Cela va réinitialiser le tirage au sort depuis le début."
#: draw/templates/draw/tournament_content.html:349
msgid "This operation is irreversible."
msgstr "Cette opération est irréversible."
#: draw/templates/draw/tournament_content.html:350
msgid "Are you sure you want to abort this draw?"
msgstr "Êtes-vous sûr·e de vouloir annuler le tirage au sort ?"
#: draw/templates/draw/tournament_content.html:354
#: tfjm/templates/base_modal.html:17
msgid "Close"
msgstr "Fermer"
#: logs/apps.py:11
msgid "Logs"
msgstr "Logs"
@ -399,26 +425,26 @@ msgstr "Changelog de type \"{action}\" pour le modèle {model} le {timestamp}"
msgid "valid"
msgstr "valide"
#: participation/admin.py:24 participation/models.py:332
#: participation/admin.py:24 participation/models.py:331
msgid "selected for final"
msgstr "sélectionnée pour la finale"
#: participation/admin.py:57 participation/admin.py:116
#: participation/models.py:445 participation/tables.py:109
#: participation/models.py:444 participation/tables.py:109
msgid "defender"
msgstr "défenseur⋅se"
#: participation/admin.py:61 participation/models.py:452
#: participation/models.py:635
#: participation/admin.py:61 participation/models.py:451
#: participation/models.py:634
msgid "opponent"
msgstr "opposant⋅e"
#: participation/admin.py:65 participation/models.py:459
#: participation/models.py:636
#: participation/admin.py:65 participation/models.py:458
#: participation/models.py:635
msgid "reporter"
msgstr "rapporteur⋅e"
#: participation/admin.py:120 participation/models.py:590
#: participation/admin.py:120 participation/models.py:589
msgid "problem"
msgstr "numéro de problème"
@ -471,17 +497,17 @@ msgstr "Le fichier envoyé doit être au format PDF."
msgid "The PDF file must not have more than 30 pages."
msgstr "Le fichier PDF ne doit pas avoir plus de 30 pages."
#: participation/forms.py:209
#: participation/forms.py:210
msgid "Add new jury"
msgstr "Ajouter un⋅e nouvelleau juré⋅e"
#: participation/forms.py:224
#: participation/forms.py:225
#: participation/templates/participation/pool_detail.html:77
#: participation/templates/participation/tournament_detail.html:111
msgid "Add"
msgstr "Ajouter"
#: participation/forms.py:237 registration/forms.py:35 registration/forms.py:60
#: participation/forms.py:238 registration/forms.py:35 registration/forms.py:60
msgid "This email address is already used."
msgstr "Cette adresse e-mail est déjà utilisée."
@ -608,53 +634,53 @@ msgstr "organisateur⋅rices"
msgid "final"
msgstr "finale"
#: participation/models.py:297 registration/admin.py:92
#: participation/models.py:296 registration/admin.py:93
msgid "tournaments"
msgstr "tournois"
#: participation/models.py:326
#: participation/models.py:325
msgid "valid team"
msgstr "équipe valide"
#: participation/models.py:327
#: participation/models.py:326
msgid "The participation got the validation of the organizers."
msgstr "La participation a été validée par les organisateur⋅rices."
#: participation/models.py:333
#: participation/models.py:332
msgid "The team is selected for the final tournament."
msgstr "L'équipe est sélectionnée pour la finale."
#: participation/models.py:340
#: participation/models.py:339
#, python-brace-format
msgid "Participation of the team {name} ({trigram})"
msgstr "Participation de l'équipe {name} ({trigram})"
#: participation/models.py:344 participation/models.py:376
#: participation/models.py:343 participation/models.py:375
msgid "participations"
msgstr "participations"
#: participation/models.py:358 participation/models.py:359
#: participation/models.py:357 participation/models.py:358
#, python-brace-format
msgid "Round {round}"
msgstr "Tour {round}"
#: participation/models.py:382
#: participation/models.py:381
msgid "juries"
msgstr "jurys"
#: participation/models.py:389
#: participation/models.py:388
msgid "BigBlueButton URL"
msgstr "Lien BigBlueButton"
#: participation/models.py:390
#: participation/models.py:389
msgid "The link of the BBB visio for this pool."
msgstr "Le lien du salon BBB pour cette poule."
#: participation/models.py:395
#: participation/models.py:394
msgid "results available"
msgstr "résultats disponibles"
#: participation/models.py:396
#: participation/models.py:395
msgid ""
"Check this case when results become accessible to teams. They stay "
"accessible to you. Only averages are given."
@ -663,20 +689,20 @@ msgstr ""
"Ils restent toujours accessibles pour vous. Seules les moyennes sont "
"communiquées."
#: participation/models.py:416
#: participation/models.py:415
#, python-brace-format
msgid "Pool of day {round} for tournament {tournament} with teams {teams}"
msgstr "Poule du jour {round} du tournoi {tournament} avec les équipes {teams}"
#: participation/models.py:436
#: participation/models.py:435
msgid "defended solution"
msgstr "solution défendue"
#: participation/models.py:464
#: participation/models.py:463
msgid "penalties"
msgstr "pénalités"
#: participation/models.py:466
#: participation/models.py:465
msgid ""
"Number of penalties for the defender. The defender will loose a 0.5 "
"coefficient per penalty."
@ -684,120 +710,120 @@ msgstr ""
"Nombre de pénalités pour læ défenseur⋅se. Læ défenseur⋅se perd un "
"coefficient 0.5 sur sa présentation orale par pénalité."
#: participation/models.py:526 participation/models.py:529
#: participation/models.py:532
#: participation/models.py:525 participation/models.py:528
#: participation/models.py:531
#, python-brace-format
msgid "Team {trigram} is not registered in the pool."
msgstr "L'équipe {trigram} n'est pas inscrite dans la poule."
#: participation/models.py:537
#: participation/models.py:536
#, python-brace-format
msgid "Passage of {defender} for problem {problem}"
msgstr "Passage de {defender} pour le problème {problem}"
#: participation/models.py:541 participation/models.py:549
#: participation/models.py:630 participation/models.py:672
#: participation/models.py:540 participation/models.py:548
#: participation/models.py:629 participation/models.py:671
msgid "passage"
msgstr "passage"
#: participation/models.py:542
#: participation/models.py:541
msgid "passages"
msgstr "passages"
#: participation/models.py:560
#: participation/models.py:559
msgid "difference"
msgstr "différence"
#: participation/models.py:561
#: participation/models.py:560
msgid "Score to add/remove on the final score"
msgstr "Score à ajouter/retrancher au score final"
#: participation/models.py:568
#: participation/models.py:567
msgid "tweak"
msgstr "harmonisation"
#: participation/models.py:569
#: participation/models.py:568
msgid "tweaks"
msgstr "harmonisations"
#: participation/models.py:597
#: participation/models.py:596
msgid "solution for the final tournament"
msgstr "solution pour la finale"
#: participation/models.py:602 participation/models.py:641
#: participation/models.py:601 participation/models.py:640
msgid "file"
msgstr "fichier"
#: participation/models.py:608
#: participation/models.py:607
#, python-brace-format
msgid "Solution of team {team} for problem {problem}"
msgstr "Solution de l'équipe {team} pour le problème {problem}"
#: participation/models.py:610
#: participation/models.py:609
msgid "for final"
msgstr "pour la finale"
#: participation/models.py:613
#: participation/models.py:612
msgid "solution"
msgstr "solution"
#: participation/models.py:614
#: participation/models.py:613
msgid "solutions"
msgstr "solutions"
#: participation/models.py:647
#: participation/models.py:646
#, 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}"
#: participation/models.py:655
#: participation/models.py:654
msgid "synthesis"
msgstr "note de synthèse"
#: participation/models.py:656
#: participation/models.py:655
msgid "syntheses"
msgstr "notes de synthèse"
#: participation/models.py:665
#: participation/models.py:664
msgid "jury"
msgstr "jury"
#: participation/models.py:677
#: participation/models.py:676
msgid "defender writing note"
msgstr "note d'écrit de læ défenseur⋅se"
#: participation/models.py:683
#: participation/models.py:682
msgid "defender oral note"
msgstr "note d'oral de læ défenseur⋅se"
#: participation/models.py:689
#: participation/models.py:688
msgid "opponent writing note"
msgstr "note d'écrit de l'opposant⋅e"
#: participation/models.py:695
#: participation/models.py:694
msgid "opponent oral note"
msgstr "note d'oral de l'opposant⋅e"
#: participation/models.py:701
#: participation/models.py:700
msgid "reporter writing note"
msgstr "note d'écrit de læ rapporteur⋅e"
#: participation/models.py:707
#: participation/models.py:706
msgid "reporter oral note"
msgstr "note d'oral de læ rapporteur⋅e"
#: participation/models.py:725
#: participation/models.py:724
#, python-brace-format
msgid "Notes of {jury} for {passage}"
msgstr "Notes de {jury} pour le {passage}"
#: participation/models.py:732
#: participation/models.py:731
msgid "note"
msgstr "note"
#: participation/models.py:733
#: participation/models.py:732
msgid "notes"
msgstr "notes"
@ -1061,8 +1087,8 @@ msgid ""
"Be careful: this form register new users. To add existing users into the "
"jury, please use this form:"
msgstr ""
"Attention : ce formulaire inscrit des nouvelleaux utilisateur⋅rices. "
"Pour ajouter des utilisateur⋅rices au jury, utilisez ce formulaire :"
"Attention : ce formulaire inscrit des nouvelleaux utilisateur⋅rices. Pour "
"ajouter des utilisateur⋅rices au jury, utilisez ce formulaire :"
#: participation/templates/participation/pool_add_jurys.html:11
#: participation/templates/participation/pool_add_jurys.html:34
@ -1504,43 +1530,43 @@ msgstr "Participation de l'équipe {trigram}"
msgid "You can't upload a solution after the deadline."
msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
#: participation/views.py:727
#: participation/views.py:730
#, python-brace-format
msgid "Jurys of {pool}"
msgstr "Juré⋅es de la {pool}"
#: participation/views.py:749
#: participation/views.py:757
msgid "New TFJM² jury account"
msgstr "Nouveau compte de juré⋅e pour le TFJM²"
#: participation/views.py:761
#: participation/views.py:770
#, python-brace-format
msgid "The jury {name} has been successfully added!"
msgstr "Læ juré⋅e {name} a été ajouté⋅e avec succès !"
#: participation/views.py:796
#: participation/views.py:806
msgid "The following user is not registered as a jury:"
msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :"
#: participation/views.py:804
#: participation/views.py:814
msgid "Notes were successfully uploaded."
msgstr "Les notes ont bien été envoyées."
#: participation/views.py:916
#: participation/views.py:926
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."
#: registration/admin.py:21 registration/admin.py:36 registration/admin.py:52
#: registration/admin.py:68 registration/admin.py:84
#: registration/admin.py:21 registration/admin.py:37 registration/admin.py:53
#: registration/admin.py:69 registration/admin.py:85
msgid "first name"
msgstr "prénom"
#: registration/admin.py:25 registration/admin.py:40 registration/admin.py:56
#: registration/admin.py:72 registration/admin.py:88 registration/tables.py:17
#: registration/admin.py:25 registration/admin.py:41 registration/admin.py:57
#: registration/admin.py:73 registration/admin.py:89 registration/tables.py:17
msgid "last name"
msgstr "nom de famille"
#: registration/admin.py:104
#: registration/admin.py:105
msgid "registration type"
msgstr "type d'inscription"
@ -1552,7 +1578,7 @@ msgstr "rôle"
msgid "participant"
msgstr "participant⋅e"
#: registration/forms.py:25 registration/models.py:286
#: registration/forms.py:25 registration/models.py:285
msgid "coach"
msgstr "encadrant⋅e"
@ -1577,7 +1603,7 @@ msgstr "email confirmé"
msgid "Activate your TFJM² account"
msgstr "Activez votre compte du TFJM²"
#: registration/models.py:99 registration/models.py:339
#: registration/models.py:99 registration/models.py:338
msgid "registration"
msgstr "inscription"
@ -1633,91 +1659,91 @@ msgstr ""
msgid "photo authorization"
msgstr "autorisation de droit à l'image"
#: registration/models.py:196
#: registration/models.py:195
msgid "participant registration"
msgstr "inscription de participant⋅e"
#: registration/models.py:197
#: registration/models.py:196
msgid "participant registrations"
msgstr "inscriptions de participant⋅es"
#: registration/models.py:206
#: registration/models.py:205
msgid "birth date"
msgstr "date de naissance"
#: registration/models.py:212
#: registration/models.py:211
msgid "12th grade"
msgstr "Terminale"
#: registration/models.py:213
#: registration/models.py:212
msgid "11th grade"
msgstr "Première"
#: registration/models.py:214
#: registration/models.py:213
msgid "10th grade or lower"
msgstr "Seconde ou inférieur"
#: registration/models.py:216
#: registration/models.py:215
msgid "student class"
msgstr "classe"
#: registration/models.py:221
#: registration/models.py:220
msgid "school"
msgstr "école"
#: registration/models.py:226
#: registration/models.py:225
msgid "responsible name"
msgstr "nom de læ responsable légal⋅e"
#: registration/models.py:231
#: registration/models.py:230
msgid "responsible phone number"
msgstr "numéro de téléphone de læ responsable légal⋅e"
#: registration/models.py:236
#: registration/models.py:235
msgid "responsible email address"
msgstr "adresse e-mail de læ responsable légal⋅e"
#: registration/models.py:241
#: registration/models.py:240
msgid "parental authorization"
msgstr "autorisation parentale"
#: registration/models.py:248
#: registration/models.py:247
msgid "health sheet"
msgstr "fiche sanitaire"
#: registration/models.py:255
#: registration/models.py:254
msgid "vaccine sheet"
msgstr "carnet de vaccination"
#: registration/models.py:263
#: registration/models.py:262
msgid "student"
msgstr "étudiant⋅e"
#: registration/models.py:271
#: registration/models.py:270
msgid "student registration"
msgstr "inscription d'élève"
#: registration/models.py:272
#: registration/models.py:271
msgid "student registrations"
msgstr "inscriptions d'élève"
#: registration/models.py:281 registration/models.py:303
#: registration/models.py:280 registration/models.py:302
msgid "professional activity"
msgstr "activité professionnelle"
#: registration/models.py:294
#: registration/models.py:293
msgid "coach registration"
msgstr "inscription d'encadrant⋅e"
#: registration/models.py:295
#: registration/models.py:294
msgid "coach registrations"
msgstr "inscriptions d'encadrant⋅es"
#: registration/models.py:307
#: registration/models.py:306
msgid "administrator"
msgstr "administrateur⋅rice"
#: registration/models.py:308
#: registration/models.py:307
msgid ""
"An administrator has all rights. Please don't give this right to all juries "
"and volunteers."
@ -1725,76 +1751,76 @@ msgstr ""
"Un⋅e administrateur⋅rice a tous les droits. Merci de ne pas donner ce droit "
"à toustes les juré⋅es et bénévoles."
#: registration/models.py:318
#: registration/models.py:317
msgid "admin"
msgstr "admin"
#: registration/models.py:318
#: registration/models.py:317
msgid "volunteer"
msgstr "bénévole"
#: registration/models.py:326
#: registration/models.py:325
msgid "volunteer registration"
msgstr "inscription de bénévole"
#: registration/models.py:327
#: registration/models.py:326
msgid "volunteer registrations"
msgstr "inscriptions de bénévoles"
#: registration/models.py:343
#: registration/models.py:342
msgid "type"
msgstr "type"
#: registration/models.py:346
#: registration/models.py:345
msgid "No payment"
msgstr "Pas de paiement"
#: registration/models.py:348
#: registration/models.py:347
msgid "Scholarship"
msgstr "Notification de bourse"
#: registration/models.py:349
#: registration/models.py:348
msgid "Bank transfer"
msgstr "Virement bancaire"
#: registration/models.py:350
#: registration/models.py:349
msgid "Other (please indicate)"
msgstr "Autre (veuillez spécifier)"
#: registration/models.py:351
#: registration/models.py:350
msgid "The tournament is free"
msgstr "Le tournoi est gratuit"
#: registration/models.py:358
#: registration/models.py:357
msgid "scholarship file"
msgstr "Notification de bourse"
#: registration/models.py:359
#: registration/models.py:358
msgid "only if you have a scholarship."
msgstr "Nécessaire seulement si vous déclarez être boursier."
#: registration/models.py:366
#: registration/models.py:365
msgid "additional information"
msgstr "informations additionnelles"
#: registration/models.py:367
#: registration/models.py:366
msgid "To help us to find your payment."
msgstr "Pour nous aider à retrouver votre paiement, si nécessaire."
#: registration/models.py:373
#: registration/models.py:372
msgid "payment valid"
msgstr "paiement valide"
#: registration/models.py:382
#: registration/models.py:381
#, python-brace-format
msgid "Payment of {registration}"
msgstr "Paiement de {registration}"
#: registration/models.py:385
#: registration/models.py:384
msgid "payment"
msgstr "paiement"
#: registration/models.py:386
#: registration/models.py:385
msgid "payments"
msgstr "paiements"
@ -2367,10 +2393,6 @@ msgstr "À propos"
msgid "Search results"
msgstr "Résultats de la recherche"
#: tfjm/templates/base_modal.html:17
msgid "Close"
msgstr "Fermer"
#: tfjm/templates/registration/logged_out.html:8
msgid "Thanks for spending some quality time with the Web site today."
msgstr "Merci d'avoir utilisé la plateforme du TFJM²."