From f85a563cf37f79eec973aee12bdbfaeca4fcf7d4 Mon Sep 17 00:00:00 2001 From: Emmy D'Anello Date: Fri, 24 Mar 2023 11:10:07 +0100 Subject: [PATCH] Auto-generate tables Signed-off-by: Emmy D'Anello --- draw/consumers.py | 30 +- draw/models.py | 16 +- draw/static/draw.js | 279 +++++++++++++++ draw/templates/draw/tournament_content.html | 365 ++++++++++---------- 4 files changed, 498 insertions(+), 192 deletions(-) diff --git a/draw/consumers.py b/draw/consumers.py index fc55ab7..f458c16 100644 --- a/draw/consumers.py +++ b/draw/consumers.py @@ -96,6 +96,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): await Pool.objects.acreate(round=r, letter=j + 1, size=f) for participation in self.participations: await TeamDraw.objects.acreate(participation=participation, round=r) + await self.channel_layer.group_send(f"tournament-{self.tournament.id}", + {'type': 'draw.send_poules', 'round': r}) draw.current_round = r1 await sync_to_async(draw.save)() @@ -106,6 +108,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): {'type': 'draw.start', 'fmt': fmt, 'draw': draw}) await self.channel_layer.group_send(f"tournament-{self.tournament.id}", {'type': 'draw.set_info', 'draw': draw}) + await self.channel_layer.group_send(f"tournament-{self.tournament.id}", + {'type': 'draw.set_active', 'draw': self.tournament.draw}) async def draw_start(self, content): await self.alert(_("The draw for the tournament {tournament} will start.")\ @@ -225,8 +229,14 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): 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"tournament-{self.tournament.id}", + {'type': 'draw.send_poules', + 'round': self.tournament.draw.current_round}) + 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}) elif state == 'DICE_ORDER_POULE' and \ not await TeamDraw.objects.filter(pool=self.tournament.draw.current_round.current_pool, last_dice__isnull=True).aexists(): @@ -238,7 +248,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): tds.append(td) dices = {td: td.last_dice for td in tds} - values = list(dices) + values = list(dices.values()) error = False for v in set(values): if values.count(v) > 1: @@ -271,6 +281,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): 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): return await self.alert(**content) @@ -286,3 +298,19 @@ class DrawConsumer(AsyncJsonWebsocketConsumer): async def draw_dice_visibility(self, content): await self.send_json({'type': 'dice_visibility', 'visible': content['visible']}) + + async def draw_send_poules(self, content): + await self.send_json({'type': 'set_poules', 'round': content['round'].number, + 'poules': [{'letter': pool.get_letter_display(), 'teams': await pool.atrigrams()} + async for pool in content['round'].pool_set.order_by('letter').all()]}) + + async def draw_set_active(self, content): + r = content['draw'].current_round + await self.send_json( + await sync_to_async(lambda: { + 'type': 'set_active', + 'round': r.number, + 'poule': r.current_pool.get_letter_display() if r.current_pool else None, + 'team': r.current_pool.current_team.participation.team.trigram \ + if r.current_pool and r.current_pool.current_team else None, + })()) diff --git a/draw/models.py b/draw/models.py index 0fcea76..ed4a1f5 100644 --- a/draw/models.py +++ b/draw/models.py @@ -63,7 +63,8 @@ class Draw(models.Model): pour déterminer l'ordre de tirage. L'équipe réalisant le plus gros score pourra tirer en premier.""" - s += """

Pour plus de détails sur le déroulement du tirage au sort, + s += "

