diff --git a/draw/consumers.py b/draw/consumers.py index 13acf45..18aca04 100644 --- a/draw/consumers.py +++ b/draw/consumers.py @@ -1,6 +1,6 @@ # Copyright (C) 2023 by Animath # SPDX-License-Identifier: GPL-3.0-or-later - +from collections import OrderedDict from random import randint from asgiref.sync import sync_to_async @@ -436,6 +436,37 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): {'type': 'draw.box_visibility', 'visible': True}) else: # Pool is ended + if pool.size == 5: + # Maybe reorder teams if the same problem is presented twice + problems = OrderedDict() + async for td in pool.team_draws: + problems.setdefault(td.accepted, []) + problems[td.accepted].append(td) + p_index = 0 + for pb, tds in problems.items(): + if len(tds) == 2: + tds[0].passage_index = p_index + tds[1].passage_index = p_index + 1 + p_index += 2 + await sync_to_async(tds[0].save)() + await sync_to_async(tds[1].save)() + for pb, tds in problems.items(): + if len(tds) == 1: + tds[0].passage_index = p_index + p_index += 1 + await sync_to_async(tds[0].save)() + + print(p_index) + + await self.channel_layer.group_send(f"tournament-{self.tournament.id}", { + 'type': 'draw.reorder_pool', + 'round': r.number, + 'pool': pool.get_letter_display(), + 'teams': [td.participation.team.trigram + async for td in pool.team_draws.prefetch_related('participation__team')], + 'problems': [td.accepted async for td in pool.team_draws], + }) + msg += f"

Le tirage de la poule {pool.get_letter_display()}{r.number} est terminé. " \ f"Le tableau récapitulatif est en bas." self.tournament.draw.last_message = msg @@ -451,7 +482,6 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): else: # Round is ended # TODO: For the final tournament, add some adjustments - # TODO: Make some adjustments for 5-teams-pools if r.number == 1: # Next round r2 = await self.tournament.draw.round_set.filter(number=2).aget() @@ -583,3 +613,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): async def draw_reject_problem(self, content): await self.send_json({'type': 'reject_problem', 'round': content['round'], 'team': content['team'], 'rejected': content['rejected']}) + + async def draw_reorder_pool(self, content): + await self.send_json({'type': 'reorder_poule', 'round': content['round'], + 'poule': content['pool'], 'teams': content['teams'], + 'problems': content['problems']}) diff --git a/draw/static/draw.js b/draw/static/draw.js index 95286c5..e225b94 100644 --- a/draw/static/draw.js +++ b/draw/static/draw.js @@ -238,7 +238,7 @@ document.addEventListener('DOMContentLoaded', () => { tablesRoundDiv = document.createElement('div') tablesRoundDiv.id = `tables-${tournament.id}-round-${round}` - tablesRoundDiv.classList.add('card-body') + tablesRoundDiv.classList.add('card-body', 'd-flex', 'flex-wrap') card.append(tablesRoundDiv) } @@ -246,148 +246,153 @@ document.addEventListener('DOMContentLoaded', () => { if (poule.teams.length === 0) continue - let pouleTable = document.getElementById(`table-${tournament.id}-${round}-${poule.letter}`) - if (pouleTable === null) { - // Create table - let card = document.createElement('div') - card.classList.add('card', 'my-3') - tablesRoundDiv.append(card) + updatePouleTable(round, poule) + } + } + } - let cardHeader = document.createElement('div') - cardHeader.classList.add('card-header') - cardHeader.innerHTML = `

Poule ${poule.letter}

` - card.append(cardHeader) + function updatePouleTable(round, poule) { + let tablesRoundDiv = document.getElementById(`tables-${tournament.id}-round-${round}`) + let pouleTable = document.getElementById(`table-${tournament.id}-${round}-${poule.letter}`) + if (pouleTable === null) { + // Create table + let card = document.createElement('div') + card.classList.add('card', 'w-100', 'my-3', `order-${poule.letter.charCodeAt(0) - 64}`) + tablesRoundDiv.append(card) - let cardBody = document.createElement('div') - cardBody.classList.add('card-body') - card.append(cardBody) + let cardHeader = document.createElement('div') + cardHeader.classList.add('card-header') + cardHeader.innerHTML = `

