1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-06-21 01:18:22 +02:00

Add comments and linting

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello
2023-04-05 17:52:46 +02:00
parent 2840a15fd5
commit 7e212d011e
14 changed files with 166 additions and 147 deletions

View File

@ -4,7 +4,7 @@
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from .models import Draw, Round, Pool, TeamDraw
from .models import Draw, Pool, Round, TeamDraw
@admin.register(Draw)
@ -59,4 +59,4 @@ class TeamDrawAdmin(admin.ModelAdmin):
@admin.display(ordering='round__number', description=_('round'))
def view_round(self, record):
return record.round.get_number_display()
return record.round.get_number_display()

View File

@ -8,9 +8,8 @@ from channels.generic.websocket import AsyncJsonWebsocketConsumer
from django.conf import settings
from django.utils import translation
from django.utils.translation import gettext_lazy as _
from draw.models import Draw, Round, Pool, TeamDraw
from participation.models import Tournament, Participation
from draw.models import Draw, Pool, Round, TeamDraw
from participation.models import Participation, Tournament
from registration.models import Registration
@ -62,7 +61,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
reg = await Registration.objects.aget(user=user)
self.registration = reg
if reg.is_volunteer and not reg.is_admin and self.tournament not in reg.interesting_tournaments \
or not reg.is_volunteer and reg.team.participation.tournament != self.tournament:
or not reg.is_volunteer and reg.team.participation.tournament != self.tournament:
# This user may not have access to the drawing session
await self.close()
return
@ -148,14 +147,14 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
try:
# Parse format from string
fmt: list[int] = sorted(map(int, fmt.split('+')), reverse=True)
except ValueError as _ignored:
except ValueError:
return await self.alert(_("Invalid format"), 'danger')
# Ensure that the number of teams is good
if sum(fmt) != len(self.participations):
return await self.alert(
_("The sum must be equal to the number of teams: expected {len}, got {sum}")\
.format(len=len(self.participations), sum=sum(fmt)), 'danger')
_("The sum must be equal to the number of teams: expected {len}, got {sum}")
.format(len=len(self.participations), sum=sum(fmt)), 'danger')
# The drawing system works with a maximum of 1 pool of 5 teams, which is already the case in the TFJM²
if fmt.count(5) > 1:
@ -191,9 +190,9 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update user interface
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.start', 'fmt': fmt, 'draw': draw})
{'type': 'draw.start', 'fmt': fmt, 'draw': draw})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': draw})
{'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})
@ -207,7 +206,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
Send information to users that the draw has started.
"""
await self.alert(_("The draw for the tournament {tournament} will start.")\
await self.alert(_("The draw for the tournament {tournament} will start.")
.format(tournament=self.tournament.name), 'warning')
await self.send_json({'type': 'draw_start', 'fmt': content['fmt'],
'trigrams': [p.team.trigram for p in self.participations]})
@ -230,11 +229,10 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
Send information to users that the draw was aborted.
"""
await self.alert(_("The draw for the tournament {tournament} is aborted.")\
await self.alert(_("The draw for the tournament {tournament} is aborted.")
.format(tournament=self.tournament.name), 'danger')
await self.send_json({'type': 'abort'})
async def process_dice(self, trigram: str | None = None, **kwargs):
"""
Launch the dice for a team.
@ -332,13 +330,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Get concerned TeamDraw objects
if state == 'DICE_SELECT_POULES':
tds = [td async for td in TeamDraw.objects.filter(round_id=self.tournament.draw.current_round_id) \
.prefetch_related('participation__team')]
tds = [td async for td in TeamDraw.objects.filter(round_id=self.tournament.draw.current_round_id)
.prefetch_related('participation__team')]
dices = {td: td.passage_dice for td in tds}
else:
tds = [td async for td in TeamDraw.objects\
.filter(pool_id=self.tournament.draw.current_round.current_pool_id)\
.prefetch_related('participation__team')]
tds = [td async for td in TeamDraw.objects
.filter(pool_id=self.tournament.draw.current_round.current_pool_id)
.prefetch_related('participation__team')]
dices = {td: td.choice_dice for td in tds}
values = list(dices.values())
@ -408,8 +406,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# which is this specific pool since they are ordered by decreasing size.
tds_copy = tds.copy()
round2 = await self.tournament.draw.round_set.filter(number=2).aget()
round2_pools = [p async for p in Pool.objects.filter(round__draw__tournament=self.tournament, round=round2) \
.order_by('letter').all()]
round2_pools = [p async for p in Pool.objects.filter(round__draw__tournament=self.tournament, round=round2)
.order_by('letter').all()]
current_pool_id, current_passage_index = 0, 0
for i, td in enumerate(tds_copy):
if i == len(tds) - 1 and round2_pools[0].size == 5:
@ -511,7 +509,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{tds[0].participation.team.trigram}",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
'body': "C'est à vous de tirer un nouveau problème !"})
async def select_problem(self, **kwargs):
"""
@ -566,7 +564,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
self.tournament.draw.last_message = ""
await self.tournament.draw.asave()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw})
{'type': 'draw.set_info', 'draw': self.tournament.draw})
async def accept_problem(self, **kwargs):
"""
@ -636,109 +634,127 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'body': "C'est à vous de tirer un nouveau problème !"})
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:
# Le règlement demande à ce que l'ordre soit tiré au sort
shuffle(tds)
tds[0].passage_index = p_index
tds[1].passage_index = p_index + 1
p_index += 2
await tds[0].asave()
await tds[1].asave()
for pb, tds in problems.items():
if len(tds) == 1:
tds[0].passage_index = p_index
p_index += 1
await tds[0].asave()
await self.end_pool(pool)
# Send the reordered pool
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],
})
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})
msg += f"<br><br>Le tirage de la poule {pool.get_letter_display()}{r.number} est terminé. " \
f"Le tableau récapitulatif est en bas."
async def end_pool(self, pool: Pool) -> None:
"""
End the pool, and pass to the next one, or to the next round, or end the draw.
:param pool: The pool to end.
"""
msg = self.tournament.draw.last_message
r = pool.round
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:
# Le règlement demande à ce que l'ordre soit tiré au sort
shuffle(tds)
tds[0].passage_index = p_index
tds[1].passage_index = p_index + 1
p_index += 2
await tds[0].asave()
await tds[1].asave()
for pb, tds in problems.items():
if len(tds) == 1:
tds[0].passage_index = p_index
p_index += 1
await tds[0].asave()
# Send the reordered pool
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"<br><br>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
await self.tournament.draw.asave()
if await r.teamdraw_set.filter(accepted__isnull=True).aexists():
# There is a pool that does not have selected its problem, so we continue to the next pool
next_pool = await r.next_pool()
r.current_pool = next_pool
await r.asave()
async for td in next_pool.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.dice_visibility', 'visible': True})
# Notify the team that it can draw a dice
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True})
else:
# Round is ended
await self.end_round(r)
async def end_round(self, r: Round) -> None:
"""
End the round, and pass to the next one, or end the draw.
:param r: The current round.
"""
msg = self.tournament.draw.last_message
if r.number == 1 and not self.tournament.final:
# Next round
r2 = await self.tournament.draw.round_set.filter(number=2).aget()
self.tournament.draw.current_round = r2
msg += "<br><br>Le tirage au sort du tour 1 est terminé."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
if await r.teamdraw_set.filter(accepted__isnull=True).aexists():
# There is a pool that does not have selected its problem, so we continue to the next pool
next_pool = await r.next_pool()
r.current_pool = next_pool
await r.asave()
for participation in self.participations:
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{'type': 'draw.dice', 'team': participation.team.trigram, 'result': None})
# Notify the team that it can draw a dice
await self.channel_layer.group_send(f"team-{participation.team.trigram}",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
async for td in next_pool.team_draws.prefetch_related('participation__team').all():
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.dice_visibility', 'visible': True})
# Notify the team that it can draw a dice
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
# Reorder dices
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules',
'round': r2})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
# The passage order for the second round is already determined by the first round
# Start the first pool of the second round
p1: Pool = await r2.pool_set.filter(letter=1).aget()
r2.current_pool = p1
await r2.asave()
async for td in p1.teamdraw_set.prefetch_related('participation__team').all():
await self.channel_layer.group_send(f"team-{td.participation.team.trigram}",
{'type': 'draw.dice_visibility', 'visible': True})
else:
# Round is ended
if r.number == 1 and not self.tournament.final:
# Next round
r2 = await self.tournament.draw.round_set.filter(number=2).aget()
self.tournament.draw.current_round = r2
msg += "<br><br>Le tirage au sort du tour 1 est terminé."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.dice_visibility', 'visible': True})
elif r.number == 1 and self.tournament.final:
# For the final tournament, we wait for a manual update between the two rounds.
msg += "<br><br>Le tirage au sort du tour 1 est terminé."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
for participation in self.participations:
await self.channel_layer.group_send(
f"tournament-{self.tournament.id}",
{'type': 'draw.dice', 'team': participation.team.trigram, 'result': None})
# Notify the team that it can draw a dice
await self.channel_layer.group_send(f"team-{participation.team.trigram}",
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de lancer le dé !"})
# Reorder dices
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules',
'round': r2})
# The passage order for the second round is already determined by the first round
# Start the first pool of the second round
p1: Pool = await r2.pool_set.filter(letter=1).aget()
r2.current_pool = p1
await r2.asave()
async for td in p1.teamdraw_set.prefetch_related('participation__team').all():
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})
elif r.number == 1 and self.tournament.final:
# For the final tournament, we wait for a manual update between the two rounds.
msg += "<br><br>Le tirage au sort du tour 1 est terminé."
self.tournament.draw.last_message = msg
await self.tournament.draw.asave()
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.export_visibility', 'visible': True})
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})
await self.channel_layer.group_send(f"volunteer-{self.tournament.id}",
{'type': 'draw.export_visibility', 'visible': True})
async def reject_problem(self, **kwargs):
"""
@ -813,7 +829,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
{'type': 'draw.box_visibility', 'visible': True})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw})
{'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})
@ -822,7 +838,6 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
{'type': 'draw.notify', 'title': "À votre tour !",
'body': "C'est à vous de tirer un nouveau problème !"})
@ensure_orga
async def export(self, **kwargs):
"""
@ -867,8 +882,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
notes = dict()
async for participation in self.tournament.participations.filter(valid=True).prefetch_related('team').all():
notes[participation] = sum([await pool.aaverage(participation)
async for pool in self.tournament.pools.filter(participations=participation)\
.prefetch_related('passages').prefetch_related('tweaks')
async for pool in self.tournament.pools.filter(participations=participation)
.prefetch_related('passages').prefetch_related('tweaks')
if pool.results_available])
# Sort notes in a decreasing order
ordered_participations = sorted(notes.keys(), key=lambda x: -notes[x])
@ -906,7 +921,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
{'type': 'draw.continue_visibility', 'visible': False})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw})
{'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})
@ -981,8 +996,8 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'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,
'team': r.current_pool.current_team.participation.team.trigram
if r.current_pool and r.current_pool.current_team else None,
})
async def draw_set_problem(self, content):

View File

@ -3,14 +3,13 @@
from asgiref.sync import sync_to_async
from django.conf import settings
from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import QuerySet
from django.urls import reverse_lazy
from django.utils.text import format_lazy, slugify
from django.utils.translation import gettext_lazy as _
from participation.models import Passage, Participation, Pool as PPool, Tournament
from participation.models import Participation, Passage, Pool as PPool, Tournament
class Draw(models.Model):
@ -292,16 +291,16 @@ class Pool(models.Model):
Returns a list of trigrams of the teams in this pool ordered by passage index.
This property is synchronous.
"""
return [td.participation.team.trigram for td in self.teamdraw_set.order_by('passage_index')\
.prefetch_related('participation__team').all()]
return [td.participation.team.trigram for td in self.teamdraw_set.order_by('passage_index')
.prefetch_related('participation__team').all()]
async def atrigrams(self) -> list[str]:
"""
Returns a list of trigrams of the teams in this pool ordered by passage index.
This property is asynchronous.
"""
return [td.participation.team.trigram async for td in self.teamdraw_set.order_by('passage_index')\
.prefetch_related('participation__team').all()]
return [td.participation.team.trigram async for td in self.teamdraw_set.order_by('passage_index')
.prefetch_related('participation__team').all()]
async def next_td(self) -> "TeamDraw":
"""
@ -349,8 +348,8 @@ class Pool(models.Model):
# Define the participations of the pool
tds = [td async for td in self.team_draws.prefetch_related('participation')]
await self.associated_pool.participations.aset([td.participation async for td in self.team_draws\
.prefetch_related('participation')])
await self.associated_pool.participations.aset([td.participation async for td in self.team_draws
.prefetch_related('participation')])
await self.asave()
# Define the passage matrix according to the number of teams

View File

@ -3,8 +3,7 @@
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView, DetailView
from django.views.generic import TemplateView
from participation.models import Tournament
@ -36,5 +35,4 @@ class DisplayView(LoginRequiredMixin, TemplateView):
context['tournaments_simplified'] = [{'id': t.id, 'name': t.name} for t in tournaments]
context['problems'] = settings.PROBLEMS
return context