" if s else "" + s += """Pour plus de détails sur le déroulement du tirage au sort, le règlement est accessible sur https://tfjm.org/reglement.""" return s @@ -100,6 +101,10 @@ class Round(models.Model): verbose_name=_('current pool'), ) + @property + def team_draws(self): + return self.teamdraw_set.order_by('pool__letter', 'passage_index').all() + def __str__(self): return self.get_number_display() @@ -136,9 +141,16 @@ class Pool(models.Model): verbose_name=_('current team'), ) + @property + def team_draws(self): + return self.teamdraw_set.order_by('passage_index').all() + @property def trigrams(self): - return set(td.participation.team.trigram for td in self.teamdraw_set.all()) + return [td.participation.team.trigram for td in self.teamdraw_set.order_by('passage_index').all()] + + async def atrigrams(self): + return await sync_to_async(lambda: self.trigrams)() def __str__(self): return f"{self.get_letter_display()}{self.round.number}" diff --git a/draw/static/draw.js b/draw/static/draw.js index 2fd9a57..aceb599 100644 --- a/draw/static/draw.js +++ b/draw/static/draw.js @@ -82,6 +82,278 @@ document.addEventListener('DOMContentLoaded', () => { div.classList.add('d-none') } + function updatePoules(round, poules) { + let roundList = document.getElementById(`recap-${tournament.id}-round-list`) + let poolListId = `recap-${tournament.id}-round-${round}-pool-list` + let poolList = document.getElementById(poolListId) + if (poolList === null) { + let li = document.createElement('li') + li.id = `recap-${tournament.id}-round-${round}` + li.classList.add('list-group-item') + li.setAttribute('data-tournament', tournament.id) + + let title = document.createElement('strong') + title.textContent = 'Tour ' + round + + poolList = document.createElement('ul') + poolList.id = poolListId + poolList.classList.add('list-group', 'list-group-flush') + + li.append(title, poolList) + roundList.append(li) + } + + let c = 1 + + for (let poule of poules) { + let teamListId = `recap-${tournament.id}-round-${round}-pool-${poule.letter}-team-list` + let teamList = document.getElementById(teamListId) + if (teamList === null) { + let li = document.createElement('li') + li.id = `recap-${tournament.id}-round-${round}-pool-${poule.letter}` + li.classList.add('list-group-item') + li.setAttribute('data-tournament', tournament.id) + + let title = document.createElement('strong') + title.textContent = 'Poule ' + poule.letter + round + + teamList = document.createElement('ul') + teamList.id = teamListId + teamList.classList.add('list-group', 'list-group-flush') + + li.append(title, teamList) + poolList.append(li) + } + + if (poule.teams.length > 0) { + for (let team of poule.teams) { + // Reorder dices + let diceDiv = document.getElementById(`dice-${tournament.id}-${team}`) + diceDiv.parentElement.style.order = c.toString() + c += 1 + + let teamLiId = `recap-team-${team}` + let teamLi = document.getElementById(teamLiId) + + if (teamLi === null) { + teamLi = document.createElement('li') + teamLi.id = teamLiId + teamLi.classList.add('list-group-item') + teamLi.setAttribute('data-tournament', tournament.id) + + teamList.append(teamLi) + } + + let acceptedDivId = `recap-team-${team}-accepted` + let acceptedDiv = document.getElementById(acceptedDivId) + if (acceptedDiv === null) { + acceptedDiv = document.createElement('div') + acceptedDiv.id = acceptedDivId + acceptedDiv.classList.add('badge', 'rounded-pill', 'text-bg-warning') + acceptedDiv.textContent = `${team} 📃 ?` + teamLi.append(acceptedDiv) + } + + let rejectedDivId = `recap-team-${team}-rejected` + let rejectedDiv = document.getElementById(rejectedDivId) + if (rejectedDiv === null) { + rejectedDiv = document.createElement('div') + rejectedDiv.id = rejectedDivId + rejectedDiv.classList.add('badge', 'rounded-pill', 'text-bg-danger') + rejectedDiv.textContent = '🗑️' + teamLi.append(rejectedDiv) + } + } + } + + // Draw tables + let tablesDiv = document.getElementById(`tables-${tournament.id}`) + let tablesRoundDiv = document.getElementById(`tables-${tournament.id}-round-${round}`) + if (tablesRoundDiv === null) { + let card = document.createElement('div') + card.classList.add('card', 'col-md-6') + tablesDiv.append(card) + + let cardHeader = document.createElement('div') + cardHeader.classList.add('card-header') + cardHeader.innerHTML = `

Tour ${round}

` + card.append(cardHeader) + + tablesRoundDiv = document.createElement('div') + tablesRoundDiv.id = `tables-${tournament.id}-round-${round}` + tablesRoundDiv.classList.add('card-body') + card.append(tablesRoundDiv) + } + + for (let poule of poules) { + let pouleTable = document.getElementById(`table-${tournament.id}-${round}-${poule.letter}`) + if (pouleTable === null) { + // Create table + let card = document.createElement('div') + card.classList.add('card') + tablesRoundDiv.append(card) + + let cardHeader = document.createElement('div') + cardHeader.classList.add('card-header') + cardHeader.innerHTML = `

Poule ${poule.letter}