Poule ${poule.letter}${round}

` + card.append(cardHeader) - pouleTable = document.createElement('table') - pouleTable.id = `table-${tournament.id}-${round}-${poule.letter}` - pouleTable.classList.add('table', 'table-stripped') - cardBody.append(pouleTable) + let cardBody = document.createElement('div') + cardBody.classList.add('card-body') + card.append(cardBody) - let thead = document.createElement('thead') - pouleTable.append(thead) + pouleTable = document.createElement('table') + pouleTable.id = `table-${tournament.id}-${round}-${poule.letter}` + pouleTable.classList.add('table', 'table-stripped') + cardBody.append(pouleTable) - let phaseTr = document.createElement('tr') - thead.append(phaseTr) + let thead = document.createElement('thead') + pouleTable.append(thead) - let teamTh = document.createElement('th') - teamTh.classList.add('text-center') - teamTh.rowSpan = poule.teams.length === 5 ? 3 : 2 - teamTh.textContent = "Équipe" - phaseTr.append(teamTh) + let phaseTr = document.createElement('tr') + thead.append(phaseTr) - for (let i = 1; i <= (poule.teams.length === 4 ? 4 : 3); ++i) { - let phaseTh = document.createElement('th') - phaseTh.classList.add('text-center') - if (poule.teams.length === 5 && i < 3) - phaseTh.colSpan = 2 - phaseTh.textContent = `Phase ${i}` - phaseTr.append(phaseTh) + let teamTh = document.createElement('th') + teamTh.classList.add('text-center') + teamTh.rowSpan = poule.teams.length === 5 ? 3 : 2 + teamTh.textContent = "Équipe" + phaseTr.append(teamTh) + + for (let i = 1; i <= (poule.teams.length === 4 ? 4 : 3); ++i) { + let phaseTh = document.createElement('th') + phaseTh.classList.add('text-center') + if (poule.teams.length === 5 && i < 3) + phaseTh.colSpan = 2 + phaseTh.textContent = `Phase ${i}` + phaseTr.append(phaseTh) + } + + if (poule.teams.length === 5) { + let roomTr = document.createElement('tr') + thead.append(roomTr) + + for (let i = 0; i < 5; ++i) { + let roomTh = document.createElement('th') + roomTh.classList.add('text-center') + roomTh.textContent = `Salle ${1 + (i % 2)}` + roomTr.append(roomTh) + } + } + + let problemTr = document.createElement('tr') + thead.append(problemTr) + + for (let team of poule.teams) { + let problemTh = document.createElement('th') + problemTh.classList.add('text-center') + problemTh.innerHTML = `Pb. ?` + problemTr.append(problemTh) + } + + let tbody = document.createElement('tbody') + pouleTable.append(tbody) + + for (let i = 0; i < poule.teams.length; ++i) { + let team = poule.teams[i] + + let teamTr = document.createElement('tr') + tbody.append(teamTr) + + let teamTd = document.createElement('td') + teamTd.classList.add('text-center') + teamTd.innerText = team + teamTr.append(teamTd) + + let defenderTd = document.createElement('td') + defenderTd.classList.add('text-center') + defenderTd.innerText = 'Déf' + + let opponentTd = document.createElement('td') + opponentTd.classList.add('text-center') + opponentTd.innerText = 'Opp' + + let reporterTd = document.createElement('td') + reporterTd.classList.add('text-center') + reporterTd.innerText = 'Rap' + + let emptyTd = document.createElement('td') + let emptyTd2 = document.createElement('td') + + + if (poule.teams.length === 3) { + switch (i) { + case 0: + teamTr.append(defenderTd, reporterTd, opponentTd) + break + case 1: + teamTr.append(opponentTd, defenderTd, reporterTd) + break + case 2: + teamTr.append(reporterTd, opponentTd, defenderTd) + break } - - if (poule.teams.length === 5) { - let roomTr = document.createElement('tr') - thead.append(roomTr) - - for (let i = 0; i < 5; ++i) { - let roomTh = document.createElement('th') - roomTh.classList.add('text-center') - roomTh.textContent = `Salle ${1 + (i % 2)}` - roomTr.append(roomTh) - } + } + else if (poule.teams.length === 4) { + switch (i) { + case 0: + teamTr.append(defenderTd, emptyTd, reporterTd, opponentTd) + break + case 1: + teamTr.append(opponentTd, defenderTd, emptyTd, reporterTd) + break + case 2: + teamTr.append(reporterTd, opponentTd, defenderTd, emptyTd) + break + case 3: + teamTr.append(emptyTd, reporterTd, opponentTd, defenderTd) + break } - - let problemTr = document.createElement('tr') - thead.append(problemTr) - - for (let team of poule.teams) { - let problemTh = document.createElement('th') - problemTh.classList.add('text-center') - problemTh.innerHTML = `Pb. ?` - problemTr.append(problemTh) - } - - let tbody = document.createElement('tbody') - pouleTable.append(tbody) - - for (let i = 0; i < poule.teams.length; ++i) { - let team = poule.teams[i] - - let teamTr = document.createElement('tr') - tbody.append(teamTr) - - let teamTd = document.createElement('td') - teamTd.classList.add('text-center') - teamTd.innerText = team - teamTr.append(teamTd) - - let defenderTd = document.createElement('td') - defenderTd.classList.add('text-center') - defenderTd.innerText = 'Déf' - - let opponentTd = document.createElement('td') - opponentTd.classList.add('text-center') - opponentTd.innerText = 'Opp' - - let reporterTd = document.createElement('td') - reporterTd.classList.add('text-center') - reporterTd.innerText = 'Rap' - - let emptyTd = document.createElement('td') - let emptyTd2 = document.createElement('td') - - - if (poule.teams.length === 3) { - switch (i) { - case 0: - teamTr.append(defenderTd, reporterTd, opponentTd) - break - case 1: - teamTr.append(opponentTd, defenderTd, reporterTd) - break - case 2: - teamTr.append(reporterTd, opponentTd, defenderTd) - break - } - } - else if (poule.teams.length === 4) { - switch (i) { - case 0: - teamTr.append(defenderTd, emptyTd, reporterTd, opponentTd) - break - case 1: - teamTr.append(opponentTd, defenderTd, emptyTd, reporterTd) - break - case 2: - teamTr.append(reporterTd, opponentTd, defenderTd, emptyTd) - break - case 3: - teamTr.append(emptyTd, reporterTd, opponentTd, defenderTd) - break - } - } - else if (poule.teams.length === 5) { - switch (i) { - case 0: - teamTr.append(defenderTd, emptyTd, opponentTd, reporterTd, emptyTd2) - break - case 1: - teamTr.append(emptyTd, defenderTd, reporterTd, emptyTd2, opponentTd) - break - case 2: - teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd2, reporterTd) - break - case 3: - teamTr.append(reporterTd, opponentTd, emptyTd, defenderTd, emptyTd2) - break - case 4: - teamTr.append(emptyTd, reporterTd, emptyTd2, opponentTd, defenderTd) - break - } - } + } + else if (poule.teams.length === 5) { + switch (i) { + case 0: + teamTr.append(defenderTd, emptyTd, opponentTd, reporterTd, emptyTd2) + break + case 1: + teamTr.append(emptyTd, defenderTd, reporterTd, emptyTd2, opponentTd) + break + case 2: + teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd2, reporterTd) + break + case 3: + teamTr.append(reporterTd, opponentTd, emptyTd, defenderTd, emptyTd2) + break + case 4: + teamTr.append(emptyTd, reporterTd, emptyTd2, opponentTd, defenderTd) + break } } } @@ -442,6 +447,20 @@ document.addEventListener('DOMContentLoaded', () => { } } + function reorderPoule(round, poule, teams, problems) { + let table = document.getElementById(`table-${tournament.id}-${round}-${poule}`) + table.parentElement.parentElement.remove() + + updatePouleTable(round, {'letter': poule, 'teams': teams}) + + for (let i = 0; i < teams.length; ++i) { + let team = teams[i] + let problem = problems[i] + + setProblemAccepted(round, team, problem) + } + } + socket.addEventListener('message', e => { const data = JSON.parse(e.data) console.log(data) @@ -486,6 +505,9 @@ document.addEventListener('DOMContentLoaded', () => { case 'reject_problem': setProblemRejected(data.round, data.team, data.rejected) break + case 'reorder_poule': + reorderPoule(data.round, data.poule, data.teams, data.problems) + break } }) diff --git a/draw/templates/draw/tournament_content.html b/draw/templates/draw/tournament_content.html index fff5cc0..3dbf2a9 100644 --- a/draw/templates/draw/tournament_content.html +++ b/draw/templates/draw/tournament_content.html @@ -160,10 +160,10 @@ {{ round }} -
+
{% for pool in round.pool_set.all %} {% if pool.teamdraw_set.count %} -
+

{% trans "pool"|capfirst %} {{ pool }} diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index dbc592a..3e79205 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: TFJM\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-03-24 12:29+0100\n" +"POT-Creation-Date: 2023-03-25 07:20+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Emmy D'Anello \n" "Language-Team: LANGUAGE \n" @@ -25,53 +25,54 @@ msgstr "API" msgid "Draw" msgstr "Tirage au sort" -#: draw/consumers.py:20 +#: draw/consumers.py:21 msgid "You are not an organizer." msgstr "Vous n'êtes pas un⋅e organisateur⋅rice." -#: draw/consumers.py:81 +#: draw/consumers.py:91 msgid "Invalid format" msgstr "Format invalide" -#: draw/consumers.py:85 +#: draw/consumers.py:95 #, 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:105 +#: draw/consumers.py:121 msgid "Draw started!" msgstr "Le tirage a commencé !" -#: draw/consumers.py:115 +#: draw/consumers.py:131 #, 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:121 +#: draw/consumers.py:142 #, 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:141 draw/consumers.py:144 +#: draw/consumers.py:176 draw/consumers.py:179 msgid "You've already launched the dice." msgstr "Vous avez déjà lancé le dé." -#: draw/consumers.py:147 +#: draw/consumers.py:182 msgid "It is not your turn." msgstr "Ce n'est pas votre tour." -#: draw/consumers.py:149 +#: draw/consumers.py:184 draw/consumers.py:353 draw/consumers.py:392 +#: draw/consumers.py:510 msgid "This is not the time for this." msgstr "Ce n'est pas le moment pour cela." -#: draw/consumers.py:182 draw/consumers.py:279 +#: draw/consumers.py:217 draw/consumers.py:316 #, 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:216 +#: draw/consumers.py:251 msgid "Two pools are identical. Please relaunch your dices." msgstr "Deux poules sont identiques. Merci de relancer vos dés." @@ -88,98 +89,98 @@ msgstr "tour actuel" msgid "last message" msgstr "dernier message" -#: draw/models.py:102 draw/models.py:110 +#: draw/models.py:104 draw/models.py:112 msgid "draw" msgstr "tirage au sort" -#: draw/models.py:103 +#: draw/models.py:105 msgid "draws" msgstr "tirages au sort" -#: draw/models.py:115 +#: draw/models.py:117 msgid "Round 1" msgstr "Tour 1" -#: draw/models.py:116 +#: draw/models.py:118 msgid "Round 2" msgstr "Tour 2" -#: draw/models.py:118 +#: draw/models.py:120 msgid "number" msgstr "numéro" -#: draw/models.py:127 +#: draw/models.py:129 msgid "current pool" msgstr "poule actuelle" -#: draw/models.py:138 draw/models.py:199 participation/models.py:355 +#: draw/models.py:144 draw/models.py:215 participation/models.py:355 msgid "round" msgstr "tour" -#: draw/models.py:139 +#: draw/models.py:145 msgid "rounds" msgstr "tours" -#: draw/models.py:154 +#: draw/models.py:160 msgid "letter" msgstr "lettre" -#: draw/models.py:158 +#: draw/models.py:164 #: participation/templates/participation/tournament_detail.html:15 msgid "size" msgstr "taille" -#: draw/models.py:167 +#: draw/models.py:173 msgid "current team" msgstr "équipe actuelle" -#: draw/models.py:185 draw/models.py:207 +#: draw/models.py:201 draw/models.py:223 #: draw/templates/draw/tournament_content.html:70 -#: draw/templates/draw/tournament_content.html:151 participation/models.py:407 +#: draw/templates/draw/tournament_content.html:169 participation/models.py:407 #: participation/models.py:415 msgid "pool" msgstr "poule" -#: draw/models.py:186 participation/models.py:408 +#: draw/models.py:202 participation/models.py:408 msgid "pools" msgstr "poules" -#: draw/models.py:193 participation/models.py:342 participation/models.py:539 +#: draw/models.py:209 participation/models.py:342 participation/models.py:539 #: participation/models.py:569 participation/models.py:607 msgid "participation" msgstr "participation" -#: draw/models.py:214 +#: draw/models.py:230 msgid "passage index" msgstr "numéro de passage" -#: draw/models.py:221 +#: draw/models.py:237 msgid "choose index" msgstr "numéro de choix" -#: draw/models.py:226 draw/models.py:242 participation/models.py:422 +#: draw/models.py:242 draw/models.py:258 participation/models.py:422 #: participation/models.py:576 #, python-brace-format msgid "Problem #{problem}" msgstr "Problème n°{problem}" -#: draw/models.py:230 draw/models.py:246 +#: draw/models.py:246 draw/models.py:262 msgid "accepted problem" msgstr "problème accepté" -#: draw/models.py:237 +#: draw/models.py:253 msgid "last dice" msgstr "dernier dé" -#: draw/models.py:251 +#: draw/models.py:267 msgid "rejected problems" msgstr "problèmes rejetés" -#: draw/models.py:258 +#: draw/models.py:275 msgid "team draw" msgstr "tirage d'équipe" -#: draw/models.py:259 +#: draw/models.py:276 msgid "team draws" msgstr "tirages d'équipe" @@ -203,28 +204,32 @@ msgstr "Derniers jets de dés" msgid "Abort" msgstr "Annuler" -#: draw/templates/draw/tournament_content.html:118 +#: draw/templates/draw/tournament_content.html:119 msgid "Launch dice" msgstr "Lancer le dé" -#: draw/templates/draw/tournament_content.html:125 +#: draw/templates/draw/tournament_content.html:132 +msgid "Draw a problem" +msgstr "Tirer un problème" + +#: draw/templates/draw/tournament_content.html:142 msgid "Accept" msgstr "Accepter" -#: draw/templates/draw/tournament_content.html:128 +#: draw/templates/draw/tournament_content.html:145 msgid "Decline" msgstr "Refuser" -#: draw/templates/draw/tournament_content.html:158 participation/models.py:125 +#: draw/templates/draw/tournament_content.html:176 participation/models.py:125 #: participation/models.py:310 registration/models.py:127 msgid "team" msgstr "équipe" -#: draw/templates/draw/tournament_content.html:168 -#: draw/templates/draw/tournament_content.html:169 -#: draw/templates/draw/tournament_content.html:170 -#: draw/templates/draw/tournament_content.html:171 -#: draw/templates/draw/tournament_content.html:172 +#: draw/templates/draw/tournament_content.html:186 +#: draw/templates/draw/tournament_content.html:187 +#: draw/templates/draw/tournament_content.html:188 +#: draw/templates/draw/tournament_content.html:189 +#: draw/templates/draw/tournament_content.html:190 msgid "Room" msgstr "Salle"