diff --git a/draw/admin.py b/draw/admin.py
index ce2362c..4d4b9e5 100644
--- a/draw/admin.py
+++ b/draw/admin.py
@@ -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()
\ No newline at end of file
+ return record.round.get_number_display()
diff --git a/draw/consumers.py b/draw/consumers.py
index a365db7..d6e93e7 100644
--- a/draw/consumers.py
+++ b/draw/consumers.py
@@ -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"
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"
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 += "
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 += "
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 += "
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 += "
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):
diff --git a/draw/models.py b/draw/models.py
index 3fb496c..b15c859 100644
--- a/draw/models.py
+++ b/draw/models.py
@@ -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
diff --git a/draw/views.py b/draw/views.py
index 37ef3fb..6e98e1c 100644
--- a/draw/views.py
+++ b/draw/views.py
@@ -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
diff --git a/participation/forms.py b/participation/forms.py
index 639469e..bc6c67a 100644
--- a/participation/forms.py
+++ b/participation/forms.py
@@ -8,7 +8,7 @@ from typing import Iterable
from crispy_forms.bootstrap import InlineField
from crispy_forms.helper import FormHelper
-from crispy_forms.layout import Submit, Fieldset, Layout, Div
+from crispy_forms.layout import Div, Fieldset, Submit
from django import forms
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
@@ -200,6 +200,7 @@ class PoolTeamsForm(forms.ModelForm):
}),
}
+
class AddJuryForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -242,7 +243,6 @@ class AddJuryForm(forms.ModelForm):
fields = ('first_name', 'last_name', 'email',)
-
class UploadNotesForm(forms.Form):
file = forms.FileField(
label=_("CSV file:"),
diff --git a/participation/models.py b/participation/models.py
index 93031bc..29eec9e 100644
--- a/participation/models.py
+++ b/participation/models.py
@@ -285,7 +285,6 @@ class Tournament(models.Model):
fmt = [n] if n <= 5 else [3] * (n // 3 - 1) + [3 + n % 3]
return '+'.join(map(str, sorted(fmt, reverse=True)))
-
def get_absolute_url(self):
return reverse_lazy("participation:tournament_detail", args=(self.pk,))
diff --git a/participation/views.py b/participation/views.py
index 77ca609..2636dae 100644
--- a/participation/views.py
+++ b/participation/views.py
@@ -718,6 +718,9 @@ class PoolUpdateTeamsView(VolunteerMixin, UpdateView):
class PoolAddJurysView(VolunteerMixin, FormView, DetailView):
+ """
+ This view lets organizers set jurys for a pool, without multiplying clicks.
+ """
model = Pool
form_class = AddJuryForm
template_name = 'participation/pool_add_jurys.html'
@@ -731,21 +734,26 @@ class PoolAddJurysView(VolunteerMixin, FormView, DetailView):
def form_valid(self, form):
self.object = self.get_object()
+ # Save the user object first
form.save()
user = form.instance
+ # Create associated registration object to the new user
reg = VolunteerRegistration.objects.create(
user=user,
professional_activity="Juré⋅e du tournoi " + self.object.tournament.name,
)
+ # Add the user in the jury
self.object.juries.add(reg)
self.object.save()
reg.send_email_validation_link()
+ # Generate new password for the user
password = get_random_string(16)
user.set_password(password)
user.save()
+ # Send welcome mail
subject = "[TFJM²] " + str(_("New TFJM² jury account"))
site = Site.objects.first()
message = render_to_string('registration/mails/add_organizer.txt', dict(user=user,
@@ -758,12 +766,14 @@ class PoolAddJurysView(VolunteerMixin, FormView, DetailView):
domain=site.domain))
user.email_user(subject, message, html_message=html)
- messages.success(self.request, _("The jury {name} has been successfully added!")\
+ # Add notification
+ messages.success(self.request, _("The jury {name} has been successfully added!")
.format(name=f"{user.first_name} {user.last_name}"))
return super().form_valid(form)
def form_invalid(self, form):
+ # This is useful since we have a FormView + a DetailView
self.object = self.get_object()
return super().form_invalid(form)
diff --git a/registration/admin.py b/registration/admin.py
index fe59a83..0ac81ef 100644
--- a/registration/admin.py
+++ b/registration/admin.py
@@ -6,7 +6,7 @@ from django.contrib.admin import ModelAdmin
from django.utils.translation import gettext_lazy as _
from polymorphic.admin import PolymorphicChildModelAdmin, PolymorphicChildModelFilter, PolymorphicParentModelAdmin
-from .models import CoachRegistration, Payment, ParticipantRegistration, Registration, \
+from .models import CoachRegistration, ParticipantRegistration, Payment, Registration, \
StudentRegistration, VolunteerRegistration
@@ -26,6 +26,7 @@ class RegistrationAdmin(PolymorphicParentModelAdmin):
def last_name(self, record):
return record.user.last_name
+
@admin.register(ParticipantRegistration)
class ParticipantRegistrationAdmin(PolymorphicChildModelAdmin):
list_display = ('user', 'first_name', 'last_name', 'type', 'team', 'email_confirmed',)
diff --git a/registration/models.py b/registration/models.py
index 6af1ebc..d4dbea0 100644
--- a/registration/models.py
+++ b/registration/models.py
@@ -191,7 +191,6 @@ class ParticipantRegistration(Registration):
def form_class(self): # pragma: no cover
raise NotImplementedError
-
class Meta:
verbose_name = _("participant registration")
verbose_name_plural = _("participant registrations")
diff --git a/tfjm/asgi.py b/tfjm/asgi.py
index 126040c..3bec7e0 100644
--- a/tfjm/asgi.py
+++ b/tfjm/asgi.py
@@ -21,7 +21,8 @@ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tfjm.settings')
django_asgi_app = get_asgi_application()
-import draw.routing
+# useful since the import must be done after the application initialization
+import draw.routing # noqa: E402, I202
application = ProtocolTypeRouter(
{
diff --git a/tfjm/templates/base.html b/tfjm/templates/base.html
index b260bf6..7bc9212 100644
--- a/tfjm/templates/base.html
+++ b/tfjm/templates/base.html
@@ -173,7 +173,7 @@
{% endif %}