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

Compare commits

..

No commits in common. "8778f58fe4ce9391b468a92e1b6c0febcbb6b571" and "746aae464a9a94caa3507aa881b70832fad1e2d2" have entirely different histories.

4 changed files with 47 additions and 431 deletions

View File

@ -2,16 +2,13 @@
# 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
@ -118,9 +115,6 @@ 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)
@ -351,14 +345,11 @@ 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 if state == 'DICE_SELECT_POULES' else td.choice_dice) == v]
dups = [td for td in tds if td.passage_dice == v]
for dup in dups:
# Reset the dice
if state == 'DICE_SELECT_POULES':
dup.passage_dice = None
else:
dup.choice_dice = None
dup.passage_dice = None
await dup.asave()
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
@ -656,7 +647,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
:param pool: The pool to end.
"""
msg = self.tournament.draw.last_message
r = self.tournament.draw.current_round
r = pool.round
if pool.size == 5:
# Maybe reorder teams if the same problem is presented twice
@ -934,350 +925,6 @@ 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')
content_type = await ContentType.objects.aget(app_label=TeamDraw._meta.app_label,
model=TeamDraw._meta.model_name)
state = self.tournament.draw.get_state()
self.tournament.draw.last_message = ""
await self.tournament.draw.asave()
r = self.tournament.draw.current_round
if state == 'DRAW_ENDED' or state == 'WAITING_FINAL':
td = self.tournament.draw.current_round.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})
elif state == 'WAITING_CHOOSE_PROBLEM':
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})
elif state == 'WAITING_DRAW_PROBLEM':
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})
elif state == 'DICE_ORDER_POULE':
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})
elif state == 'DICE_SELECT_POULES':
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()
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 draw_alert(self, content):
"""
Send alert to the current user.

View File

@ -20,15 +20,6 @@ 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.
@ -592,19 +583,13 @@ document.addEventListener('DOMContentLoaded', () => {
function setProblemAccepted(round, team, problem) {
// Update recap
let recapDiv = document.getElementById(`recap-${tournament.id}-round-${round}-team-${team}-accepted`)
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 : '?'}`
recapDiv.classList.remove('text-bg-warning')
recapDiv.classList.add('text-bg-success')
recapDiv.textContent = `${team} 📃 ${problem}`
// Update table
let tableSpan = document.getElementById(`table-${tournament.id}-round-${round}-problem-${team}`)
tableSpan.textContent = problem ? problem : '?'
tableSpan.textContent = problem
}
/**
@ -618,9 +603,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`
@ -629,11 +614,6 @@ document.addEventListener('DOMContentLoaded', () => {
}
penaltyDiv.textContent = `${0.5 * (rejected.length - (problems_count - 5))}`
}
else {
// Eventually remove this div
if (penaltyDiv !== null)
penaltyDiv.remove()
}
}
/**

View File

@ -54,13 +54,6 @@
<div class="card">
<div class="card-header">
Recap
{% if user.registration.is_volunteer %}
<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>
<div class="card-body">
<div id="recap-{{ tournament.id }}-round-list" class="row">
@ -327,17 +320,17 @@
{% 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>
{% 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 id="abort{{ tournament.id }}Modal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-05 18:54+0200\n"
"POT-Creation-Date: 2023-04-05 18:37+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"
@ -46,64 +46,64 @@ msgstr "Tirage au sort"
msgid "You are not an organizer."
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice."
#: draw/consumers.py:148
#: draw/consumers.py:145
msgid "The draw is already started."
msgstr "Le tirage a déjà commencé."
#: draw/consumers.py:154
#: draw/consumers.py:151
msgid "Invalid format"
msgstr "Format invalide"
#: draw/consumers.py:159
#: draw/consumers.py:156
#, 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:164
#: draw/consumers.py:161
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:192
#: draw/consumers.py:189
msgid "Draw started!"
msgstr "Le tirage a commencé !"
#: draw/consumers.py:212
#: draw/consumers.py:209
#, 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: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
#: draw/consumers.py:220 draw/consumers.py:245 draw/consumers.py:576
#: draw/consumers.py:765 draw/consumers.py:847 draw/consumers.py:864
#: 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:235
#: draw/consumers.py:232
#, 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:275 draw/consumers.py:296 draw/consumers.py:525
#: draw/consumers.py:584 draw/consumers.py:773
#: draw/consumers.py:272 draw/consumers.py:293 draw/consumers.py:522
#: draw/consumers.py:581 draw/consumers.py:770
msgid "This is not the time for this."
msgstr "Ce n'est pas le moment pour cela."
#: draw/consumers.py:288 draw/consumers.py:291
#: draw/consumers.py:285 draw/consumers.py:288
msgid "You've already launched the dice."
msgstr "Vous avez déjà lancé le dé."
#: draw/consumers.py:294
#: draw/consumers.py:291
msgid "It is not your turn."
msgstr "Ce n'est pas votre tour."
#: draw/consumers.py:372
#: draw/consumers.py:369
#, 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:870
#: draw/consumers.py:867
msgid "This is only available for the final tournament."
msgstr "Cela n'est possible que pour la finale."
@ -312,46 +312,42 @@ msgstr "Exporter"
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
#: draw/templates/draw/tournament_content.html:209 participation/admin.py:100
#: participation/models.py:125 participation/models.py:310
#: registration/models.py:127
msgid "team"
msgstr "équipe"
#: draw/templates/draw/tournament_content.html:225
#: draw/templates/draw/tournament_content.html:226
#: draw/templates/draw/tournament_content.html:227
#: draw/templates/draw/tournament_content.html:228
#: draw/templates/draw/tournament_content.html:229
#: draw/templates/draw/tournament_content.html:219
#: draw/templates/draw/tournament_content.html:220
#: draw/templates/draw/tournament_content.html:221
#: draw/templates/draw/tournament_content.html:222
#: draw/templates/draw/tournament_content.html:223
msgid "Room"
msgstr "Salle"
#: draw/templates/draw/tournament_content.html:335
#: draw/templates/draw/tournament_content.html:353
#: draw/templates/draw/tournament_content.html:329
#: draw/templates/draw/tournament_content.html:347
msgid "Abort"
msgstr "Annuler"
#: draw/templates/draw/tournament_content.html:344
#: draw/templates/draw/tournament_content.html:338
msgid "Are you sure?"
msgstr "Êtes-vous sûr⋅e ?"
#: draw/templates/draw/tournament_content.html:348
#: draw/templates/draw/tournament_content.html:342
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
#: draw/templates/draw/tournament_content.html:343
msgid "This operation is irreversible."
msgstr "Cette opération est irréversible."
#: draw/templates/draw/tournament_content.html:350
#: draw/templates/draw/tournament_content.html:344
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
#: draw/templates/draw/tournament_content.html:348
#: tfjm/templates/base_modal.html:17
msgid "Close"
msgstr "Fermer"