Add Redis Channel Layer for the drawing system

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2023-04-11 23:05:58 +02:00
parent 0e7a275a28
commit 8245ba0063
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
5 changed files with 121 additions and 33 deletions

View File

@ -176,7 +176,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await TeamDraw.objects.acreate(participation=participation, round=r)
# Send to clients the different pools
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', 'round': r})
{'type': 'draw.send_poules',
'round': r.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r.pool_set.order_by('letter').all()
]})
draw.current_round = r1
await draw.asave()
@ -191,9 +199,10 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'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',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
{'type': 'draw.set_active', 'round': 1})
# Send notification to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
@ -457,25 +466,44 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
{'type': 'draw.dice_visibility', 'visible': True})
# First send the second pool to have the good team order
r2 = await self.tournament.draw.round_set.filter(number=2).aget()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules',
'round': await self.tournament.draw.round_set.filter(number=2).aget()})
'round': r2.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r2.pool_set.order_by('letter').all()
]})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules',
'round': self.tournament.draw.current_round})
'round': r.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r.pool_set.order_by('letter').all()
]})
# Update information header and the active team on the recap menu
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw})
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
{'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display()})
async def process_dice_order_poule(self):
"""
Called when all teams of the current launched their dice to determine the choice order.
Place teams into pools and order their passage.
"""
pool = self.tournament.draw.current_round.current_pool
r = self.tournament.draw.current_round
pool = r.current_pool
tds = [td async for td in TeamDraw.objects.filter(pool=pool).prefetch_related('participation__team')]
# Order teams by decreasing dice score
@ -493,9 +521,13 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Update information header
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw})
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
{'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display(),
'team': pool.current_team.participation.team.trigram})
# Hide dice button to everyone
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
@ -566,7 +598,8 @@ 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',
'info': await self.tournament.draw.ainformation()})
async def accept_problem(self, **kwargs):
"""
@ -637,11 +670,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
else:
# Pool is ended
await self.end_pool(pool)
r = self.tournament.draw.current_round
pool = r.current_pool
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw})
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
{'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display(),
'team': pool.current_team.participation.team.trigram
if pool.current_team else None})
async def end_pool(self, pool: Pool) -> None:
"""
@ -736,7 +776,14 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Reorder dices
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules',
'round': r2})
'round': r2.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r2.pool_set.order_by('letter').all()
]})
# The passage order for the second round is already determined by the first round
# Start the first pool of the second round
@ -831,9 +878,13 @@ 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',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
{'type': 'draw.set_active',
'round': r.number,
'pool': pool.get_letter_display(),
'team': new_trigram})
# Notify the team that it can draw a problem
await self.channel_layer.group_send(f"team-{new_trigram}",
@ -900,7 +951,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
# Send pools to users
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', 'round': r2})
{'type': 'draw.send_poules',
'round': r2.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r2.pool_set.order_by('letter').all()
]})
# Reset dices and update interface
for participation in self.participations:
@ -923,9 +982,12 @@ 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',
'info': await self.tournament.draw.ainformation()})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
{'type': 'draw.set_active',
'round': r2.number,
'pool': r2.current_pool.get_letter_display()})
@ensure_orga
async def cancel_last_step(self, **kwargs):
@ -952,9 +1014,16 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
await self.undo_order_dice()
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_info', 'draw': self.tournament.draw})
{'type': 'draw.set_info',
'info': await self.tournament.draw.ainformation()})
r = self.tournament.draw.current_round
p = r.current_pool
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.set_active', 'draw': self.tournament.draw})
{'type': 'draw.set_active',
'round': r.number,
'pool': p.get_letter_display() if p else None,
'team': p.current_team.participation.team.trigram
if p and p.current_team else None})
async def undo_end_draw(self) -> None:
"""
@ -1194,7 +1263,15 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
'result': td.choice_dice})
await self.channel_layer.group_send(f"tournament-{self.tournament.id}",
{'type': 'draw.send_poules', 'round': r1})
{'type': 'draw.send_poules',
'round': r1.number,
'poules': [
{
'letter': pool.get_letter_display(),
'teams': await pool.atrigrams(),
}
async for pool in r1.pool_set.order_by('letter').all()
]})
previous_pool = r1.current_pool
@ -1339,7 +1416,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
Set the information banner to the current user.
"""
await self.send_json({'type': 'set_info', 'information': await content['draw'].ainformation()})
await self.send_json({'type': 'set_info', 'information': content['info']})
async def draw_dice(self, content):
"""
@ -1381,21 +1458,18 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
"""
Send the pools and the teams to the current user to update the interface.
"""
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()]})
await self.send_json({'type': 'set_poules', 'round': content['round'],
'poules': content['poules']})
async def draw_set_active(self, content):
"""
Update the user interface to highlight the current team.
"""
r = content['draw'].current_round
await self.send_json({
'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,
'round': content.get('round', None),
'poule': content.get('pool', None),
'team': content.get('team', None),
})
async def draw_set_problem(self, content):

View File

@ -24,7 +24,7 @@ from django.utils.crypto import get_random_string
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, FormView, RedirectView, TemplateView, UpdateView, View
from django.views.generic.edit import FormMixin, ProcessFormView
from django_tables2 import SingleTableView, MultiTableMixin
from django_tables2 import MultiTableMixin, SingleTableView
from magic import Magic
from odf.opendocument import OpenDocumentSpreadsheet
from odf.style import Style, TableCellProperties, TableColumnProperties, TextProperties

View File

@ -1,4 +1,5 @@
channels[daphne]~=4.0.0
channels-redis~=4.0.0
crispy-bootstrap5~=0.7
Django>=4.1,<5.0
django-cas-server~=2.0

View File

@ -79,6 +79,11 @@ if "test" not in sys.argv: # pragma: no cover
'mailer',
]
if os.getenv("TFJM_STAGE", "dev") == "prod": # pragma: no cover
INSTALLED_APPS += [
'channels_redis',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
@ -268,7 +273,6 @@ FORBIDDEN_TRIGRAMS = [
"SEX",
]
# TODO: Use a redis server in production
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"

View File

@ -30,3 +30,12 @@ CSRF_COOKIE_SECURE = False
CSRF_COOKIE_HTTPONLY = False
X_FRAME_OPTIONS = 'DENY'
SESSION_COOKIE_AGE = 60 * 60 * 3
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [(os.getenv('REDIS_SERVER_HOST', 'localhost'), os.getenv('REDIS_SERVER_PORT', 6379))],
},
},
}