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 @@
-
-
-
-
-
-
-
-
-
-
- Problem n°? |
- Problem n°? |
- Problem n°? |
-
-
-
-
- ??? |
- ??? |
- ??? |
-
-
- ??? |
- ??? |
- ??? |
-
-
- ??? |
- ??? |
- ??? |
-
-
-
-
+
+ {% for round in tournament.draw.round_set.all %}
+
+
+
+ {% for pool in round.pool_set.all %}
+ {% if pool.teamdraw_set.count %}
+
+
+
+
+
+
+ {% trans "team"|capfirst %} |
+ Phase 1 |
+ Phase 2 |
+ Phase 3 |
+ {% if pool.size == 4 %}
+ Phase 4 |
+ {% endif %}
+
+ {% if pool.size == 5 %}
+
+ {% trans "Room" %} 1 |
+ {% trans "Room" %} 2 |
+ {% trans "Room" %} 1 |
+ {% trans "Room" %} 2 |
+ {% trans "Room" %} 1 |
+
+ {% endif %}
+
+ {% for td in pool.team_draws.all %}
+
+ {% trans "Problem"|capfirst %}
+ {{ td.accepted|default:"?" }}
+ |
+ {% endfor %}
+
+
+
+ {% for td in pool.team_draws %}
+
+ {{ td.participation.team.trigram }} |
+ {% if pool.size == 3 %}
+ {% if forloop.counter == 1 %}
+ {% trans "defender"|capfirst %} |
+ {% trans "reporter"|capfirst %} |
+ {% trans "opponent"|capfirst %} |
+ {% elif forloop.counter == 2 %}
+ {% trans "opponent"|capfirst %} |
+ {% trans "defender"|capfirst %} |
+ {% trans "reporter"|capfirst %} |
+ {% elif forloop.counter == 3 %}
+ {% trans "reporter"|capfirst %} |
+ {% trans "opponent"|capfirst %} |
+ {% trans "defender"|capfirst %} |
+ {% endif %}
+ {% elif pool.size == 4 %}
+ {% if forloop.counter == 1 %}
+ {% trans "defender"|capfirst %} |
+ |
+ {% trans "reporter"|capfirst %} |
+ {% trans "opponent"|capfirst %} |
+ {% elif forloop.counter == 2 %}
+ {% trans "opponent"|capfirst %} |
+ {% trans "defender"|capfirst %} |
+ |
+ {% trans "reporter"|capfirst %} |
+ {% elif forloop.counter == 3 %}
+ {% trans "reporter"|capfirst %} |
+ {% trans "opponent"|capfirst %} |
+ {% trans "defender"|capfirst %} |
+ |
+ {% elif forloop.counter == 4 %}
+ |
+ {% trans "reporter"|capfirst %} |
+ {% trans "opponent"|capfirst %} |
+ {% trans "defender"|capfirst %} |
+ {% endif %}
+ {% elif pool.size == 5 %}
+ {% if forloop.counter == 1 %}
+ {% trans "defender"|capfirst %} |
+ |
+ {% trans "opponent"|capfirst %} |
+ {% trans "reporter"|capfirst %} |
+ |
+ {% elif forloop.counter == 2 %}
+ |
+ {% trans "defender"|capfirst %} |
+ {% trans "reporter"|capfirst %} |
+ |
+ {% trans "opponent"|capfirst %} |
+ {% elif forloop.counter == 3 %}
+ {% trans "opponent"|capfirst %} |
+ |
+ {% trans "defender"|capfirst %} |
+ |
+ {% trans "reporter"|capfirst %} |
+ {% elif forloop.counter == 4 %}
+ {% trans "reporter"|capfirst %} |
+ {% trans "opponent"|capfirst %} |
+ |
+ {% trans "defender"|capfirst %} |
+ |
+ {% elif forloop.counter == 4 %}
+ |
+ {% trans "reporter"|capfirst %} |
+ |
+ {% trans "opponent"|capfirst %} |
+ {% trans "defender"|capfirst %} |
+ |
+ {% endif %}
+ {% endif %}
+
+ {% endfor %}
+
+
+
+
+ {% endif %}
+ {% endfor %}
-
-
-
-
-
-
-
-
-
-
- Problem n°? |
- Problem n°? |
- Problem n°? |
-
-
-
-
- ??? |
- ??? |
- ??? |
-
-
- ??? |
- ??? |
- ??? |
-
-
- ??? |
- ??? |
- ??? |
-
-
-
-
-
-
-
+ {% endfor %}