` + card.append(cardHeader) + + let cardBody = document.createElement('div') + cardBody.classList.add('card-body') + card.append(cardBody) + + pouleTable = document.createElement('table') + pouleTable.id = `table-${tournament.id}-${round}-${poule.letter}` + pouleTable.classList.add('table', 'table-stripped') + cardBody.append(pouleTable) + + let thead = document.createElement('thead') + pouleTable.append(thead) + + let phaseTr = document.createElement('tr') + thead.append(phaseTr) + + 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 <= 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 = `Problème ?` + 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éfenseur⋅se' + + let opponentTd = document.createElement('td') + opponentTd.classList.add('text-center') + opponentTd.innerText = 'Opposant⋅e' + + let reporterTd = document.createElement('td') + reporterTd.classList.add('text-center') + reporterTd.innerText = 'Rapporteur⋅e' + + let emptyTd = 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, emptyTd) + break + case 1: + teamTr.append(emptyTd, defenderTd, reporterTd, emptyTd, opponentTd) + break + case 2: + teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd, reporterTd) + break + case 3: + teamTr.append(reporterTd, opponentTd, emptyTd, defenderTd, emptyTd) + break + case 4: + teamTr.append(emptyTd, reporterTd, emptyTd, opponentTd, defenderTd) + break + } + } + } + } + } + } + } + + function updateActiveRecap(round, pool, team) { + document.querySelectorAll(`li.list-group-item-primary[data-tournament="${tournament.id}"]`) + .forEach(elem => elem.classList.remove('list-group-item-primary')) + document.querySelectorAll(`li.list-group-item-success[data-tournament="${tournament.id}"]`) + .forEach(elem => elem.classList.remove('list-group-item-success')) + document.querySelectorAll(`li.list-group-item-info[data-tournament="${tournament.id}"]`) + .forEach(elem => elem.classList.remove('list-group-item-info')) + + let roundLi = document.getElementById(`recap-${tournament.id}-round-${round}`) + if (roundLi !== null) + roundLi.classList.add('list-group-item-primary') + + let poolLi = document.getElementById(`recap-${tournament.id}-round-${round}-pool-${pool}`) + if (poolLi !== null) + poolLi.classList.add('list-group-item-success') + + let teamLi = document.getElementById(`recap-team-${team}`) + if (teamLi !== null) + teamLi.classList.add('list-group-item-info') + } + socket.addEventListener('message', e => { const data = JSON.parse(e.data) console.log(data) @@ -92,6 +364,7 @@ document.addEventListener('DOMContentLoaded', () => { break case 'notification': showNotification(data.title, data.body) + break case 'set_info': setInfo(data.information) break @@ -104,6 +377,12 @@ document.addEventListener('DOMContentLoaded', () => { case 'dice_visibility': updateDiceVisibility(data.visible) break + case 'set_poules': + updatePoules(data.round, data.poules) + break + case 'set_active': + updateActiveRecap(data.round, data.poule, data.team) + break } }) diff --git a/draw/templates/draw/tournament_content.html b/draw/templates/draw/tournament_content.html index 0f7e6bc..692129d 100644 --- a/draw/templates/draw/tournament_content.html +++ b/draw/templates/draw/tournament_content.html @@ -29,14 +29,14 @@
- {% for participation in tournament.participations.all %} -
-
+
- {{ participation.team.trigram }} 🎲 {{ participation.teamdraw_set.all.first.current.last_dice|default:'??' }} + {{ td.participation.team.trigram }} 🎲 {{ td.last_dice|default:'??' }}
{% endfor %} @@ -50,103 +50,47 @@ Recap
-
    -
  • - {% trans "Round 1" %}: -
      -
    • - Poule A -
        -
      • -
        - MSR 📃 ? -
        -
        - 🗑️ 2, 3 -
        +
          + {% for round in tournament.draw.round_set.all %} +
        • + {{ round }} +
            + {% for pool in round.pool_set.all %} +
          • + {% trans "pool"|capfirst %} {{ pool }} +
              + {% for td in pool.team_draws.all %} +
            • +
              + {{ td.participation.team.trigram }} 📃 {{ td.accepted|default:'?' }} +
              +
              + 🗑️ {{ td.rejected|join:', ' }} +
              + {% if td.penalty %} +
              + ❌ {{ td.penalty }} +
              + {% endif %} +
            • + {% endfor %} +
          • -
          • -
            - PON 📃 4 -
            -
            - 🗑️ 5, 6, 7, 8 -
            -
            - ❌ 0.5 -
            -
          • -
          • -
            - CMS 📃 1 -
            -
            - 🗑️ -
            -
          • -
          -
        • -
        • - Poule B -
            -
          • -
            - ANT 📃 ? -
            -
            - 🗑️ -
            -
          • -
          • -
            - PBM 📃 ? -
            -
            - 🗑️ -
            -
          • -
          • -
            - LHP 📃 ? -
            -
            - 🗑️ -
            -
          • -
          -
        • -
        • - Poule C -
            -
          • -
            - GJS 📃 ? -
            -
            - 🗑️ -
            -
          • -
          • -
            - DAT 📃 ? -
            -
            - 🗑️ -
            -
          • -
          • -
            - LHM 📃 ? -
            -
            - 🗑️ -
            -
          • -
          -
        • -
        -
      • -
      • {% trans "Round 2" %}:
      • + {% endfor %} +
      +
    • + {% endfor %}
@@ -185,95 +129,138 @@
-
-
-
-

- {% trans "Round 1" %} -

-
-
-
-
-

- Poule A -

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
Problem n°?Problem n°?Problem n°?
?????????
?????????
?????????
-
+
+ {% for round in tournament.draw.round_set.all %} +
+
+

+ {{ round }} +

+
+
+ {% for pool in round.pool_set.all %} + {% if pool.teamdraw_set.count %} +
+
+

+ {% trans "pool"|capfirst %} {{ pool }} +

+
+
+ + + + + + + + {% if pool.size == 4 %} + + {% endif %} + + {% if pool.size == 5 %} + + + + + + + + {% endif %} + + {% for td in pool.team_draws.all %} + + {% endfor %} + + + + {% for td in pool.team_draws %} + + + {% if pool.size == 3 %} + {% if forloop.counter == 1 %} + + + + {% elif forloop.counter == 2 %} + + + + {% elif forloop.counter == 3 %} + + + + {% endif %} + {% elif pool.size == 4 %} + {% if forloop.counter == 1 %} + + + + + {% elif forloop.counter == 2 %} + + + + + {% elif forloop.counter == 3 %} + + + + + {% elif forloop.counter == 4 %} + + + + + {% endif %} + {% elif pool.size == 5 %} + {% if forloop.counter == 1 %} + + + + + + {% elif forloop.counter == 2 %} + + + + + + {% elif forloop.counter == 3 %} + + + + + + {% elif forloop.counter == 4 %} + + + + + + {% elif forloop.counter == 4 %} + + + + + + + {% endif %} + {% endif %} + + {% endfor %} + +
{% trans "team"|capfirst %}Phase 1Phase 2Phase 3Phase 4
{% trans "Room" %} 1{% trans "Room" %} 2{% trans "Room" %} 1{% trans "Room" %} 2{% trans "Room" %} 1
+ {% trans "Problem"|capfirst %} + {{ td.accepted|default:"?" }} +
{{ td.participation.team.trigram }}{% trans "defender"|capfirst %}{% trans "reporter"|capfirst %}{% trans "opponent"|capfirst %}{% trans "opponent"|capfirst %}{% trans "defender"|capfirst %}{% trans "reporter"|capfirst %}{% trans "reporter"|capfirst %}{% trans "opponent"|capfirst %}{% trans "defender"|capfirst %}{% trans "defender"|capfirst %}{% trans "reporter"|capfirst %}{% trans "opponent"|capfirst %}{% trans "opponent"|capfirst %}{% trans "defender"|capfirst %}{% trans "reporter"|capfirst %}{% trans "reporter"|capfirst %}{% trans "opponent"|capfirst %}{% trans "defender"|capfirst %}{% trans "reporter"|capfirst %}{% trans "opponent"|capfirst %}{% trans "defender"|capfirst %}{% trans "defender"|capfirst %}{% trans "opponent"|capfirst %}{% trans "reporter"|capfirst %}{% trans "defender"|capfirst %}{% trans "reporter"|capfirst %}{% trans "opponent"|capfirst %}{% trans "opponent"|capfirst %}{% trans "defender"|capfirst %}{% trans "reporter"|capfirst %}{% trans "reporter"|capfirst %}{% trans "opponent"|capfirst %}{% trans "defender"|capfirst %}{% trans "reporter"|capfirst %}{% trans "opponent"|capfirst %}{% trans "defender"|capfirst %}
+
+
+ {% endif %} + {% endfor %}
-
-
-
-

- {% trans "Round 2" %} -

-
-
-
-
-

- Poule A -

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
Problem n°?Problem n°?Problem n°?
?????????
?????????
?????????
-
-
-
-
+ {% endfor %}