From 965ba074b604b0e93c535601811be0cbb8745c7a Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 16 Jun 2022 15:22:07 +0200 Subject: [PATCH 01/40] Fix vagrant setup w/ daphne --- provisioning/systemd/daphne.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioning/systemd/daphne.service b/provisioning/systemd/daphne.service index 31b31c16..517c86c9 100644 --- a/provisioning/systemd/daphne.service +++ b/provisioning/systemd/daphne.service @@ -11,7 +11,7 @@ WorkingDirectory=/vagrant Environment="DJANGO_SETTINGS_MODULE=gestioasso.settings.dev" ExecStart=/home/vagrant/venv/bin/daphne \ -u /srv/gestiocof/gestiocof.sock \ - cof.asgi:channel_layer + gestioasso.asgi:channel_layer [Install] WantedBy=multi-user.target -- 2.45.1 From f026e0b5e3e91ae0e721ceea619a158deea8ef41 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Mon, 27 Jun 2022 15:34:24 +0200 Subject: [PATCH 02/40] Update to django channels 2 --- gestioasso/asgi.py | 13 +++++-- gestioasso/routing.py | 19 ++++++++-- gestioasso/settings/cof_prod.py | 6 ++-- gestioasso/settings/local.py | 3 +- kfet/consumers.py | 3 ++ kfet/open/consumers.py | 18 +++++----- kfet/open/open.py | 14 ++++---- kfet/open/routing.py | 11 ++++-- kfet/open/views.py | 5 +-- kfet/routing.py | 17 +++++---- kfet/utils.py | 61 +++++++++++++++++---------------- kfet/views.py | 28 +++++++++++---- requirements-prod.txt | 2 +- requirements.txt | 2 +- 14 files changed, 129 insertions(+), 73 deletions(-) diff --git a/gestioasso/asgi.py b/gestioasso/asgi.py index 773acaa0..728a3433 100644 --- a/gestioasso/asgi.py +++ b/gestioasso/asgi.py @@ -1,8 +1,15 @@ +""" +ASGI entrypoint. Configures Django and then runs the application +defined in the ASGI_APPLICATION setting. +""" + import os -from channels.asgi import get_channel_layer +import django +from channels.routing import get_default_application if "DJANGO_SETTINGS_MODULE" not in os.environ: - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings.local") -channel_layer = get_channel_layer() +django.setup() +application = get_default_application() diff --git a/gestioasso/routing.py b/gestioasso/routing.py index 3c2e5718..c293a9cb 100644 --- a/gestioasso/routing.py +++ b/gestioasso/routing.py @@ -1,3 +1,18 @@ -from channels.routing import include +from channels.auth import AuthMiddlewareStack +from channels.routing import ProtocolTypeRouter, URLRouter +from django.urls import path -routing = [include("kfet.routing.routing", path=r"^/ws/k-fet")] +from kfet.routing import KFRouter + +application = ProtocolTypeRouter( + { + # WebSocket chat handler + "websocket": AuthMiddlewareStack( + URLRouter( + [ + path("ws/k-fet", KFRouter), + ] + ) + ), + } +) diff --git a/gestioasso/settings/cof_prod.py b/gestioasso/settings/cof_prod.py index ebfef337..c239ea0c 100644 --- a/gestioasso/settings/cof_prod.py +++ b/gestioasso/settings/cof_prod.py @@ -109,6 +109,8 @@ MEDIA_URL = "/gestion/media/" CORS_ORIGIN_WHITELIST = ("bda.ens.fr", "www.bda.ens.fr" "cof.ens.fr", "www.cof.ens.fr") +ASGI_APPLICATION = "gestioasso.routing.application" + # --- # Auth-related stuff # --- @@ -147,7 +149,7 @@ CACHES = { CHANNEL_LAYERS = { "default": { - "BACKEND": "asgi_redis.RedisChannelLayer", + "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [ ( @@ -160,11 +162,9 @@ CHANNEL_LAYERS = { ) ] }, - "ROUTING": "gestioasso.routing.routing", } } - # --- # reCAPTCHA settings # https://github.com/praekelt/django-recaptcha diff --git a/gestioasso/settings/local.py b/gestioasso/settings/local.py index 5c8c2734..2cba6a10 100644 --- a/gestioasso/settings/local.py +++ b/gestioasso/settings/local.py @@ -47,8 +47,7 @@ CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache" # Use the default in memory asgi backend for local development CHANNEL_LAYERS = { "default": { - "BACKEND": "asgiref.inmemory.ChannelLayer", - "ROUTING": "gestioasso.routing.routing", + "BACKEND": "channels.layers.InMemoryChannelLayer", } } diff --git a/kfet/consumers.py b/kfet/consumers.py index 2655c86b..e8334421 100644 --- a/kfet/consumers.py +++ b/kfet/consumers.py @@ -4,3 +4,6 @@ from .utils import DjangoJsonWebsocketConsumer, PermConsumerMixin class KPsul(PermConsumerMixin, DjangoJsonWebsocketConsumer): groups = ["kfet.kpsul"] perms_connect = ["kfet.is_team"] + + async def kpsul(self, event): + await self.send_json(event) diff --git a/kfet/open/consumers.py b/kfet/open/consumers.py index 8b800c76..1b190212 100644 --- a/kfet/open/consumers.py +++ b/kfet/open/consumers.py @@ -12,13 +12,15 @@ class OpenKfetConsumer(PermConsumerMixin, DjangoJsonWebsocketConsumer): """ - def connection_groups(self, user, **kwargs): - """Select which group the user should be connected.""" - if kfet_is_team(user): - return ["kfet.open.team"] - return ["kfet.open.base"] + async def open_status(self, event): + await self.send_json(event) - def connect(self, message, *args, **kwargs): + async def connect(self): """Send current status on connect.""" - super().connect(message, *args, **kwargs) - self.send(kfet_open.export(message.user)) + await super().connect() + + group = "team" if kfet_is_team(self.user) else "base" + + await self.channel_layer.group_add(f"kfet.open.{group}", self.channel_name) + + await self.send_json(kfet_open.export(self.user)) diff --git a/kfet/open/open.py b/kfet/open/open.py index d0e0c901..ec391de6 100644 --- a/kfet/open/open.py +++ b/kfet/open/open.py @@ -1,5 +1,6 @@ from datetime import timedelta +from channels.layers import get_channel_layer from django.utils import timezone from ..decorators import kfet_is_team @@ -77,7 +78,7 @@ class OpenKfet(CachedMixin, object): """ status = self.status() - base = {"status": status} + base = {"status": status, "type": "open.status"} restrict = { "admin_status": self.admin_status(status), "force_close": self.force_close, @@ -95,13 +96,14 @@ class OpenKfet(CachedMixin, object): base, team = self._export() return team if kfet_is_team(user) else base - def send_ws(self): + async def send_ws(self): """Send internal state to websocket channels.""" - from .consumers import OpenKfetConsumer - base, team = self._export() - OpenKfetConsumer.group_send("kfet.open.base", base) - OpenKfetConsumer.group_send("kfet.open.team", team) + + channel_layer = get_channel_layer() + + await channel_layer.group_send("kfet.open.base", base) + await channel_layer.group_send("kfet.open.team", team) kfet_open = OpenKfet() diff --git a/kfet/open/routing.py b/kfet/open/routing.py index 811ae56e..6b10b4ec 100644 --- a/kfet/open/routing.py +++ b/kfet/open/routing.py @@ -1,5 +1,10 @@ -from channels.routing import route_class +from channels.routing import URLRouter +from django.urls import path -from . import consumers +from .consumers import OpenKfetConsumer -routing = [route_class(consumers.OpenKfetConsumer)] +OpenRouter = URLRouter( + [ + path(r"", OpenKfetConsumer), + ] +) diff --git a/kfet/open/views.py b/kfet/open/views.py index 49b91f4a..e5b09fb9 100644 --- a/kfet/open/views.py +++ b/kfet/open/views.py @@ -1,3 +1,4 @@ +from asgiref.sync import async_to_sync from django.conf import settings from django.contrib.auth.decorators import permission_required from django.core.exceptions import PermissionDenied @@ -18,7 +19,7 @@ def raw_open(request): raise PermissionDenied raw_open = request.POST.get("raw_open") in TRUE_STR kfet_open.raw_open = raw_open - kfet_open.send_ws() + async_to_sync(kfet_open.send_ws)() return HttpResponse() @@ -27,5 +28,5 @@ def raw_open(request): def force_close(request): force_close = request.POST.get("force_close") in TRUE_STR kfet_open.force_close = force_close - kfet_open.send_ws() + async_to_sync(kfet_open.send_ws)() return HttpResponse() diff --git a/kfet/routing.py b/kfet/routing.py index ceafca06..492f6e4f 100644 --- a/kfet/routing.py +++ b/kfet/routing.py @@ -1,8 +1,13 @@ -from channels.routing import include, route_class +from channels.routing import URLRouter +from django.urls import path -from . import consumers +from kfet.open.routing import OpenRouter -routing = [ - route_class(consumers.KPsul, path=r"^/k-psul/$"), - include("kfet.open.routing.routing", path=r"^/open"), -] +from .consumers import KPsul + +KFRouter = URLRouter( + [ + path("k-psul/", KPsul), + path("open", OpenRouter), + ] +) diff --git a/kfet/utils.py b/kfet/utils.py index 0c4f170a..b1facf1e 100644 --- a/kfet/utils.py +++ b/kfet/utils.py @@ -1,8 +1,7 @@ import json import math -from channels.channel import Group -from channels.generic.websockets import JsonWebsocketConsumer +from channels.generic.websocket import AsyncJsonWebsocketConsumer from django.core.cache import cache from django.core.serializers.json import DjangoJSONEncoder @@ -63,7 +62,7 @@ class CachedMixin: # Consumers -class DjangoJsonWebsocketConsumer(JsonWebsocketConsumer): +class DjangoJsonWebsocketConsumer(AsyncJsonWebsocketConsumer): """Custom Json Websocket Consumer. Encode to JSON with DjangoJSONEncoder. @@ -71,7 +70,7 @@ class DjangoJsonWebsocketConsumer(JsonWebsocketConsumer): """ @classmethod - def encode_json(cls, content): + async def encode_json(cls, content): return json.dumps(content, cls=DjangoJSONEncoder) @@ -89,31 +88,35 @@ class PermConsumerMixin: http_user = True # Enable message.user perms_connect = [] - def connect(self, message, **kwargs): + async def connect(self): """Check permissions on connection.""" - if message.user.has_perms(self.perms_connect): - super().connect(message, **kwargs) + self.user = self.scope["user"] + + if self.user.has_perms(self.perms_connect): + await super().connect() else: - self.close() + await self.close() - def raw_connect(self, message, **kwargs): - # Same as original raw_connect method of JsonWebsocketConsumer - # We add user to connection_groups call. - groups = self.connection_groups(user=message.user, **kwargs) - for group in groups: - Group(group, channel_layer=message.channel_layer).add(message.reply_channel) - self.connect(message, **kwargs) - - def raw_disconnect(self, message, **kwargs): - # Same as original raw_connect method of JsonWebsocketConsumer - # We add user to connection_groups call. - groups = self.connection_groups(user=message.user, **kwargs) - for group in groups: - Group(group, channel_layer=message.channel_layer).discard( - message.reply_channel - ) - self.disconnect(message, **kwargs) - - def connection_groups(self, user, **kwargs): - """`message.user` is available as `user` arg. Original behavior.""" - return super().connection_groups(user=user, **kwargs) + # async def raw_connect(self, message, **kwargs): + # # Same as original raw_connect method of JsonWebsocketConsumer + # # We add user to connection_groups call. + # groups = self.connection_groups(user=message.user, **kwargs) + # for group in groups: + # await self.channel_layer.group_add(group, message.reply_channel) + # # Group(group, channel_layer=message.channel_layer).add(message.reply_channel) + # self.connect(message, **kwargs) + # + # async def raw_disconnect(self, message, **kwargs): + # # Same as original raw_connect method of JsonWebsocketConsumer + # # We add user to connection_groups call. + # groups = self.connection_groups(user=message.user, **kwargs) + # for group in groups: + # await self.channel_layer.group_discard(group, message.reply_channel) + # # Group(group, channel_layer=message.channel_layer).discard( + # # message.reply_channel + # # ) + # self.disconnect(message, **kwargs) + # + # def connection_groups(self, user, **kwargs): + # """`message.user` is available as `user` arg. Original behavior.""" + # return super().connection_groups(user=user, **kwargs) diff --git a/kfet/views.py b/kfet/views.py index 569e42a5..b0711a6b 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -6,6 +6,8 @@ from decimal import Decimal from typing import List, Tuple from urllib.parse import urlencode +from asgiref.sync import async_to_sync +from channels.layers import get_channel_layer from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required @@ -31,7 +33,7 @@ from django.views.generic.detail import BaseDetailView from django.views.generic.edit import CreateView, DeleteView, UpdateView from gestioncof.models import CofProfile -from kfet import KFET_DELETED_TRIGRAMME, consumers +from kfet import KFET_DELETED_TRIGRAMME from kfet.auth.decorators import kfet_password_auth from kfet.autocomplete import kfet_account_only_autocomplete, kfet_autocomplete from kfet.config import kfet_config @@ -984,8 +986,14 @@ def kpsul_update_addcost(request): kfet_config.set(addcost_for=account, addcost_amount=amount) - data = {"addcost": {"for": account and account.trigramme or None, "amount": amount}} - consumers.KPsul.group_send("kfet.kpsul", data) + data = { + "addcost": {"for": account and account.trigramme or None, "amount": amount}, + "type": "kpsul", + } + + channel_layer = get_channel_layer() + + async_to_sync(channel_layer.group_send)("kfet.kpsul", data) return JsonResponse(data) @@ -1169,7 +1177,7 @@ def kpsul_perform_operations(request): ) # Websocket data - websocket_data = {} + websocket_data = {"type": "kpsul"} websocket_data["groups"] = [ { "add": True, @@ -1216,7 +1224,10 @@ def kpsul_perform_operations(request): websocket_data["articles"].append( {"id": article["id"], "stock": article["stock"]} ) - consumers.KPsul.group_send("kfet.kpsul", websocket_data) + + channel_layer = get_channel_layer() + + async_to_sync(channel_layer.group_send)("kfet.kpsul", websocket_data) return JsonResponse(data) @@ -1411,7 +1422,7 @@ def cancel_operations(request): articles = Article.objects.values("id", "stock").filter(pk__in=articles_pk) # Websocket data - websocket_data = {"checkouts": [], "articles": []} + websocket_data = {"checkouts": [], "articles": [], "type": "kpsul"} for checkout in checkouts: websocket_data["checkouts"].append( {"id": checkout["id"], "balance": checkout["balance"]} @@ -1420,7 +1431,10 @@ def cancel_operations(request): websocket_data["articles"].append( {"id": article["id"], "stock": article["stock"]} ) - consumers.KPsul.group_send("kfet.kpsul", websocket_data) + + channel_layer = get_channel_layer() + + async_to_sync(channel_layer.group_send)("kfet.kpsul", websocket_data) data["canceled"] = list(opes) data["opegroups_to_update"] = list(opegroups) diff --git a/requirements-prod.txt b/requirements-prod.txt index 6d6fd334..fc7ed097 100644 --- a/requirements-prod.txt +++ b/requirements-prod.txt @@ -6,7 +6,7 @@ psycopg2<2.8 # Redis django-redis-cache==2.1.* redis~=2.10.6 -asgi-redis==1.4.* +channels-redis==2.4.* # ASGI protocol and HTTP server asgiref~=1.1.2 diff --git a/requirements.txt b/requirements.txt index 8baaa5ed..d2e0c525 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ Pillow django-bootstrap-form==3.3 statistics==1.0.3.5 django-widget-tweaks==1.4.1 -channels==1.1.* +channels==2.4.* python-dateutil wagtail==2.7.* wagtailmenus==3.* -- 2.45.1 From 654848c4f0ae54ca5cf372e1fc4382134a670794 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Mon, 27 Jun 2022 15:42:44 +0200 Subject: [PATCH 03/40] Update daphne version --- requirements-prod.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements-prod.txt b/requirements-prod.txt index fc7ed097..bd50ba77 100644 --- a/requirements-prod.txt +++ b/requirements-prod.txt @@ -9,8 +9,7 @@ redis~=2.10.6 channels-redis==2.4.* # ASGI protocol and HTTP server -asgiref~=1.1.2 -daphne==1.3.0 +daphne==2.5.* # ldap bindings python-ldap -- 2.45.1 From e1d942a7ca1be49cef5579cbc2a3e498c1f973ef Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 28 Jun 2022 15:58:46 +0200 Subject: [PATCH 04/40] Add default http router --- gestioasso/routing.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gestioasso/routing.py b/gestioasso/routing.py index c293a9cb..f329fdcd 100644 --- a/gestioasso/routing.py +++ b/gestioasso/routing.py @@ -1,4 +1,5 @@ from channels.auth import AuthMiddlewareStack +from channels.http import AsgiHandler from channels.routing import ProtocolTypeRouter, URLRouter from django.urls import path @@ -14,5 +15,6 @@ application = ProtocolTypeRouter( ] ) ), + "http": AsgiHandler, } ) -- 2.45.1 From d120a2bb62f30d3f99bb6dd329dcde98275be231 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 28 Jun 2022 15:59:05 +0200 Subject: [PATCH 05/40] Update base consumers --- kfet/utils.py | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/kfet/utils.py b/kfet/utils.py index b1facf1e..540f260c 100644 --- a/kfet/utils.py +++ b/kfet/utils.py @@ -71,6 +71,9 @@ class DjangoJsonWebsocketConsumer(AsyncJsonWebsocketConsumer): @classmethod async def encode_json(cls, content): + # Remove the type value, only used by Channels to choose the group to send to + content.pop("type") + return json.dumps(content, cls=DjangoJSONEncoder) @@ -96,27 +99,3 @@ class PermConsumerMixin: await super().connect() else: await self.close() - - # async def raw_connect(self, message, **kwargs): - # # Same as original raw_connect method of JsonWebsocketConsumer - # # We add user to connection_groups call. - # groups = self.connection_groups(user=message.user, **kwargs) - # for group in groups: - # await self.channel_layer.group_add(group, message.reply_channel) - # # Group(group, channel_layer=message.channel_layer).add(message.reply_channel) - # self.connect(message, **kwargs) - # - # async def raw_disconnect(self, message, **kwargs): - # # Same as original raw_connect method of JsonWebsocketConsumer - # # We add user to connection_groups call. - # groups = self.connection_groups(user=message.user, **kwargs) - # for group in groups: - # await self.channel_layer.group_discard(group, message.reply_channel) - # # Group(group, channel_layer=message.channel_layer).discard( - # # message.reply_channel - # # ) - # self.disconnect(message, **kwargs) - # - # def connection_groups(self, user, **kwargs): - # """`message.user` is available as `user` arg. Original behavior.""" - # return super().connection_groups(user=user, **kwargs) -- 2.45.1 From 6db3ed604bdfeabe6dbbee08a56bfbc497a4297b Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 28 Jun 2022 22:01:04 +0200 Subject: [PATCH 06/40] Update django version --- bda/migrations/0019_auto_20220628_1621.py | 76 ++++++++++ bda/templates/bda-attrib.html | 2 +- bda/templates/bda/etat-places.html | 2 +- bda/templates/bda/inscription-tirage.html | 2 +- bda/templates/bda/participants.html | 2 +- .../bda/revente/confirm-shotgun.html | 2 +- bda/templates/bda/revente/confirmed.html | 2 +- bda/templates/bda/revente/mail-success.html | 2 +- bda/templates/bda/revente/manage.html | 2 +- bda/templates/bda/revente/subscribe.html | 2 +- bda/templates/bda/revente/tirages.html | 2 +- bda/templates/spectacle_list.html | 2 +- bds/migrations/0007_alter_bdsprofile_id.py | 20 +++ bds/templates/bds/base.html | 2 +- clubs/migrations/0002_alter_club_id.py | 20 +++ events/migrations/0005_auto_20220628_1621.py | 55 +++++++ gestioasso/settings/cof_prod.py | 1 - gestioasso/settings/common.py | 1 + .../migrations/0019_auto_20220628_1621.py | 139 ++++++++++++++++++ gestioncof/templates/base.html | 2 +- gestioncof/templates/registration.html | 2 +- gestioncof/templates/tristate_js.html | 2 +- .../0004_alter_genericteamtoken_id.py | 20 +++ kfet/cms/hooks.py | 2 +- .../migrations/0003_alter_memberteam_id.py | 20 +++ kfet/migrations/0081_auto_20220628_1621.py | 125 ++++++++++++++++ kfet/templates/kfet/account_create.html | 2 +- .../kfet/account_create_special.html | 2 +- kfet/templates/kfet/account_group_form.html | 2 +- kfet/templates/kfet/account_read.html | 2 +- kfet/templates/kfet/article_read.html | 2 +- kfet/templates/kfet/history.html | 2 +- kfet/templates/kfet/home.html | 2 +- kfet/templates/kfet/kpsul.html | 2 +- kfet/templates/kfet/transfers.html | 4 +- kfet/templates/kfet/transfers_create.html | 2 +- .../templates/petitscours/demande_detail.html | 2 +- .../templates/petitscours/demande_list.html | 2 +- .../petitscours/details_demande_infos.html | 2 +- .../templates/petitscours/inscription.html | 2 +- .../traitement_demande_autre_niveau.html | 2 +- requirements.txt | 14 +- 42 files changed, 515 insertions(+), 40 deletions(-) create mode 100644 bda/migrations/0019_auto_20220628_1621.py create mode 100644 bds/migrations/0007_alter_bdsprofile_id.py create mode 100644 clubs/migrations/0002_alter_club_id.py create mode 100644 events/migrations/0005_auto_20220628_1621.py create mode 100644 gestioncof/migrations/0019_auto_20220628_1621.py create mode 100644 kfet/auth/migrations/0004_alter_genericteamtoken_id.py create mode 100644 kfet/cms/migrations/0003_alter_memberteam_id.py create mode 100644 kfet/migrations/0081_auto_20220628_1621.py diff --git a/bda/migrations/0019_auto_20220628_1621.py b/bda/migrations/0019_auto_20220628_1621.py new file mode 100644 index 00000000..3b5d3f2f --- /dev/null +++ b/bda/migrations/0019_auto_20220628_1621.py @@ -0,0 +1,76 @@ +# Generated by Django 3.2.13 on 2022-06-28 14:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bda", "0018_auto_20201021_1818"), + ] + + operations = [ + migrations.AlterField( + model_name="attribution", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="categoriespectacle", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="choixspectacle", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="participant", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="quote", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="salle", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="spectacle", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="spectaclerevente", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="tirage", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/bda/templates/bda-attrib.html b/bda/templates/bda-attrib.html index fac0de67..057cacb4 100644 --- a/bda/templates/bda-attrib.html +++ b/bda/templates/bda-attrib.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block extra_head %} diff --git a/bda/templates/bda/etat-places.html b/bda/templates/bda/etat-places.html index 401cc856..d1af0667 100644 --- a/bda/templates/bda/etat-places.html +++ b/bda/templates/bda/etat-places.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block realcontent %}

État des inscriptions BdA

diff --git a/bda/templates/bda/inscription-tirage.html b/bda/templates/bda/inscription-tirage.html index 3f8091df..1eecd7af 100644 --- a/bda/templates/bda/inscription-tirage.html +++ b/bda/templates/bda/inscription-tirage.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block extra_head %} diff --git a/bda/templates/bda/participants.html b/bda/templates/bda/participants.html index 4ab2d1f7..c99e5182 100644 --- a/bda/templates/bda/participants.html +++ b/bda/templates/bda/participants.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block realcontent %}

{{ spectacle }}

diff --git a/bda/templates/bda/revente/confirm-shotgun.html b/bda/templates/bda/revente/confirm-shotgun.html index d7614c25..bf8dccba 100644 --- a/bda/templates/bda/revente/confirm-shotgun.html +++ b/bda/templates/bda/revente/confirm-shotgun.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {%block realcontent %} diff --git a/bda/templates/bda/revente/confirmed.html b/bda/templates/bda/revente/confirmed.html index 780330bd..6f8ee583 100644 --- a/bda/templates/bda/revente/confirmed.html +++ b/bda/templates/bda/revente/confirmed.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block realcontent %}

Inscription à une revente

diff --git a/bda/templates/bda/revente/mail-success.html b/bda/templates/bda/revente/mail-success.html index 5e970eb7..6340a451 100644 --- a/bda/templates/bda/revente/mail-success.html +++ b/bda/templates/bda/revente/mail-success.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block realcontent %} diff --git a/bda/templates/bda/revente/manage.html b/bda/templates/bda/revente/manage.html index cd09f997..c42e0203 100644 --- a/bda/templates/bda/revente/manage.html +++ b/bda/templates/bda/revente/manage.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block realcontent %} diff --git a/bda/templates/bda/revente/subscribe.html b/bda/templates/bda/revente/subscribe.html index e0a7176c..c91fff15 100644 --- a/bda/templates/bda/revente/subscribe.html +++ b/bda/templates/bda/revente/subscribe.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles%} +{% load static %} {% block realcontent %}

Inscriptions pour BdA-Revente

diff --git a/bda/templates/bda/revente/tirages.html b/bda/templates/bda/revente/tirages.html index 4d9ac126..6ef55e03 100644 --- a/bda/templates/bda/revente/tirages.html +++ b/bda/templates/bda/revente/tirages.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block realcontent %} diff --git a/bda/templates/spectacle_list.html b/bda/templates/spectacle_list.html index 4539d730..1ffd7cc3 100644 --- a/bda/templates/spectacle_list.html +++ b/bda/templates/spectacle_list.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block extra_head %} diff --git a/bds/migrations/0007_alter_bdsprofile_id.py b/bds/migrations/0007_alter_bdsprofile_id.py new file mode 100644 index 00000000..7da58ca3 --- /dev/null +++ b/bds/migrations/0007_alter_bdsprofile_id.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2022-06-28 14:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bds", "0006_bdsprofile_comments"), + ] + + operations = [ + migrations.AlterField( + model_name="bdsprofile", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/bds/templates/bds/base.html b/bds/templates/bds/base.html index f456f6dc..74759e88 100644 --- a/bds/templates/bds/base.html +++ b/bds/templates/bds/base.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} {% load bulma_utils %} diff --git a/clubs/migrations/0002_alter_club_id.py b/clubs/migrations/0002_alter_club_id.py new file mode 100644 index 00000000..cbcb1014 --- /dev/null +++ b/clubs/migrations/0002_alter_club_id.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2022-06-28 14:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("clubs", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="club", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/events/migrations/0005_auto_20220628_1621.py b/events/migrations/0005_auto_20220628_1621.py new file mode 100644 index 00000000..f7dd6513 --- /dev/null +++ b/events/migrations/0005_auto_20220628_1621.py @@ -0,0 +1,55 @@ +# Generated by Django 3.2.13 on 2022-06-28 14:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("events", "0004_unique_constraints"), + ] + + operations = [ + migrations.AlterField( + model_name="event", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="extrafield", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="extrafieldcontent", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="option", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="optionchoice", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="registration", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/gestioasso/settings/cof_prod.py b/gestioasso/settings/cof_prod.py index c239ea0c..3e976952 100644 --- a/gestioasso/settings/cof_prod.py +++ b/gestioasso/settings/cof_prod.py @@ -85,7 +85,6 @@ MIDDLEWARE = ( + MIDDLEWARE + [ "djconfig.middleware.DjConfigMiddleware", - "wagtail.core.middleware.SiteMiddleware", "wagtail.contrib.redirects.middleware.RedirectMiddleware", ] ) diff --git a/gestioasso/settings/common.py b/gestioasso/settings/common.py index cabe7000..44a1592b 100644 --- a/gestioasso/settings/common.py +++ b/gestioasso/settings/common.py @@ -111,6 +111,7 @@ DATABASES = { SITE_ID = 1 +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # --- # Internationalization diff --git a/gestioncof/migrations/0019_auto_20220628_1621.py b/gestioncof/migrations/0019_auto_20220628_1621.py new file mode 100644 index 00000000..0163eb04 --- /dev/null +++ b/gestioncof/migrations/0019_auto_20220628_1621.py @@ -0,0 +1,139 @@ +# Generated by Django 3.2.13 on 2022-06-28 14:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("gestioncof", "0018_petitscours_email"), + ] + + operations = [ + migrations.AlterField( + model_name="calendarsubscription", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="club", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="cofprofile", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="event", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventcommentfield", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventcommentvalue", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventoption", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventoptionchoice", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventregistration", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcoursability", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcoursattribution", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcoursattributioncounter", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcoursdemande", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcourssubject", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="survey", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="surveyanswer", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="surveyquestion", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="surveyquestionanswer", + name="id", + field=models.BigAutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/gestioncof/templates/base.html b/gestioncof/templates/base.html index d313ee9d..7020e3c5 100644 --- a/gestioncof/templates/base.html +++ b/gestioncof/templates/base.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} diff --git a/gestioncof/templates/registration.html b/gestioncof/templates/registration.html index 2ef997e1..9807afde 100644 --- a/gestioncof/templates/registration.html +++ b/gestioncof/templates/registration.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block page_size %}col-sm-8{% endblock %} diff --git a/gestioncof/templates/tristate_js.html b/gestioncof/templates/tristate_js.html index af906ebe..6b5312a8 100644 --- a/gestioncof/templates/tristate_js.html +++ b/gestioncof/templates/tristate_js.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} diff --git a/kfet/templates/kfet/history.html b/kfet/templates/kfet/history.html index 100e0825..9deeefae 100644 --- a/kfet/templates/kfet/history.html +++ b/kfet/templates/kfet/history.html @@ -1,5 +1,5 @@ {% extends 'kfet/base_col_2.html' %} -{% load l10n staticfiles widget_tweaks bootstrap %} +{% load l10n static widget_tweaks bootstrap %} {% block extra_head %} diff --git a/kfet/templates/kfet/home.html b/kfet/templates/kfet/home.html index e5175dc3..8704bbe9 100644 --- a/kfet/templates/kfet/home.html +++ b/kfet/templates/kfet/home.html @@ -1,5 +1,5 @@ {% extends "kfet/base_col_1.html" %} -{% load staticfiles %} +{% load static %} {% load kfet_tags %} {% block title %}Accueil{% endblock %} diff --git a/kfet/templates/kfet/kpsul.html b/kfet/templates/kfet/kpsul.html index a89c4072..b67e298d 100644 --- a/kfet/templates/kfet/kpsul.html +++ b/kfet/templates/kfet/kpsul.html @@ -1,5 +1,5 @@ {% extends 'kfet/base.html' %} -{% load staticfiles %} +{% load static %} {% block extra_head %} diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index f8bef33d..e9ee16b0 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -1,6 +1,6 @@ {% extends 'kfet/base_col_2.html' %} -{% load staticfiles %} -{% load l10n staticfiles widget_tweaks %} +{% load static %} +{% load l10n static widget_tweaks %} {% block title %}Transferts{% endblock %} {% block header-title %}Transferts{% endblock %} diff --git a/kfet/templates/kfet/transfers_create.html b/kfet/templates/kfet/transfers_create.html index fc429d97..3a85264d 100644 --- a/kfet/templates/kfet/transfers_create.html +++ b/kfet/templates/kfet/transfers_create.html @@ -1,5 +1,5 @@ {% extends "kfet/base_col_1.html" %} -{% load staticfiles %} +{% load static %} {% block extra_head %} diff --git a/petitscours/templates/petitscours/demande_detail.html b/petitscours/templates/petitscours/demande_detail.html index d7f9ca8b..8711fcda 100644 --- a/petitscours/templates/petitscours/demande_detail.html +++ b/petitscours/templates/petitscours/demande_detail.html @@ -1,5 +1,5 @@ {% extends "petitscours/base_title.html" %} -{% load staticfiles %} +{% load static %} {% block page_size %}col-sm-8{% endblock %} diff --git a/petitscours/templates/petitscours/demande_list.html b/petitscours/templates/petitscours/demande_list.html index e4c3c782..04132d57 100644 --- a/petitscours/templates/petitscours/demande_list.html +++ b/petitscours/templates/petitscours/demande_list.html @@ -1,5 +1,5 @@ {% extends "petitscours/base_title.html" %} -{% load staticfiles %} +{% load static %} {% block realcontent %}

Demandes de petits cours

diff --git a/petitscours/templates/petitscours/details_demande_infos.html b/petitscours/templates/petitscours/details_demande_infos.html index 39cee1d3..42f37d56 100644 --- a/petitscours/templates/petitscours/details_demande_infos.html +++ b/petitscours/templates/petitscours/details_demande_infos.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} diff --git a/petitscours/templates/petitscours/inscription.html b/petitscours/templates/petitscours/inscription.html index 9512e0b3..eaf10524 100644 --- a/petitscours/templates/petitscours/inscription.html +++ b/petitscours/templates/petitscours/inscription.html @@ -1,5 +1,5 @@ {% extends "base_title.html" %} -{% load staticfiles %} +{% load static %} {% block extra_head %} diff --git a/petitscours/templates/petitscours/traitement_demande_autre_niveau.html b/petitscours/templates/petitscours/traitement_demande_autre_niveau.html index cb3ec379..c10c8aaf 100644 --- a/petitscours/templates/petitscours/traitement_demande_autre_niveau.html +++ b/petitscours/templates/petitscours/traitement_demande_autre_niveau.html @@ -1,5 +1,5 @@ {% extends "petitscours/base_title.html" %} -{% load staticfiles %} +{% load static %} {% block realcontent %}

diff --git a/requirements.txt b/requirements.txt index d2e0c525..05d0126f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ configparser==3.5.0 -Django==2.2.* -django-autocomplete-light==3.3.* -django-cas-ng==3.6.* +Django==3.2.* +django-autocomplete-light==3.9.* +django-cas-ng==4.3.* django-djconfig==0.8.0 django-hCaptcha==0.1.0 icalendar @@ -11,9 +11,9 @@ statistics==1.0.3.5 django-widget-tweaks==1.4.1 channels==2.4.* python-dateutil -wagtail==2.7.* +wagtail==2.13.* wagtailmenus==3.* -wagtail-modeltranslation==0.10.* -django-cors-headers==2.2.0 +wagtail-modeltranslation==0.11.* +django-cors-headers==3.13.* django-js-reverse -authens==0.1b0 +authens==0.1b4 -- 2.45.1 From 65ea5d21b2189d9c2ba5b588b78a3ea9e9f041e2 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 28 Jun 2022 22:31:56 +0200 Subject: [PATCH 07/40] Update django-redis-cache and fix vagrant setup --- provisioning/nginx/gestiocof.conf | 4 ++-- provisioning/systemd/daphne.service | 2 +- provisioning/systemd/worker.service | 5 ++++- requirements-prod.txt | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/provisioning/nginx/gestiocof.conf b/provisioning/nginx/gestiocof.conf index f623bcf9..7d7567c6 100644 --- a/provisioning/nginx/gestiocof.conf +++ b/provisioning/nginx/gestiocof.conf @@ -15,8 +15,8 @@ server { rewrite ^/gestion$ http://localhost:8080/gestion/ redirect; # Les pages statiques sont servies à part. - location /gestion/static { try_files $uri $uri/ =404; } - location /gestion/media { try_files $uri $uri/ =404; } + location /static { try_files $uri $uri/ =404; } + location /media { try_files $uri $uri/ =404; } # On proxy-pass les requêtes vers les pages dynamiques à daphne location / { diff --git a/provisioning/systemd/daphne.service b/provisioning/systemd/daphne.service index 517c86c9..bae9f3ca 100644 --- a/provisioning/systemd/daphne.service +++ b/provisioning/systemd/daphne.service @@ -11,7 +11,7 @@ WorkingDirectory=/vagrant Environment="DJANGO_SETTINGS_MODULE=gestioasso.settings.dev" ExecStart=/home/vagrant/venv/bin/daphne \ -u /srv/gestiocof/gestiocof.sock \ - gestioasso.asgi:channel_layer + gestioasso.asgi:application [Install] WantedBy=multi-user.target diff --git a/provisioning/systemd/worker.service b/provisioning/systemd/worker.service index a9ea733f..0d97e9a4 100644 --- a/provisioning/systemd/worker.service +++ b/provisioning/systemd/worker.service @@ -10,7 +10,10 @@ Group=vagrant TimeoutSec=300 WorkingDirectory=/vagrant Environment="DJANGO_SETTINGS_MODULE=gestioasso.settings.dev" -ExecStart=/home/vagrant/venv/bin/python manage.py runworker +ExecStart=/home/vagrant/venv/bin/python manage.py runworker \ + 'kfet.open.team' \ + 'kfet.open.base' \ + 'kpsul' [Install] WantedBy=multi-user.target diff --git a/requirements-prod.txt b/requirements-prod.txt index bd50ba77..2f6c556b 100644 --- a/requirements-prod.txt +++ b/requirements-prod.txt @@ -4,7 +4,7 @@ psycopg2<2.8 # Redis -django-redis-cache==2.1.* +django-redis-cache==3.0.* redis~=2.10.6 channels-redis==2.4.* -- 2.45.1 From 3411bc8a00ea1d31d7dd6f5a09bb549700e53e12 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 28 Jun 2022 22:56:40 +0200 Subject: [PATCH 08/40] keyOrder is deprecated, using a list in Meta gives the correct order --- gestioncof/forms.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/gestioncof/forms.py b/gestioncof/forms.py index 2a57f970..983bb249 100644 --- a/gestioncof/forms.py +++ b/gestioncof/forms.py @@ -276,7 +276,9 @@ class RegistrationProfileForm(forms.ModelForm): self.fields["mailing_bda_revente"].initial = True self.fields["mailing_unernestaparis"].initial = True - self.fields.keyOrder = [ + class Meta: + model = CofProfile + fields = [ "login_clipper", "phone", "occupation", @@ -290,22 +292,6 @@ class RegistrationProfileForm(forms.ModelForm): "comments", ] - class Meta: - model = CofProfile - fields = ( - "login_clipper", - "phone", - "occupation", - "departement", - "is_cof", - "type_cotiz", - "mailing_cof", - "mailing_bda", - "mailing_bda_revente", - "mailing_unernestaparis", - "comments", - ) - STATUS_CHOICES = ( ("no", "Non"), -- 2.45.1 From a01e11e45b7edc21f5703f6572964855722d72d6 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 11:09:16 +0200 Subject: [PATCH 09/40] Fix app import --- bds/apps.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bds/apps.py b/bds/apps.py index 5c0fa0fd..740d3559 100644 --- a/bds/apps.py +++ b/bds/apps.py @@ -1,5 +1,4 @@ -from django import apps as global_apps -from django.apps import AppConfig +from django.apps import AppConfig, apps as global_apps from django.db.models import Q from django.db.models.signals import post_migrate -- 2.45.1 From 1ae5c80f76c35758325a05a7de0843800ba4d2a4 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 11:09:34 +0200 Subject: [PATCH 10/40] Add default django asgi handler --- gestioasso/routing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gestioasso/routing.py b/gestioasso/routing.py index f329fdcd..2b42648a 100644 --- a/gestioasso/routing.py +++ b/gestioasso/routing.py @@ -1,6 +1,6 @@ from channels.auth import AuthMiddlewareStack -from channels.http import AsgiHandler from channels.routing import ProtocolTypeRouter, URLRouter +from django.core.asgi import get_asgi_application from django.urls import path from kfet.routing import KFRouter @@ -15,6 +15,6 @@ application = ProtocolTypeRouter( ] ) ), - "http": AsgiHandler, + "http": get_asgi_application(), } ) -- 2.45.1 From 2b712dd05d243e1706c96b5c04124bace8ca2f93 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 11:10:13 +0200 Subject: [PATCH 11/40] Channels 3 --- kfet/open/routing.py | 2 +- kfet/routing.py | 2 +- requirements-prod.txt | 4 ++-- requirements.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kfet/open/routing.py b/kfet/open/routing.py index 6b10b4ec..9d205638 100644 --- a/kfet/open/routing.py +++ b/kfet/open/routing.py @@ -5,6 +5,6 @@ from .consumers import OpenKfetConsumer OpenRouter = URLRouter( [ - path(r"", OpenKfetConsumer), + path("", OpenKfetConsumer.as_asgi()), ] ) diff --git a/kfet/routing.py b/kfet/routing.py index 492f6e4f..a015eebc 100644 --- a/kfet/routing.py +++ b/kfet/routing.py @@ -7,7 +7,7 @@ from .consumers import KPsul KFRouter = URLRouter( [ - path("k-psul/", KPsul), + path("k-psul/", KPsul.as_asgi()), path("open", OpenRouter), ] ) diff --git a/requirements-prod.txt b/requirements-prod.txt index 2f6c556b..ffd21935 100644 --- a/requirements-prod.txt +++ b/requirements-prod.txt @@ -6,10 +6,10 @@ psycopg2<2.8 # Redis django-redis-cache==3.0.* redis~=2.10.6 -channels-redis==2.4.* +channels-redis==3.4.* # ASGI protocol and HTTP server -daphne==2.5.* +daphne==3.0.* # ldap bindings python-ldap diff --git a/requirements.txt b/requirements.txt index 05d0126f..9e0b8b8e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ Pillow django-bootstrap-form==3.3 statistics==1.0.3.5 django-widget-tweaks==1.4.1 -channels==2.4.* +channels==3.0.* python-dateutil wagtail==2.13.* wagtailmenus==3.* -- 2.45.1 From 5a28aab7cb29c1541acd09aea85ceb80bbd78f9a Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 11:10:37 +0200 Subject: [PATCH 12/40] Simplify group_send for kpsul --- kfet/consumers.py | 9 +++++++++ kfet/views.py | 13 ++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/kfet/consumers.py b/kfet/consumers.py index e8334421..1765836c 100644 --- a/kfet/consumers.py +++ b/kfet/consumers.py @@ -1,3 +1,6 @@ +from asgiref.sync import async_to_sync +from channels.layers import get_channel_layer + from .utils import DjangoJsonWebsocketConsumer, PermConsumerMixin @@ -7,3 +10,9 @@ class KPsul(PermConsumerMixin, DjangoJsonWebsocketConsumer): async def kpsul(self, event): await self.send_json(event) + + @classmethod + @async_to_sync + async def group_send(cls, group, data): + channel_layer = get_channel_layer() + await channel_layer.group_send(group, data) diff --git a/kfet/views.py b/kfet/views.py index b0711a6b..bd25f3fe 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -6,8 +6,6 @@ from decimal import Decimal from typing import List, Tuple from urllib.parse import urlencode -from asgiref.sync import async_to_sync -from channels.layers import get_channel_layer from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required @@ -37,6 +35,7 @@ from kfet import KFET_DELETED_TRIGRAMME from kfet.auth.decorators import kfet_password_auth from kfet.autocomplete import kfet_account_only_autocomplete, kfet_autocomplete from kfet.config import kfet_config +from kfet.consumers import KPsul from kfet.decorators import teamkfet_required from kfet.forms import ( AccountForm, @@ -991,9 +990,8 @@ def kpsul_update_addcost(request): "type": "kpsul", } - channel_layer = get_channel_layer() + KPsul.group_send("kfet.kpsul", data) - async_to_sync(channel_layer.group_send)("kfet.kpsul", data) return JsonResponse(data) @@ -1225,9 +1223,8 @@ def kpsul_perform_operations(request): {"id": article["id"], "stock": article["stock"]} ) - channel_layer = get_channel_layer() + KPsul.group_send("kfet.kpsul", websocket_data) - async_to_sync(channel_layer.group_send)("kfet.kpsul", websocket_data) return JsonResponse(data) @@ -1432,9 +1429,7 @@ def cancel_operations(request): {"id": article["id"], "stock": article["stock"]} ) - channel_layer = get_channel_layer() - - async_to_sync(channel_layer.group_send)("kfet.kpsul", websocket_data) + KPsul.group_send("kfet.kpsul", websocket_data) data["canceled"] = list(opes) data["opegroups_to_update"] = list(opegroups) -- 2.45.1 From c9158c8e13e65d0187c4446e584b8b95a5645257 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 11:11:55 +0200 Subject: [PATCH 13/40] Fix kpsul tests --- kfet/tests/test_views.py | 145 +++++++++++++++++++++++++-------------- 1 file changed, 93 insertions(+), 52 deletions(-) diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py index 7a7eddcb..39ec0aa7 100644 --- a/kfet/tests/test_views.py +++ b/kfet/tests/test_views.py @@ -3,6 +3,8 @@ from datetime import datetime, timedelta from decimal import Decimal from unittest import mock +from asgiref.sync import async_to_sync +from channels.layers import get_channel_layer from django.contrib.auth.models import User from django.test import Client, TestCase from django.urls import reverse @@ -1808,10 +1810,13 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.balance = Decimal("50.00") self.account.save() - # Mock consumer of K-Psul websocket to catch what we're sending - kpsul_consumer_patcher = mock.patch("kfet.consumers.KPsul") - self.kpsul_consumer_mock = kpsul_consumer_patcher.start() - self.addCleanup(kpsul_consumer_patcher.stop) + # Create a channel to listen to KPsul's messages + channel_layer = get_channel_layer() + self.channel = async_to_sync(channel_layer.new_channel)() + + async_to_sync(channel_layer.group_add)("kfet.kpsul", self.channel) + + self.receive_msg = lambda: async_to_sync(channel_layer.receive)(self.channel) # Reset cache of kfet config kfet_config._conf_init = False @@ -2043,9 +2048,12 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(self.article.stock, 18) # Check websocket data - self.kpsul_consumer_mock.group_send.assert_called_once_with( - "kfet.kpsul", + ws_data = self.receive_msg() + + self.assertDictEqual( + ws_data, { + "type": "kpsul", "groups": [ { "add": True, @@ -2307,9 +2315,12 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("110.75")) - self.kpsul_consumer_mock.group_send.assert_called_once_with( - "kfet.kpsul", + ws_data = self.receive_msg() + + self.assertDictEqual( + ws_data, { + "type": "kpsul", "groups": [ { "add": True, @@ -2478,9 +2489,12 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("89.25")) - self.kpsul_consumer_mock.group_send.assert_called_once_with( - "kfet.kpsul", + ws_data = self.receive_msg() + + self.assertDictEqual( + ws_data, { + "type": "kpsul", "groups": [ { "add": True, @@ -2635,9 +2649,12 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("100.00")) - self.kpsul_consumer_mock.group_send.assert_called_once_with( - "kfet.kpsul", + ws_data = self.receive_msg() + + self.assertDictEqual( + ws_data, { + "type": "kpsul", "groups": [ { "add": True, @@ -2750,9 +2767,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("100.00")) - ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["groups"][0][ - "entries" - ][0] + ws_data = self.receive_msg() + ws_data_ope = ws_data["groups"][0]["entries"][0] + self.assertEqual(ws_data_ope["addcost_amount"], Decimal("1.00")) self.assertEqual(ws_data_ope["addcost_for__trigramme"], "ADD") @@ -2790,9 +2807,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("100.00")) - ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["groups"][0][ - "entries" - ][0] + ws_data = self.receive_msg() + ws_data_ope = ws_data["groups"][0]["entries"][0] + self.assertEqual(ws_data_ope["addcost_amount"], Decimal("0.80")) self.assertEqual(ws_data_ope["addcost_for__trigramme"], "ADD") @@ -2828,9 +2845,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("106.00")) - ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["groups"][0][ - "entries" - ][0] + ws_data = self.receive_msg() + ws_data_ope = ws_data["groups"][0]["entries"][0] + self.assertEqual(ws_data_ope["addcost_amount"], Decimal("1.00")) self.assertEqual(ws_data_ope["addcost_for__trigramme"], "ADD") @@ -2864,9 +2881,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.accounts["addcost"].refresh_from_db() self.assertEqual(self.accounts["addcost"].balance, Decimal("15.00")) - ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["groups"][0][ - "entries" - ][0] + ws_data = self.receive_msg() + ws_data_ope = ws_data["groups"][0]["entries"][0] + self.assertEqual(ws_data_ope["addcost_amount"], None) self.assertEqual(ws_data_ope["addcost_for__trigramme"], None) @@ -2899,9 +2916,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.accounts["addcost"].refresh_from_db() self.assertEqual(self.accounts["addcost"].balance, Decimal("0.00")) - ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["groups"][0][ - "entries" - ][0] + ws_data = self.receive_msg() + ws_data_ope = ws_data["groups"][0]["entries"][0] + self.assertEqual(ws_data_ope["addcost_amount"], None) self.assertEqual(ws_data_ope["addcost_for__trigramme"], None) @@ -3123,9 +3140,12 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(article2.stock, -6) # Check websocket data - self.kpsul_consumer_mock.group_send.assert_called_once_with( - "kfet.kpsul", + ws_data = self.receive_msg() + + self.assertDictEqual( + ws_data, { + "type": "kpsul", "groups": [ { "add": True, @@ -3218,10 +3238,13 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.balance = Decimal("50.00") self.account.save() - # Mock consumer of K-Psul websocket to catch what we're sending - kpsul_consumer_patcher = mock.patch("kfet.consumers.KPsul") - self.kpsul_consumer_mock = kpsul_consumer_patcher.start() - self.addCleanup(kpsul_consumer_patcher.stop) + # Create a channel to listen to KPsul's messages + channel_layer = get_channel_layer() + self.channel = async_to_sync(channel_layer.new_channel)() + + async_to_sync(channel_layer.group_add)("kfet.kpsul", self.channel) + + self.receive_msg = lambda: async_to_sync(channel_layer.receive)(self.channel) def _assertResponseOk(self, response): """ @@ -3271,7 +3294,11 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): on_acc=self.account, checkout=self.checkout, content=[ - {"type": Operation.PURCHASE, "article": self.article, "article_nb": 2} + { + "type": Operation.PURCHASE, + "article": self.article, + "article_nb": 2, + } ], ) operation = group.opes.get() @@ -3345,9 +3372,15 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("100.00")) - self.kpsul_consumer_mock.group_send.assert_called_with( - "kfet.kpsul", - {"checkouts": [], "articles": [{"id": self.article.pk, "stock": 22}]}, + ws_data = self.receive_msg() + + self.assertDictEqual( + ws_data, + { + "type": "kpsul", + "checkouts": [], + "articles": [{"id": self.article.pk, "stock": 22}], + }, ) def test_purchase_with_addcost(self): @@ -3407,11 +3440,11 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("95.00")) - ws_data_checkouts = self.kpsul_consumer_mock.group_send.call_args[0][1][ - "checkouts" - ] + ws_data = self.receive_msg() + self.assertListEqual( - ws_data_checkouts, [{"id": self.checkout.pk, "balance": Decimal("95.00")}] + ws_data["checkouts"], + [{"id": self.checkout.pk, "balance": Decimal("95.00")}], ) def test_purchase_cash_with_addcost(self): @@ -3447,11 +3480,11 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): addcost_account.refresh_from_db() self.assertEqual(addcost_account.balance, Decimal("9.00")) - ws_data_checkouts = self.kpsul_consumer_mock.group_send.call_args[0][1][ - "checkouts" - ] + ws_data = self.receive_msg() + self.assertListEqual( - ws_data_checkouts, [{"id": self.checkout.pk, "balance": Decimal("94.00")}] + ws_data["checkouts"], + [{"id": self.checkout.pk, "balance": Decimal("94.00")}], ) @mock.patch("django.utils.timezone.now") @@ -3533,9 +3566,12 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("89.25")) - self.kpsul_consumer_mock.group_send.assert_called_with( - "kfet.kpsul", + ws_data = self.receive_msg() + + self.assertDictEqual( + ws_data, { + "type": "kpsul", "checkouts": [{"id": self.checkout.pk, "balance": Decimal("89.25")}], "articles": [], }, @@ -3620,9 +3656,12 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("110.75")) - self.kpsul_consumer_mock.group_send.assert_called_with( - "kfet.kpsul", + ws_data = self.receive_msg() + + self.assertDictEqual( + ws_data, { + "type": "kpsul", "checkouts": [{"id": self.checkout.pk, "balance": Decimal("110.75")}], "articles": [], }, @@ -3707,9 +3746,11 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("100.00")) - self.kpsul_consumer_mock.group_send.assert_called_with( - "kfet.kpsul", - {"checkouts": [], "articles": []}, + ws_data = self.receive_msg() + + self.assertDictEqual( + ws_data, + {"type": "kpsul", "checkouts": [], "articles": []}, ) @mock.patch("django.utils.timezone.now") -- 2.45.1 From 9a7deb5e2a8d0e16a6a7644a019fd696b54a554a Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 13:34:52 +0200 Subject: [PATCH 14/40] Fix kfet.ope tests --- kfet/open/tests.py | 245 ++++++++++++++++++++++++++++----------------- 1 file changed, 152 insertions(+), 93 deletions(-) diff --git a/kfet/open/tests.py b/kfet/open/tests.py index 7918ace7..2c3f9c4e 100644 --- a/kfet/open/tests.py +++ b/kfet/open/tests.py @@ -1,19 +1,24 @@ -import json import random from datetime import timedelta from unittest import mock -from channels.channel import Group -from channels.test import ChannelTestCase, WSClient +from asgiref.sync import async_to_sync, sync_to_async +from channels.auth import AuthMiddlewareStack +from channels.consumer import get_channel_layer +from channels.testing import WebsocketCommunicator from django.contrib.auth.models import AnonymousUser, Permission, User -from django.test import Client +from django.test import Client, TestCase from django.utils import timezone from . import OpenKfet from .consumers import OpenKfetConsumer -class OpenKfetTest(ChannelTestCase): +def ws_communicator(cls, path: str, headers=[]): + return WebsocketCommunicator(AuthMiddlewareStack(cls.as_asgi()), path, headers) + + +class OpenKfetTest(TestCase): """OpenKfet object unit-tests suite.""" def setUp(self): @@ -79,7 +84,7 @@ class OpenKfetTest(ChannelTestCase): def test_export_user(self): """Export is limited for an anonymous user.""" export = self.kfet_open.export(AnonymousUser()) - self.assertSetEqual(set(["status"]), set(export)) + self.assertSetEqual(set(["status", "type"]), set(export)) def test_export_team(self): """Export all values for a team member.""" @@ -89,24 +94,32 @@ class OpenKfetTest(ChannelTestCase): ) user.user_permissions.add(is_team) export = self.kfet_open.export(user) - self.assertSetEqual(set(["status", "admin_status", "force_close"]), set(export)) + self.assertSetEqual( + set(["status", "admin_status", "force_close", "type"]), set(export) + ) - def test_send_ws(self): - Group("kfet.open.base").add("test.open.base") - Group("kfet.open.team").add("test.open.team") + async def test_send_ws(self): + channel_layer = get_channel_layer() + base_channel = await channel_layer.new_channel() + team_channel = await channel_layer.new_channel() - self.kfet_open.send_ws() + await channel_layer.group_add("kfet.open.base", base_channel) + await channel_layer.group_add("kfet.open.team", team_channel) - recv_base = self.get_next_message("test.open.base", require=True) - base = json.loads(recv_base["text"]) - self.assertSetEqual(set(["status"]), set(base)) + await self.kfet_open.send_ws() - recv_admin = self.get_next_message("test.open.team", require=True) - admin = json.loads(recv_admin["text"]) - self.assertSetEqual(set(["status", "admin_status", "force_close"]), set(admin)) + base = await channel_layer.receive(base_channel) + + self.assertSetEqual(set(["status", "type"]), set(base)) + + team = await channel_layer.receive(team_channel) + + self.assertSetEqual( + set(["status", "admin_status", "force_close", "type"]), set(team) + ) -class OpenKfetViewsTest(ChannelTestCase): +class OpenKfetViewsTest(TestCase): """OpenKfet views unit-tests suite.""" def setUp(self): @@ -177,60 +190,82 @@ class OpenKfetViewsTest(ChannelTestCase): self.assertEqual(403, resp.status_code) -class OpenKfetConsumerTest(ChannelTestCase): +class OpenKfetConsumerTest(TestCase): """OpenKfet consumer unit-tests suite.""" - def test_standard_user(self): - """Lambda user is added to kfet.open.base group.""" - # setup anonymous client - c = WSClient() - - # connect - c.send_and_consume( - "websocket.connect", path="/ws/k-fet/open", fail_on_none=True - ) - - # initialization data is replied on connection - self.assertIsNotNone(c.receive()) - - # client belongs to the 'kfet.open' group... - OpenKfetConsumer.group_send("kfet.open.base", {"test": "plop"}) - self.assertEqual(c.receive(), {"test": "plop"}) - - # ...but not to the 'kfet.open.admin' one - OpenKfetConsumer.group_send("kfet.open.team", {"test": "plop"}) - self.assertIsNone(c.receive()) - - @mock.patch("gestioncof.signals.messages") - def test_team_user(self, mock_messages): - """Team user is added to kfet.open.team group.""" - # setup team user and its client + def setUp(self): t = User.objects.create_user("team", "", "team") is_team = Permission.objects.get( codename="is_team", content_type__app_label="kfet" ) t.user_permissions.add(is_team) - c = WSClient() - c.force_login(t, backend="django.contrib.auth.backends.ModelBackend") - # connect - c.send_and_consume( - "websocket.connect", path="/ws/k-fet/open", fail_on_none=True - ) + self.team_user = t + + @async_to_sync + async def test_standard_user(self): + """Lambda user is added to kfet.open.base group.""" + # setup anonymous client + c = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") + + connected, _ = await c.connect() + + self.assertTrue(connected) # initialization data is replied on connection - self.assertIsNotNone(c.receive()) + message = await c.receive_json_from() + self.assertIsNotNone(message) - # client belongs to the 'kfet.open.admin' group... - OpenKfetConsumer.group_send("kfet.open.team", {"test": "plop"}) - self.assertEqual(c.receive(), {"test": "plop"}) + # client belongs to the 'kfet.open' group... + channel_layer = get_channel_layer() - # ... but not to the 'kfet.open' one - OpenKfetConsumer.group_send("kfet.open.base", {"test": "plop"}) - self.assertIsNone(c.receive()) + await channel_layer.group_send( + "kfet.open.base", {"test": "plop", "type": "open.status"} + ) + message = await c.receive_json_from() + + self.assertEqual(message, {"test": "plop"}) + + # ...but not to the 'kfet.open.admin' one + await channel_layer.group_send( + "kfet.open.team", {"test": "plop", "type": "open.status"} + ) + self.assertTrue(await c.receive_nothing()) + + async def test_team_user(self): + """Team user is added to kfet.open.team group.""" + + with mock.patch("gestioncof.signals.messages"), mock.patch( + "kfet.open.consumers.kfet_is_team", return_value=True + ): + c = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") + + connected, _ = await c.connect() + + channel_layer = get_channel_layer() + + self.assertTrue(connected) + + # initialization data is replied on connection + message = await c.receive_json_from() + self.assertIsNotNone(message) + + # client belongs to the 'kfet.open.team' group... + await channel_layer.group_send( + "kfet.open.team", {"test": "plop", "type": "open.status"} + ) + message = await c.receive_json_from() + + self.assertEqual(message, {"test": "plop"}) + + # ...but not to the 'kfet.open' one + await channel_layer.group_send( + "kfet.open.base", {"test": "plop", "type": "open.status"} + ) + self.assertTrue(await c.receive_nothing()) -class OpenKfetScenarioTest(ChannelTestCase): +class OpenKfetScenarioTest(TestCase): """OpenKfet functionnal tests suite.""" def setUp(self): @@ -241,91 +276,115 @@ class OpenKfetScenarioTest(ChannelTestCase): # anonymous client (for views) self.c = Client() - # anonymous client (for websockets) - self.c_ws = WSClient() # root user self.r = User.objects.create_superuser("root", "", "root") - # its client (for views) + + # root client self.r_c = Client() self.r_c.login(username="root", password="root") - # its client (for websockets) - self.r_c_ws = WSClient() - self.r_c_ws.force_login( - self.r, backend="django.contrib.auth.backends.ModelBackend" - ) self.kfet_open = OpenKfet( cache_prefix="test_kfetopen_%s" % random.randrange(2**20) ) self.addCleanup(self.kfet_open.clear_cache) - def ws_connect(self, ws_client): - ws_client.send_and_consume( - "websocket.connect", path="/ws/k-fet/open", fail_on_none=True - ) - return ws_client.receive(json=True) + async def ws_connect(self, ws_client): + c, _ = await ws_client.connect() - def test_scenario_0(self): + self.assertTrue(c) + + return await ws_client.receive_json_from() + + async def test_scenario_0(self): """Clients connect.""" + # anonymous client (for websockets) + self.c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") + # test for anonymous user - msg = self.ws_connect(self.c_ws) + msg = await self.ws_connect(self.c_ws) self.assertSetEqual(set(["status"]), set(msg)) # test for root user - msg = self.ws_connect(self.r_c_ws) - self.assertSetEqual(set(["status", "admin_status", "force_close"]), set(msg)) + with mock.patch( + "kfet.open.consumers.kfet_is_team", return_value=True + ), mock.patch("kfet.open.open.kfet_is_team", return_value=True): + self.r_c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") - def test_scenario_1(self): + msg = await self.ws_connect(self.r_c_ws) + self.assertSetEqual( + set(["status", "admin_status", "force_close"]), set(msg) + ) + + async def test_scenario_1(self): """Clients connect, door opens, enable force close.""" - self.ws_connect(self.c_ws) - self.ws_connect(self.r_c_ws) + + self.c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") + await self.ws_connect(self.c_ws) + + with mock.patch( + "kfet.open.consumers.kfet_is_team", return_value=True + ), mock.patch("kfet.open.open.kfet_is_team", return_value=True): + self.r_c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") + await self.ws_connect(self.r_c_ws) # door sent "I'm open!" - self.c.post("/k-fet/open/raw_open", {"raw_open": True, "token": "plop"}) + await sync_to_async(self.c.post)( + "/k-fet/open/raw_open", {"raw_open": True, "token": "plop"} + ) # anonymous user agree - msg = self.c_ws.receive(json=True) + msg = await self.c_ws.receive_json_from() self.assertEqual(OpenKfet.OPENED, msg["status"]) # root user too - msg = self.r_c_ws.receive(json=True) + msg = await self.r_c_ws.receive_json_from() self.assertEqual(OpenKfet.OPENED, msg["status"]) self.assertEqual(OpenKfet.OPENED, msg["admin_status"]) # admin says "no it's closed" - self.r_c.post("/k-fet/open/force_close", {"force_close": True}) + await sync_to_async(self.r_c.post)( + "/k-fet/open/force_close", {"force_close": True} + ) # so anonymous user see it's closed - msg = self.c_ws.receive(json=True) + msg = await self.c_ws.receive_json_from() self.assertEqual(OpenKfet.CLOSED, msg["status"]) # root user too - msg = self.r_c_ws.receive(json=True) + msg = await self.r_c_ws.receive_json_from() self.assertEqual(OpenKfet.CLOSED, msg["status"]) # but root knows things self.assertEqual(OpenKfet.FAKE_CLOSED, msg["admin_status"]) self.assertTrue(msg["force_close"]) - def test_scenario_2(self): + async def test_scenario_2(self): """Starting falsely closed, clients connect, disable force close.""" self.kfet_open.raw_open = True self.kfet_open.force_close = True - msg = self.ws_connect(self.c_ws) + self.c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") + + msg = await self.ws_connect(self.c_ws) self.assertEqual(OpenKfet.CLOSED, msg["status"]) - msg = self.ws_connect(self.r_c_ws) - self.assertEqual(OpenKfet.CLOSED, msg["status"]) - self.assertEqual(OpenKfet.FAKE_CLOSED, msg["admin_status"]) - self.assertTrue(msg["force_close"]) + with mock.patch( + "kfet.open.consumers.kfet_is_team", return_value=True + ), mock.patch("kfet.open.open.kfet_is_team", return_value=True): + self.r_c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") + msg = await self.ws_connect(self.r_c_ws) + self.assertEqual(OpenKfet.CLOSED, msg["status"]) + self.assertEqual(OpenKfet.FAKE_CLOSED, msg["admin_status"]) + self.assertTrue(msg["force_close"]) - self.r_c.post("/k-fet/open/force_close", {"force_close": False}) + await sync_to_async(self.r_c.post)( + "/k-fet/open/force_close", {"force_close": False} + ) - msg = self.c_ws.receive(json=True) + msg = await self.c_ws.receive_json_from() self.assertEqual(OpenKfet.OPENED, msg["status"]) - msg = self.r_c_ws.receive(json=True) + msg = await self.r_c_ws.receive_json_from() self.assertEqual(OpenKfet.OPENED, msg["status"]) self.assertEqual(OpenKfet.OPENED, msg["admin_status"]) self.assertFalse(msg["force_close"]) -- 2.45.1 From 95e658d05b91d16b165b398b8bb9d84d291e3899 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 14:37:28 +0200 Subject: [PATCH 15/40] Update redis, and implement a custom channel layer to send datetime/decimal objects --- gestioasso/settings/cof_prod.py | 2 +- requirements-prod.txt | 2 +- shared/channels.py | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 shared/channels.py diff --git a/gestioasso/settings/cof_prod.py b/gestioasso/settings/cof_prod.py index 3e976952..05a7b2c9 100644 --- a/gestioasso/settings/cof_prod.py +++ b/gestioasso/settings/cof_prod.py @@ -148,7 +148,7 @@ CACHES = { CHANNEL_LAYERS = { "default": { - "BACKEND": "channels_redis.core.RedisChannelLayer", + "BACKEND": "shared.channels.ChannelLayer", "CONFIG": { "hosts": [ ( diff --git a/requirements-prod.txt b/requirements-prod.txt index ffd21935..4037e7fe 100644 --- a/requirements-prod.txt +++ b/requirements-prod.txt @@ -5,7 +5,7 @@ psycopg2<2.8 # Redis django-redis-cache==3.0.* -redis~=2.10.6 +redis==3.5.* channels-redis==3.4.* # ASGI protocol and HTTP server diff --git a/shared/channels.py b/shared/channels.py new file mode 100644 index 00000000..ae8c1248 --- /dev/null +++ b/shared/channels.py @@ -0,0 +1,45 @@ +import datetime +import random +from decimal import Decimal + +import msgpack +from channels_redis.core import RedisChannelLayer + + +def encode_kf(obj): + if isinstance(obj, Decimal): + return {"__decimal__": True, "as_str": str(obj)} + elif isinstance(obj, datetime.datetime): + return {"__datetime__": True, "as_str": obj.strftime("%Y%m%dT%H:%M:%S.%f")} + return obj + + +def decode_kf(obj): + if "__decimal__" in obj: + obj = Decimal(obj["as_str"]) + elif "__datetime__" in obj: + obj = datetime.datetime.strptime(obj["as_str"], "%Y%m%dT%H:%M:%S.%f") + return obj + + +class ChannelLayer(RedisChannelLayer): + def serialize(self, message): + """Serializes to a byte string.""" + value = msgpack.packb(message, default=encode_kf, use_bin_type=True) + + if self.crypter: + value = self.crypter.encrypt(value) + + # As we use an sorted set to expire messages + # we need to guarantee uniqueness, with 12 bytes. + random_prefix = random.getrandbits(8 * 12).to_bytes(12, "big") + return random_prefix + value + + def deserialize(self, message): + """Deserializes from a byte string.""" + # Removes the random prefix + message = message[12:] + + if self.crypter: + message = self.crypter.decrypt(message, self.expiry + 10) + return msgpack.unpackb(message, object_hook=decode_kf, raw=False) -- 2.45.1 From 8a1c39e43b99b817f569e7a380f5744678c4554e Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 16:09:50 +0200 Subject: [PATCH 16/40] ugettext -> gettext --- gestioncof/admin.py | 2 +- gestioncof/cms/templatetags/cofcms_tags.py | 2 +- gestioncof/forms.py | 2 +- gestioncof/models.py | 2 +- gestioncof/signals.py | 2 +- gestioncof/views.py | 2 +- kfet/auth/apps.py | 2 +- kfet/auth/forms.py | 2 +- kfet/auth/models.py | 2 +- kfet/auth/signals.py | 2 +- kfet/auth/views.py | 2 +- kfet/cms/models.py | 2 +- kfet/forms.py | 2 +- kfet/models.py | 2 +- petitscours/models.py | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/gestioncof/admin.py b/gestioncof/admin.py index 89e4160d..bb90cf98 100644 --- a/gestioncof/admin.py +++ b/gestioncof/admin.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import Group, Permission, User from django.db.models import Q from django.urls import reverse from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from gestioncof.models import ( Club, diff --git a/gestioncof/cms/templatetags/cofcms_tags.py b/gestioncof/cms/templatetags/cofcms_tags.py index f9e62aed..36774232 100644 --- a/gestioncof/cms/templatetags/cofcms_tags.py +++ b/gestioncof/cms/templatetags/cofcms_tags.py @@ -2,7 +2,7 @@ from datetime import date, timedelta from django import template from django.utils import formats, timezone -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from ..models import COFActuPage, COFRootPage diff --git a/gestioncof/forms.py b/gestioncof/forms.py index 983bb249..1d482e7f 100644 --- a/gestioncof/forms.py +++ b/gestioncof/forms.py @@ -3,7 +3,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.forms import AuthenticationForm from django.forms.formsets import BaseFormSet, formset_factory from django.forms.widgets import CheckboxSelectMultiple, RadioSelect -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from djconfig.forms import ConfigForm from bda.models import Spectacle diff --git a/gestioncof/models.py b/gestioncof/models.py index c2c71660..dad77c34 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -2,7 +2,7 @@ from django.contrib.auth.models import User from django.db import models from django.db.models.signals import post_delete, post_save from django.dispatch import receiver -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from bda.models import Spectacle from shared.utils import choices_length diff --git a/gestioncof/signals.py b/gestioncof/signals.py index cf4b1f16..deed6ce9 100644 --- a/gestioncof/signals.py +++ b/gestioncof/signals.py @@ -1,7 +1,7 @@ from django.contrib import messages from django.contrib.auth.signals import user_logged_in from django.dispatch import receiver -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django_cas_ng.signals import cas_user_authenticated diff --git a/gestioncof/views.py b/gestioncof/views.py index fbe74ec7..f7c76f3c 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -20,7 +20,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.template import loader from django.urls import reverse_lazy from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.views.generic import FormView, TemplateView from django_cas_ng.views import LogoutView as CasLogoutView from icalendar import Calendar, Event as Vevent diff --git a/kfet/auth/apps.py b/kfet/auth/apps.py index 5b4fe7fd..49e19a26 100644 --- a/kfet/auth/apps.py +++ b/kfet/auth/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig from django.db.models.signals import post_migrate -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class KFetAuthConfig(AppConfig): diff --git a/kfet/auth/forms.py b/kfet/auth/forms.py index 33f5a260..74823364 100644 --- a/kfet/auth/forms.py +++ b/kfet/auth/forms.py @@ -1,5 +1,5 @@ from django.contrib.auth.models import User -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from shared.forms import ProtectedModelForm diff --git a/kfet/auth/models.py b/kfet/auth/models.py index 865852cd..70e2de51 100644 --- a/kfet/auth/models.py +++ b/kfet/auth/models.py @@ -1,7 +1,7 @@ from django.contrib.auth.models import Group, Permission from django.db import models from django.utils.crypto import get_random_string -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ KFET_APP_LABELS = ["kfet", "kfetauth"] diff --git a/kfet/auth/signals.py b/kfet/auth/signals.py index 3f691068..b1e3fc57 100644 --- a/kfet/auth/signals.py +++ b/kfet/auth/signals.py @@ -3,7 +3,7 @@ from django.contrib.auth.signals import user_logged_in from django.dispatch import receiver from django.urls import reverse from django.utils.safestring import mark_safe -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext_lazy as _ from .utils import get_kfet_generic_user diff --git a/kfet/auth/views.py b/kfet/auth/views.py index f57e8415..15519caa 100644 --- a/kfet/auth/views.py +++ b/kfet/auth/views.py @@ -9,7 +9,7 @@ from django.http import QueryDict from django.shortcuts import redirect, render from django.urls import reverse, reverse_lazy from django.utils.decorators import method_decorator -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.views.decorators.http import require_http_methods from django.views.generic import View from django.views.generic.edit import CreateView, UpdateView diff --git a/kfet/cms/models.py b/kfet/cms/models.py index b9061b21..019e53fa 100644 --- a/kfet/cms/models.py +++ b/kfet/cms/models.py @@ -1,5 +1,5 @@ from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from wagtail.admin.edit_handlers import ( FieldPanel, FieldRowPanel, diff --git a/kfet/forms.py b/kfet/forms.py index 8ab301a1..ae044535 100644 --- a/kfet/forms.py +++ b/kfet/forms.py @@ -8,7 +8,7 @@ from django.core import validators from django.core.exceptions import ValidationError from django.forms import modelformset_factory from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from djconfig.forms import ConfigForm from gestioncof.models import CofProfile diff --git a/kfet/models.py b/kfet/models.py index 7a0ec777..1ce1a9e0 100644 --- a/kfet/models.py +++ b/kfet/models.py @@ -9,7 +9,7 @@ from django.db.models import F from django.template import loader from django.urls import reverse from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from gestioncof.models import CofProfile from shared.utils import choices_length diff --git a/petitscours/models.py b/petitscours/models.py index 8e5d4884..e5ee749a 100644 --- a/petitscours/models.py +++ b/petitscours/models.py @@ -2,7 +2,7 @@ from django.contrib.auth.models import User from django.db import models from django.db.models import Min from django.utils.functional import cached_property -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from shared.utils import choices_length -- 2.45.1 From b512ba2d5818c42d50db3c2c6df23cf8b4aec820 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 16:10:11 +0200 Subject: [PATCH 17/40] Change backend name as the old one is deprecated --- gestioasso/settings/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gestioasso/settings/common.py b/gestioasso/settings/common.py index 44a1592b..b96f637e 100644 --- a/gestioasso/settings/common.py +++ b/gestioasso/settings/common.py @@ -101,7 +101,7 @@ TEMPLATES = [ DATABASES = { "default": { - "ENGINE": "django.db.backends.postgresql_psycopg2", + "ENGINE": "django.db.backends.postgresql", "NAME": DBNAME, "USER": DBUSER, "PASSWORD": DBPASSWD, -- 2.45.1 From 6e4895fb162d27c68bb471923f1ed195482440ae Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 16:10:24 +0200 Subject: [PATCH 18/40] Update django-djconfig --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9e0b8b8e..6d09eac0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ configparser==3.5.0 Django==3.2.* django-autocomplete-light==3.9.* django-cas-ng==4.3.* -django-djconfig==0.8.0 +django-djconfig==0.10.0 django-hCaptcha==0.1.0 icalendar Pillow -- 2.45.1 From aeb90f729b54e153f86e595c816e2b803c04e3dd Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 29 Jun 2022 16:16:54 +0200 Subject: [PATCH 19/40] Remove default_app_config --- bds/__init__.py | 1 - gestioncof/__init__.py | 1 - gestioncof/cms/__init__.py | 1 - kfet/__init__.py | 1 - kfet/auth/__init__.py | 2 -- kfet/cms/__init__.py | 1 - 6 files changed, 7 deletions(-) diff --git a/bds/__init__.py b/bds/__init__.py index 5c287005..e69de29b 100644 --- a/bds/__init__.py +++ b/bds/__init__.py @@ -1 +0,0 @@ -default_app_config = "bds.apps.BdsConfig" diff --git a/gestioncof/__init__.py b/gestioncof/__init__.py index 3bb260b9..e69de29b 100644 --- a/gestioncof/__init__.py +++ b/gestioncof/__init__.py @@ -1 +0,0 @@ -default_app_config = "gestioncof.apps.GestioncofConfig" diff --git a/gestioncof/cms/__init__.py b/gestioncof/cms/__init__.py index 043b644d..e69de29b 100644 --- a/gestioncof/cms/__init__.py +++ b/gestioncof/cms/__init__.py @@ -1 +0,0 @@ -default_app_config = "gestioncof.cms.apps.COFCMSAppConfig" diff --git a/kfet/__init__.py b/kfet/__init__.py index 47a6b0b8..4937ccfa 100644 --- a/kfet/__init__.py +++ b/kfet/__init__.py @@ -1,3 +1,2 @@ -default_app_config = "kfet.apps.KFetConfig" KFET_DELETED_TRIGRAMME = "☠☠☠" KFET_DELETED_USERNAME = "kfet_deleted_user" diff --git a/kfet/auth/__init__.py b/kfet/auth/__init__.py index ef2486a7..2b30780e 100644 --- a/kfet/auth/__init__.py +++ b/kfet/auth/__init__.py @@ -1,4 +1,2 @@ -default_app_config = "kfet.auth.apps.KFetAuthConfig" - KFET_GENERIC_USERNAME = "kfet_genericteam" KFET_GENERIC_TRIGRAMME = "GNR" diff --git a/kfet/cms/__init__.py b/kfet/cms/__init__.py index f6aabddc..e69de29b 100644 --- a/kfet/cms/__init__.py +++ b/kfet/cms/__init__.py @@ -1 +0,0 @@ -default_app_config = "kfet.cms.apps.KFetCMSAppConfig" -- 2.45.1 From 9e1e13d3600ff921e6b374b4301dd75c72efb29a Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 09:17:29 +0200 Subject: [PATCH 20/40] Update kfet.open tests --- kfet/open/tests.py | 173 +++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 92 deletions(-) diff --git a/kfet/open/tests.py b/kfet/open/tests.py index 2c3f9c4e..9eb07697 100644 --- a/kfet/open/tests.py +++ b/kfet/open/tests.py @@ -2,7 +2,7 @@ import random from datetime import timedelta from unittest import mock -from asgiref.sync import async_to_sync, sync_to_async +from asgiref.sync import async_to_sync from channels.auth import AuthMiddlewareStack from channels.consumer import get_channel_layer from channels.testing import WebsocketCommunicator @@ -193,16 +193,16 @@ class OpenKfetViewsTest(TestCase): class OpenKfetConsumerTest(TestCase): """OpenKfet consumer unit-tests suite.""" - def setUp(self): + @classmethod + def setUpTestData(cls): t = User.objects.create_user("team", "", "team") is_team = Permission.objects.get( codename="is_team", content_type__app_label="kfet" ) t.user_permissions.add(is_team) - self.team_user = t + cls.team_user = t - @async_to_sync async def test_standard_user(self): """Lambda user is added to kfet.open.base group.""" # setup anonymous client @@ -268,35 +268,97 @@ class OpenKfetConsumerTest(TestCase): class OpenKfetScenarioTest(TestCase): """OpenKfet functionnal tests suite.""" - def setUp(self): - # Need this (and here) because of '.login' in setUp - patcher_messages = mock.patch("gestioncof.signals.messages") - patcher_messages.start() - self.addCleanup(patcher_messages.stop) + @classmethod + def setUpTestData(cls): + # root user + cls.r = User.objects.create_superuser("team", "", "team") # anonymous client (for views) - self.c = Client() - - # root user - self.r = User.objects.create_superuser("root", "", "root") + cls.c = Client() # root client - self.r_c = Client() - self.r_c.login(username="root", password="root") + cls.r_c = Client() + + with mock.patch("gestioncof.signals.messages"): + cls.r_c.login(username="team", password="team") + + def setUp(self): + # Create a channel to listen to KPsul's messages + channel_layer = get_channel_layer() + self.channel = async_to_sync(channel_layer.new_channel)() + self.team_channel = async_to_sync(channel_layer.new_channel)() + + async_to_sync(channel_layer.group_add)("kfet.open.base", self.channel) + async_to_sync(channel_layer.group_add)("kfet.open.team", self.team_channel) + + self.receive_msg = lambda c: async_to_sync(channel_layer.receive)(c) self.kfet_open = OpenKfet( cache_prefix="test_kfetopen_%s" % random.randrange(2**20) ) self.addCleanup(self.kfet_open.clear_cache) - async def ws_connect(self, ws_client): - c, _ = await ws_client.connect() + async def ws_connect(self, ws_communicator): + c, _ = await ws_communicator.connect() self.assertTrue(c) + return await ws_communicator.receive_json_from() - return await ws_client.receive_json_from() + def test_scenario_1(self): + """Clients connect, door opens, enable force close.""" - async def test_scenario_0(self): + # door sent "I'm open!" + self.c.post("/k-fet/open/raw_open", {"raw_open": True, "token": "plop"}) + + # anonymous user agree + msg = self.receive_msg(self.channel) + self.assertEqual(OpenKfet.OPENED, msg["status"]) + + # root user too + msg = self.receive_msg(self.team_channel) + self.assertEqual(OpenKfet.OPENED, msg["status"]) + self.assertEqual(OpenKfet.OPENED, msg["admin_status"]) + + # admin says "no it's closed" + self.r_c.post("/k-fet/open/force_close", {"force_close": True}) + + # so anonymous user see it's closed + msg = self.receive_msg(self.channel) + self.assertEqual(OpenKfet.CLOSED, msg["status"]) + + # root user too + msg = self.receive_msg(self.team_channel) + self.assertEqual(OpenKfet.CLOSED, msg["status"]) + # but root knows things + self.assertEqual(OpenKfet.FAKE_CLOSED, msg["admin_status"]) + self.assertTrue(msg["force_close"]) + + def test_scenario_2(self): + """Starting falsely closed, clients connect, disable force close.""" + self.kfet_open.raw_open = True + self.kfet_open.force_close = True + + async_to_sync(OpenKfet().send_ws)() + + msg = self.receive_msg(self.channel) + self.assertEqual(OpenKfet.CLOSED, msg["status"]) + + msg = self.receive_msg(self.team_channel) + self.assertEqual(OpenKfet.CLOSED, msg["status"]) + self.assertEqual(OpenKfet.FAKE_CLOSED, msg["admin_status"]) + self.assertTrue(msg["force_close"]) + + self.r_c.post("/k-fet/open/force_close", {"force_close": False}) + + msg = self.receive_msg(self.channel) + self.assertEqual(OpenKfet.OPENED, msg["status"]) + + msg = self.receive_msg(self.team_channel) + self.assertEqual(OpenKfet.OPENED, msg["status"]) + self.assertEqual(OpenKfet.OPENED, msg["admin_status"]) + self.assertFalse(msg["force_close"]) + + async def test_scenario_3(self): """Clients connect.""" # anonymous client (for websockets) self.c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") @@ -315,76 +377,3 @@ class OpenKfetScenarioTest(TestCase): self.assertSetEqual( set(["status", "admin_status", "force_close"]), set(msg) ) - - async def test_scenario_1(self): - """Clients connect, door opens, enable force close.""" - - self.c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") - await self.ws_connect(self.c_ws) - - with mock.patch( - "kfet.open.consumers.kfet_is_team", return_value=True - ), mock.patch("kfet.open.open.kfet_is_team", return_value=True): - self.r_c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") - await self.ws_connect(self.r_c_ws) - - # door sent "I'm open!" - await sync_to_async(self.c.post)( - "/k-fet/open/raw_open", {"raw_open": True, "token": "plop"} - ) - - # anonymous user agree - msg = await self.c_ws.receive_json_from() - self.assertEqual(OpenKfet.OPENED, msg["status"]) - - # root user too - msg = await self.r_c_ws.receive_json_from() - self.assertEqual(OpenKfet.OPENED, msg["status"]) - self.assertEqual(OpenKfet.OPENED, msg["admin_status"]) - - # admin says "no it's closed" - await sync_to_async(self.r_c.post)( - "/k-fet/open/force_close", {"force_close": True} - ) - - # so anonymous user see it's closed - msg = await self.c_ws.receive_json_from() - self.assertEqual(OpenKfet.CLOSED, msg["status"]) - - # root user too - msg = await self.r_c_ws.receive_json_from() - self.assertEqual(OpenKfet.CLOSED, msg["status"]) - # but root knows things - self.assertEqual(OpenKfet.FAKE_CLOSED, msg["admin_status"]) - self.assertTrue(msg["force_close"]) - - async def test_scenario_2(self): - """Starting falsely closed, clients connect, disable force close.""" - self.kfet_open.raw_open = True - self.kfet_open.force_close = True - - self.c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") - - msg = await self.ws_connect(self.c_ws) - self.assertEqual(OpenKfet.CLOSED, msg["status"]) - - with mock.patch( - "kfet.open.consumers.kfet_is_team", return_value=True - ), mock.patch("kfet.open.open.kfet_is_team", return_value=True): - self.r_c_ws = ws_communicator(OpenKfetConsumer, "/ws/k-fet/open") - msg = await self.ws_connect(self.r_c_ws) - self.assertEqual(OpenKfet.CLOSED, msg["status"]) - self.assertEqual(OpenKfet.FAKE_CLOSED, msg["admin_status"]) - self.assertTrue(msg["force_close"]) - - await sync_to_async(self.r_c.post)( - "/k-fet/open/force_close", {"force_close": False} - ) - - msg = await self.c_ws.receive_json_from() - self.assertEqual(OpenKfet.OPENED, msg["status"]) - - msg = await self.r_c_ws.receive_json_from() - self.assertEqual(OpenKfet.OPENED, msg["status"]) - self.assertEqual(OpenKfet.OPENED, msg["admin_status"]) - self.assertFalse(msg["force_close"]) -- 2.45.1 From 67e921a49700d07ac7abb6004b1cfb8e56bdfe09 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 09:39:05 +0200 Subject: [PATCH 21/40] Try to flush old messages in tests --- kfet/open/tests.py | 4 +++- kfet/tests/test_views.py | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/kfet/open/tests.py b/kfet/open/tests.py index 9eb07697..a30ecb99 100644 --- a/kfet/open/tests.py +++ b/kfet/open/tests.py @@ -283,8 +283,10 @@ class OpenKfetScenarioTest(TestCase): cls.r_c.login(username="team", password="team") def setUp(self): - # Create a channel to listen to KPsul's messages + # Create channels to listen to messages channel_layer = get_channel_layer() + async_to_sync(channel_layer.flush)() + self.channel = async_to_sync(channel_layer.new_channel)() self.team_channel = async_to_sync(channel_layer.new_channel)() diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py index 39ec0aa7..153bb1b5 100644 --- a/kfet/tests/test_views.py +++ b/kfet/tests/test_views.py @@ -1812,6 +1812,7 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): # Create a channel to listen to KPsul's messages channel_layer = get_channel_layer() + async_to_sync(channel_layer.flush)() self.channel = async_to_sync(channel_layer.new_channel)() async_to_sync(channel_layer.group_add)("kfet.kpsul", self.channel) @@ -3240,6 +3241,9 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): # Create a channel to listen to KPsul's messages channel_layer = get_channel_layer() + + # Flush old messages + async_to_sync(channel_layer.flush)() self.channel = async_to_sync(channel_layer.new_channel)() async_to_sync(channel_layer.group_add)("kfet.kpsul", self.channel) -- 2.45.1 From 16f500838f01e4c8a4fcbb1e591a0a8d0c9b1d0c Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 09:54:31 +0200 Subject: [PATCH 22/40] Try not running tests in parallel --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b0c1f4c6..5db3578d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,7 +49,7 @@ coftest: variables: DJANGO_SETTINGS_MODULE: "gestioasso.settings.cof_prod" script: - - coverage run manage.py test gestioncof bda kfet petitscours shared --parallel + - coverage run manage.py test gestioncof bda kfet petitscours shared bdstest: stage: test -- 2.45.1 From 48dd2571bc9d757414c6a50a37ac309b8993bc68 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 10:00:29 +0200 Subject: [PATCH 23/40] Only run kf tests in sequential mode (to fix issues with channels) --- .gitlab-ci.yml | 10 +++++++++- kfet/open/tests.py | 1 - kfet/tests/test_views.py | 3 --- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5db3578d..f563e22c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,13 +43,21 @@ variables: # Keep this disabled for now, as it may kill GitLab... # coverage: '/TOTAL.*\s(\d+\.\d+)\%$/' +kfettest: + stage: test + extends: .test_template + variables: + DJANGO_SETTINGS_MODULE: "gestioasso.settings.cof_prod" + script: + - coverage run manage.py kfet + coftest: stage: test extends: .test_template variables: DJANGO_SETTINGS_MODULE: "gestioasso.settings.cof_prod" script: - - coverage run manage.py test gestioncof bda kfet petitscours shared + - coverage run manage.py test gestioncof bda petitscours shared --parallel bdstest: stage: test diff --git a/kfet/open/tests.py b/kfet/open/tests.py index a30ecb99..295a8cd7 100644 --- a/kfet/open/tests.py +++ b/kfet/open/tests.py @@ -285,7 +285,6 @@ class OpenKfetScenarioTest(TestCase): def setUp(self): # Create channels to listen to messages channel_layer = get_channel_layer() - async_to_sync(channel_layer.flush)() self.channel = async_to_sync(channel_layer.new_channel)() self.team_channel = async_to_sync(channel_layer.new_channel)() diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py index 153bb1b5..d7df91ad 100644 --- a/kfet/tests/test_views.py +++ b/kfet/tests/test_views.py @@ -1812,7 +1812,6 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): # Create a channel to listen to KPsul's messages channel_layer = get_channel_layer() - async_to_sync(channel_layer.flush)() self.channel = async_to_sync(channel_layer.new_channel)() async_to_sync(channel_layer.group_add)("kfet.kpsul", self.channel) @@ -3242,8 +3241,6 @@ class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): # Create a channel to listen to KPsul's messages channel_layer = get_channel_layer() - # Flush old messages - async_to_sync(channel_layer.flush)() self.channel = async_to_sync(channel_layer.new_channel)() async_to_sync(channel_layer.group_add)("kfet.kpsul", self.channel) -- 2.45.1 From 14eb74017909c6c369d9b58f0950f9e7590255e8 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 10:05:56 +0200 Subject: [PATCH 24/40] Fix typo --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f563e22c..ce3bd041 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,7 +49,7 @@ kfettest: variables: DJANGO_SETTINGS_MODULE: "gestioasso.settings.cof_prod" script: - - coverage run manage.py kfet + - coverage run manage.py test kfet coftest: stage: test -- 2.45.1 From 8d655f8b2f8a22f3214376a030cca13f25086c45 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 10:15:51 +0200 Subject: [PATCH 25/40] cof has been renamed to gestioasso --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 8aa73856..9b1c72d0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,8 +3,8 @@ source = bda bds clubs - cof events + gestioasso gestioncof kfet petitscours -- 2.45.1 From 1e5d30737fa69d7eda4e03a30294d652aeba5a8f Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 10:16:12 +0200 Subject: [PATCH 26/40] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b6860..42ab598f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ adhérents ni des cotisations. ## TODO Prod +- Lancer `python manage.py update_translation_fields` après la migration +- Mettre à jour les units systemd `daphne.service` et `worker.service` + - Créer un compte hCaptcha (https://www.hcaptcha.com/), au COF, et remplacer les secrets associés ## Version ??? - ??/??/???? @@ -31,6 +34,8 @@ adhérents ni des cotisations. - Fixe un problème de rendu causé par l'agrandissement du menu +- Mise à jour vers Channels 3.x et Django 3.2 + ## Version 0.12 - 17/06/2022 ### K-Fêt -- 2.45.1 From 6be970b230338421de1732a24bcdd16dfd3e9e55 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 10:38:25 +0200 Subject: [PATCH 27/40] Replace unique_together by UniqueConstraint --- bda/migrations/0020_auto_20220630_1035.py | 23 +++++++ bda/models.py | 6 +- events/migrations/0006_auto_20220630_1035.py | 63 +++++++++++++++++++ events/models.py | 28 +++++++-- .../migrations/0020_auto_20220630_1036.py | 43 +++++++++++++ gestioncof/models.py | 12 +++- petitscours/models.py | 6 +- 7 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 bda/migrations/0020_auto_20220630_1035.py create mode 100644 events/migrations/0006_auto_20220630_1035.py create mode 100644 gestioncof/migrations/0020_auto_20220630_1036.py diff --git a/bda/migrations/0020_auto_20220630_1035.py b/bda/migrations/0020_auto_20220630_1035.py new file mode 100644 index 00000000..43075bfe --- /dev/null +++ b/bda/migrations/0020_auto_20220630_1035.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.13 on 2022-06-30 08:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bda", "0019_auto_20220628_1621"), + ] + + operations = [ + migrations.AlterUniqueTogether( + name="choixspectacle", + unique_together=set(), + ), + migrations.AddConstraint( + model_name="choixspectacle", + constraint=models.UniqueConstraint( + fields=("participant", "spectacle"), name="unique_participation" + ), + ), + ] diff --git a/bda/models.py b/bda/models.py index 578f235c..9bc2ce3c 100644 --- a/bda/models.py +++ b/bda/models.py @@ -253,7 +253,11 @@ class ChoixSpectacle(models.Model): class Meta: ordering = ("priority",) - unique_together = (("participant", "spectacle"),) + constraints = [ + models.UniqueConstraint( + fields=["participant", "spectacle"], name="unique_participation" + ) + ] verbose_name = "voeu" verbose_name_plural = "voeux" diff --git a/events/migrations/0006_auto_20220630_1035.py b/events/migrations/0006_auto_20220630_1035.py new file mode 100644 index 00000000..f6cffcac --- /dev/null +++ b/events/migrations/0006_auto_20220630_1035.py @@ -0,0 +1,63 @@ +# Generated by Django 3.2.13 on 2022-06-30 08:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("events", "0005_auto_20220628_1621"), + ] + + operations = [ + migrations.AlterUniqueTogether( + name="extrafield", + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name="extrafieldcontent", + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name="option", + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name="optionchoice", + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name="registration", + unique_together=set(), + ), + migrations.AddConstraint( + model_name="extrafield", + constraint=models.UniqueConstraint( + fields=("event", "name"), name="unique_extra_field" + ), + ), + migrations.AddConstraint( + model_name="extrafieldcontent", + constraint=models.UniqueConstraint( + fields=("field", "registration"), name="unique_extra_field_content" + ), + ), + migrations.AddConstraint( + model_name="option", + constraint=models.UniqueConstraint( + fields=("event", "name"), name="unique_event_option" + ), + ), + migrations.AddConstraint( + model_name="optionchoice", + constraint=models.UniqueConstraint( + fields=("option", "choice"), name="unique_option_choice" + ), + ), + migrations.AddConstraint( + model_name="registration", + constraint=models.UniqueConstraint( + fields=("event", "user"), name="unique_registration" + ), + ), + ] diff --git a/events/models.py b/events/models.py index 7b536c86..a421e8a3 100644 --- a/events/models.py +++ b/events/models.py @@ -72,9 +72,13 @@ class Option(models.Model): multi_choices = models.BooleanField(_("choix multiples"), default=False) class Meta: + constraints = [ + models.UniqueConstraint( + fields=["event", "name"], name="unique_event_option" + ) + ] verbose_name = _("option d'événement") verbose_name_plural = _("options d'événement") - unique_together = [["event", "name"]] def __str__(self): return self.name @@ -87,9 +91,13 @@ class OptionChoice(models.Model): choice = models.CharField(_("choix"), max_length=200) class Meta: + constraints = [ + models.UniqueConstraint( + fields=["option", "choice"], name="unique_option_choice" + ) + ] verbose_name = _("choix d'option d'événement") verbose_name_plural = _("choix d'option d'événement") - unique_together = [["option", "choice"]] def __str__(self): return self.choice @@ -118,7 +126,9 @@ class ExtraField(models.Model): field_type = models.CharField(_("type de champ"), max_length=9, choices=FIELD_TYPE) class Meta: - unique_together = [["event", "name"]] + constraints = [ + models.UniqueConstraint(fields=["event", "name"], name="unique_extra_field") + ] class ExtraFieldContent(models.Model): @@ -137,9 +147,13 @@ class ExtraFieldContent(models.Model): ) class Meta: + constraints = [ + models.UniqueConstraint( + fields=["field", "registration"], name="unique_extra_field_content" + ) + ] verbose_name = _("contenu d'un champ événement supplémentaire") verbose_name_plural = _("contenus d'un champ événement supplémentaire") - unique_together = [["field", "registration"]] def __str__(self): max_length = 50 @@ -163,9 +177,13 @@ class Registration(models.Model): ) class Meta: + constraints = [ + models.UniqueConstraint( + fields=["event", "user"], name="unique_registration" + ) + ] verbose_name = _("inscription à un événement") verbose_name_plural = _("inscriptions à un événement") - unique_together = [["event", "user"]] def __str__(self): return "inscription de {} à {}".format(self.user, self.event) diff --git a/gestioncof/migrations/0020_auto_20220630_1036.py b/gestioncof/migrations/0020_auto_20220630_1036.py new file mode 100644 index 00000000..2085f329 --- /dev/null +++ b/gestioncof/migrations/0020_auto_20220630_1036.py @@ -0,0 +1,43 @@ +# Generated by Django 3.2.13 on 2022-06-30 08:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("gestioncof", "0019_auto_20220628_1621"), + ] + + operations = [ + migrations.AlterUniqueTogether( + name="eventregistration", + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name="petitcoursability", + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name="surveyanswer", + unique_together=set(), + ), + migrations.AddConstraint( + model_name="eventregistration", + constraint=models.UniqueConstraint( + fields=("user", "event"), name="unique_event_registration" + ), + ), + migrations.AddConstraint( + model_name="petitcoursability", + constraint=models.UniqueConstraint( + fields=("user", "niveau", "matiere"), name="unique_competence_level" + ), + ), + migrations.AddConstraint( + model_name="surveyanswer", + constraint=models.UniqueConstraint( + fields=("user", "survey"), name="unique_survey_answer" + ), + ), + ] diff --git a/gestioncof/models.py b/gestioncof/models.py index dad77c34..e8f6fc57 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -193,8 +193,12 @@ class EventRegistration(models.Model): paid = models.BooleanField("A payé", default=False) class Meta: + constraints = [ + models.UniqueConstraint( + fields=["user", "event"], name="unique_event_registration" + ) + ] verbose_name = "Inscription" - unique_together = ("user", "event") def __str__(self): return "Inscription de {} à {}".format(self.user, self.event.title) @@ -246,8 +250,12 @@ class SurveyAnswer(models.Model): answers = models.ManyToManyField(SurveyQuestionAnswer, related_name="selected_by") class Meta: + constraints = [ + models.UniqueConstraint( + fields=["user", "survey"], name="unique_survey_answer" + ) + ] verbose_name = "Réponses" - unique_together = ("user", "survey") def __str__(self): return "Réponse de %s sondage %s" % ( diff --git a/petitscours/models.py b/petitscours/models.py index e5ee749a..0be81449 100644 --- a/petitscours/models.py +++ b/petitscours/models.py @@ -44,9 +44,13 @@ class PetitCoursAbility(models.Model): class Meta: app_label = "gestioncof" + constraints = [ + models.UniqueConstraint( + fields=["user", "niveau", "matiere"], name="unique_competence_level" + ) + ] verbose_name = "Compétence petits cours" verbose_name_plural = "Compétences des petits cours" - unique_together = ("user", "niveau", "matiere") def __str__(self): return "{:s} - {!s} - {:s}".format( -- 2.45.1 From 29726486e0a2df9da3fafaeea9b0d54931b2bad2 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 11:01:17 +0200 Subject: [PATCH 28/40] url -> re_path --- bda/urls.py | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/bda/urls.py b/bda/urls.py index 5b452362..726c4057 100644 --- a/bda/urls.py +++ b/bda/urls.py @@ -1,74 +1,80 @@ -from django.conf.urls import url +from django.urls import re_path from bda import views from bda.views import SpectacleListView from gestioncof.decorators import buro_required urlpatterns = [ - url( + re_path( r"^inscription/(?P\d+)$", views.inscription, name="bda-tirage-inscription", ), - url(r"^places/(?P\d+)$", views.places, name="bda-places-attribuees"), - url(r"^etat-places/(?P\d+)$", views.etat_places, name="bda-etat-places"), - url(r"^tirage/(?P\d+)$", views.tirage, name="bda-tirage"), - url( + re_path(r"^places/(?P\d+)$", views.places, name="bda-places-attribuees"), + re_path( + r"^etat-places/(?P\d+)$", views.etat_places, name="bda-etat-places" + ), + re_path(r"^tirage/(?P\d+)$", views.tirage, name="bda-tirage"), + re_path( r"^spectacles/(?P\d+)$", buro_required(SpectacleListView.as_view()), name="bda-liste-spectacles", ), - url( + re_path( r"^spectacles/(?P\d+)/(?P\d+)$", views.spectacle, name="bda-spectacle", ), - url( + re_path( r"^spectacles/unpaid/(?P\d+)$", views.UnpaidParticipants.as_view(), name="bda-unpaid", ), - url( + re_path( r"^spectacles/autocomplete$", views.spectacle_autocomplete, name="bda-spectacle-autocomplete", ), - url( + re_path( r"^participants/autocomplete$", views.participant_autocomplete, name="bda-participant-autocomplete", ), # Urls BdA-Revente - url( + re_path( r"^revente/(?P\d+)/manage$", views.revente_manage, name="bda-revente-manage", ), - url( + re_path( r"^revente/(?P\d+)/subscribe$", views.revente_subscribe, name="bda-revente-subscribe", ), - url( + re_path( r"^revente/(?P\d+)/tirages$", views.revente_tirages, name="bda-revente-tirages", ), - url( + re_path( r"^revente/(?P\d+)/buy$", views.revente_buy, name="bda-revente-buy", ), - url( + re_path( r"^revente/(?P\d+)/confirm$", views.revente_confirm, name="bda-revente-confirm", ), - url( + re_path( r"^revente/(?P\d+)/shotgun$", views.revente_shotgun, name="bda-revente-shotgun", ), - url(r"^mails-rappel/(?P\d+)$", views.send_rappel, name="bda-rappels"), - url(r"^catalogue/(?P[a-z]+)$", views.catalogue, name="bda-catalogue"), + re_path( + r"^mails-rappel/(?P\d+)$", views.send_rappel, name="bda-rappels" + ), + re_path( + r"^catalogue/(?P[a-z]+)$", views.catalogue, name="bda-catalogue" + ), ] -- 2.45.1 From e51951b1f2f7b0d52ec3518afacf1fc0021f5043 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 11:04:12 +0200 Subject: [PATCH 29/40] Use AutoField instead of BigAutoField --- bda/migrations/0021_auto_20220630_1103.py | 76 ++++++++++ bds/migrations/0008_alter_bdsprofile_id.py | 20 +++ clubs/migrations/0003_alter_club_id.py | 20 +++ events/migrations/0007_auto_20220630_1103.py | 55 +++++++ gestioasso/settings/common.py | 2 +- .../migrations/0021_auto_20220630_1103.py | 139 ++++++++++++++++++ .../0005_alter_genericteamtoken_id.py | 20 +++ .../migrations/0004_alter_memberteam_id.py | 20 +++ kfet/migrations/0082_auto_20220630_1103.py | 125 ++++++++++++++++ 9 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 bda/migrations/0021_auto_20220630_1103.py create mode 100644 bds/migrations/0008_alter_bdsprofile_id.py create mode 100644 clubs/migrations/0003_alter_club_id.py create mode 100644 events/migrations/0007_auto_20220630_1103.py create mode 100644 gestioncof/migrations/0021_auto_20220630_1103.py create mode 100644 kfet/auth/migrations/0005_alter_genericteamtoken_id.py create mode 100644 kfet/cms/migrations/0004_alter_memberteam_id.py create mode 100644 kfet/migrations/0082_auto_20220630_1103.py diff --git a/bda/migrations/0021_auto_20220630_1103.py b/bda/migrations/0021_auto_20220630_1103.py new file mode 100644 index 00000000..df0aea3c --- /dev/null +++ b/bda/migrations/0021_auto_20220630_1103.py @@ -0,0 +1,76 @@ +# Generated by Django 3.2.13 on 2022-06-30 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bda", "0020_auto_20220630_1035"), + ] + + operations = [ + migrations.AlterField( + model_name="attribution", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="categoriespectacle", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="choixspectacle", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="participant", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="quote", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="salle", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="spectacle", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="spectaclerevente", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="tirage", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/bds/migrations/0008_alter_bdsprofile_id.py b/bds/migrations/0008_alter_bdsprofile_id.py new file mode 100644 index 00000000..89c74d4e --- /dev/null +++ b/bds/migrations/0008_alter_bdsprofile_id.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2022-06-30 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bds", "0007_alter_bdsprofile_id"), + ] + + operations = [ + migrations.AlterField( + model_name="bdsprofile", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/clubs/migrations/0003_alter_club_id.py b/clubs/migrations/0003_alter_club_id.py new file mode 100644 index 00000000..92819ce7 --- /dev/null +++ b/clubs/migrations/0003_alter_club_id.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2022-06-30 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("clubs", "0002_alter_club_id"), + ] + + operations = [ + migrations.AlterField( + model_name="club", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/events/migrations/0007_auto_20220630_1103.py b/events/migrations/0007_auto_20220630_1103.py new file mode 100644 index 00000000..6fc3502d --- /dev/null +++ b/events/migrations/0007_auto_20220630_1103.py @@ -0,0 +1,55 @@ +# Generated by Django 3.2.13 on 2022-06-30 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("events", "0006_auto_20220630_1035"), + ] + + operations = [ + migrations.AlterField( + model_name="event", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="extrafield", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="extrafieldcontent", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="option", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="optionchoice", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="registration", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/gestioasso/settings/common.py b/gestioasso/settings/common.py index b96f637e..13f2e5b1 100644 --- a/gestioasso/settings/common.py +++ b/gestioasso/settings/common.py @@ -111,7 +111,7 @@ DATABASES = { SITE_ID = 1 -DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" # --- # Internationalization diff --git a/gestioncof/migrations/0021_auto_20220630_1103.py b/gestioncof/migrations/0021_auto_20220630_1103.py new file mode 100644 index 00000000..3f1d7dcc --- /dev/null +++ b/gestioncof/migrations/0021_auto_20220630_1103.py @@ -0,0 +1,139 @@ +# Generated by Django 3.2.13 on 2022-06-30 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("gestioncof", "0020_auto_20220630_1036"), + ] + + operations = [ + migrations.AlterField( + model_name="calendarsubscription", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="club", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="cofprofile", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="event", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventcommentfield", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventcommentvalue", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventoption", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventoptionchoice", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="eventregistration", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcoursability", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcoursattribution", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcoursattributioncounter", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcoursdemande", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="petitcourssubject", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="survey", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="surveyanswer", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="surveyquestion", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="surveyquestionanswer", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/kfet/auth/migrations/0005_alter_genericteamtoken_id.py b/kfet/auth/migrations/0005_alter_genericteamtoken_id.py new file mode 100644 index 00000000..b6be0a0c --- /dev/null +++ b/kfet/auth/migrations/0005_alter_genericteamtoken_id.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2022-06-30 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("kfetauth", "0004_alter_genericteamtoken_id"), + ] + + operations = [ + migrations.AlterField( + model_name="genericteamtoken", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/kfet/cms/migrations/0004_alter_memberteam_id.py b/kfet/cms/migrations/0004_alter_memberteam_id.py new file mode 100644 index 00000000..2ca24156 --- /dev/null +++ b/kfet/cms/migrations/0004_alter_memberteam_id.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2022-06-30 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("kfetcms", "0003_alter_memberteam_id"), + ] + + operations = [ + migrations.AlterField( + model_name="memberteam", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] diff --git a/kfet/migrations/0082_auto_20220630_1103.py b/kfet/migrations/0082_auto_20220630_1103.py new file mode 100644 index 00000000..3be2cfe1 --- /dev/null +++ b/kfet/migrations/0082_auto_20220630_1103.py @@ -0,0 +1,125 @@ +# Generated by Django 3.2.13 on 2022-06-30 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("kfet", "0081_auto_20220628_1621"), + ] + + operations = [ + migrations.AlterField( + model_name="account", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="accountnegative", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="article", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="articlecategory", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="checkout", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="checkoutstatement", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="inventory", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="inventoryarticle", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="operation", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="operationgroup", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="order", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="orderarticle", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="supplier", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="supplierarticle", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="transfer", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + migrations.AlterField( + model_name="transfergroup", + name="id", + field=models.AutoField( + auto_created=True, primary_key=True, serialize=False, verbose_name="ID" + ), + ), + ] -- 2.45.1 From 75bf5c9b7d3cfa9536be68c317403c7386a0e344 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 11:19:04 +0200 Subject: [PATCH 30/40] assertEquals is deprecated --- bds/tests/test_views.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bds/tests/test_views.py b/bds/tests/test_views.py index ef6139f4..332db8d7 100644 --- a/bds/tests/test_views.py +++ b/bds/tests/test_views.py @@ -31,7 +31,7 @@ class TestHomeView(TestCase): user, backend="django.contrib.auth.backends.ModelBackend" ) resp = self.client.get(reverse("bds:home")) - self.assertEquals(resp.status_code, 200) + self.assertEqual(resp.status_code, 200) class TestRegistrationView(TestCase): @@ -48,12 +48,12 @@ class TestRegistrationView(TestCase): # Logged-in but unprivileged GET client.force_login(user, backend="django.contrib.auth.backends.ModelBackend") resp = client.get(url) - self.assertEquals(resp.status_code, 403) + self.assertEqual(resp.status_code, 403) # Burô user GET give_bds_buro_permissions(user) resp = client.get(url) - self.assertEquals(resp.status_code, 200) + self.assertEqual(resp.status_code, 200) @mock.patch("gestioncof.signals.messages") def test_get(self, mock_messages): @@ -68,9 +68,9 @@ class TestRegistrationView(TestCase): # Logged-in but unprivileged GET client.force_login(user, backend="django.contrib.auth.backends.ModelBackend") resp = client.get(url) - self.assertEquals(resp.status_code, 403) + self.assertEqual(resp.status_code, 403) # Burô user GET give_bds_buro_permissions(user) resp = client.get(url) - self.assertEquals(resp.status_code, 200) + self.assertEqual(resp.status_code, 200) -- 2.45.1 From 0b03a42accffdcac6a0cc4d6ca1748df5a6d732a Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 11:35:02 +0200 Subject: [PATCH 31/40] Explicitely set transform=repr in assertQuerysetEquals --- gestioncof/tests/test_views.py | 41 +++++++++++++++++++++++++--------- kfet/auth/tests.py | 1 + kfet/tests/test_tests_utils.py | 1 + kfet/tests/test_views.py | 23 ++++++++++++++----- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/gestioncof/tests/test_views.py b/gestioncof/tests/test_views.py index ecbb20f6..40a85bb1 100644 --- a/gestioncof/tests/test_views.py +++ b/gestioncof/tests/test_views.py @@ -194,7 +194,9 @@ class RegistrationViewTests(ViewTestCaseMixin, TestCase): ) er = e.eventregistration_set.get(user=self.users["user"]) - self.assertQuerysetEqual(er.options.all(), map(repr, [oc1, oc3]), ordered=False) + self.assertQuerysetEqual( + er.options.all(), map(repr, [oc1, oc3]), transform=repr, ordered=False + ) self.assertCountEqual( er.comments.values_list("content", flat=True), ["comment 1"] ) @@ -299,10 +301,10 @@ class RegistrationAutocompleteViewTests(MockLDAPMixin, ViewTestCaseMixin, TestCa raise ValueError("Unexpected section name: {}".format(section.name)) self.assertQuerysetEqual( - others, map(str, expected_others), ordered=False, transform=str + others, map(str, expected_others), transform=str, ordered=False ) self.assertQuerysetEqual( - members, map(str, expected_members), ordered=False, transform=str + members, map(str, expected_members), transform=str, ordered=False ) self.assertSetEqual( set(clippers), set(map(LDAPSearch().result_verbose_name, expected_clippers)) @@ -647,7 +649,10 @@ class ClubListViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context["owned_clubs"], map(repr, [self.c1, self.c2]), ordered=False + r.context["owned_clubs"], + map(repr, [self.c1, self.c2]), + transform=repr, + ordered=False, ) @@ -949,7 +954,10 @@ class EventViewTests(ViewTestCaseMixin, TestCase): er = self.e.eventregistration_set.get(user=self.users["user"]) self.assertQuerysetEqual( - er.options.all(), map(repr, [self.oc1, self.oc3, self.oc4]), ordered=False + er.options.all(), + map(repr, [self.oc1, self.oc3, self.oc4]), + transform=repr, + ordered=False, ) # TODO: Make the view care about comments. # self.assertQuerysetEqual( @@ -974,7 +982,9 @@ class EventViewTests(ViewTestCaseMixin, TestCase): self.assertIn(self.post_expected_message, get_messages(r.wsgi_request)) er.refresh_from_db() - self.assertQuerysetEqual(er.options.all(), map(repr, [self.oc3]), ordered=False) + self.assertQuerysetEqual( + er.options.all(), map(repr, [self.oc3]), transform=repr, ordered=False + ) # TODO: Make the view care about comments. # self.assertQuerysetEqual( # er.comments.all(), map(repr, []), @@ -1028,7 +1038,10 @@ class EventStatusViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context["user_choices"], map(repr, expected), ordered=False + r.context["user_choices"], + map(repr, expected), + transform=repr, + ordered=False, ) def test_filter_none(self): @@ -1095,7 +1108,10 @@ class SurveyViewTests(ViewTestCaseMixin, TestCase): a = self.s.surveyanswer_set.get(user=self.users["user"]) self.assertQuerysetEqual( - a.answers.all(), map(repr, [self.qa1, self.qa3, self.qa4]), ordered=False + a.answers.all(), + map(repr, [self.qa1, self.qa3, self.qa4]), + transform=repr, + ordered=False, ) def test_post_edit(self): @@ -1114,7 +1130,9 @@ class SurveyViewTests(ViewTestCaseMixin, TestCase): self.assertIn(self.post_expected_message, get_messages(r.wsgi_request)) a.refresh_from_db() - self.assertQuerysetEqual(a.answers.all(), map(repr, [self.qa3]), ordered=False) + self.assertQuerysetEqual( + a.answers.all(), map(repr, [self.qa3]), transform=repr, ordered=False + ) def test_post_delete(self): a = self.s.surveyanswer_set.create(user=self.users["user"]) @@ -1195,7 +1213,10 @@ class SurveyStatusViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context["user_answers"], map(repr, expected), ordered=False + r.context["user_answers"], + map(repr, expected), + transform=repr, + ordered=False, ) def test_filter_none(self): diff --git a/kfet/auth/tests.py b/kfet/auth/tests.py index a7a0b5ad..ade0d11a 100644 --- a/kfet/auth/tests.py +++ b/kfet/auth/tests.py @@ -40,6 +40,7 @@ class UserGroupFormTests(TestCase): self.assertQuerysetEqual( groups_field.queryset, [repr(g.group_ptr) for g in self.kfet_groups], + transform=repr, ordered=False, ) diff --git a/kfet/tests/test_tests_utils.py b/kfet/tests/test_tests_utils.py index 49661e23..2c42ff79 100644 --- a/kfet/tests/test_tests_utils.py +++ b/kfet/tests/test_tests_utils.py @@ -94,6 +94,7 @@ class PermHelpersTest(TestCaseMixin, TestCase): self.assertQuerysetEqual( user.user_permissions.all(), map(repr, [self.perm1, self.perm2, self.perm_team]), + transform=repr, ordered=False, ) diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py index d7df91ad..c6d8a5a7 100644 --- a/kfet/tests/test_views.py +++ b/kfet/tests/test_views.py @@ -520,6 +520,7 @@ class AccountGroupCreateViewTests(ViewTestCaseMixin, TestCase): self.assertQuerysetEqual( group.permissions.all(), map(repr, [self.perms["kfet.is_team"], self.perms["kfet.manage_perms"]]), + transform=repr, ordered=False, ) @@ -573,6 +574,7 @@ class AccountGroupUpdateViewTests(ViewTestCaseMixin, TestCase): self.assertQuerysetEqual( self.group.permissions.all(), map(repr, [self.perms["kfet.is_team"], self.perms["kfet.manage_perms"]]), + transform=repr, ordered=False, ) @@ -600,6 +602,7 @@ class AccountNegativeListViewTests(ViewTestCaseMixin, TestCase): self.assertQuerysetEqual( r.context["negatives"], map(repr, [self.accounts["user"].negative]), + transform=repr, ordered=False, ) @@ -877,6 +880,7 @@ class CheckoutListViewTests(ViewTestCaseMixin, TestCase): self.assertQuerysetEqual( r.context["checkouts"], map(repr, [self.checkout1, self.checkout2]), + transform=repr, ordered=False, ) @@ -1067,6 +1071,7 @@ class CheckoutStatementListViewTests(ViewTestCaseMixin, TestCase): self.assertQuerysetEqual( r.context["checkoutstatements"], map(repr, expected_statements), + transform=repr, ordered=False, ) @@ -1293,7 +1298,9 @@ class ArticleCategoryListViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context["categories"], map(repr, [self.category1, self.category2]) + r.context["categories"], + map(repr, [self.category1, self.category2]), + transform=repr, ) @@ -1368,7 +1375,9 @@ class ArticleListViewTests(ViewTestCaseMixin, TestCase): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context["articles"], map(repr, [self.article1, self.article2]) + r.context["articles"], + map(repr, [self.article1, self.article2]), + transform=repr, ) @@ -4435,7 +4444,9 @@ class InventoryListViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(r.status_code, 200) inventories = r.context["inventories"] - self.assertQuerysetEqual(inventories, map(repr, [self.inventory])) + self.assertQuerysetEqual( + inventories, map(repr, [self.inventory]), transform=repr + ) class InventoryCreateViewTests(ViewTestCaseMixin, TestCase): @@ -4622,7 +4633,7 @@ class OrderListViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(r.status_code, 200) orders = r.context["orders"] - self.assertQuerysetEqual(orders, map(repr, [self.order])) + self.assertQuerysetEqual(orders, map(repr, [self.order]), transform=repr) class OrderReadViewTests(ViewTestCaseMixin, TestCase): @@ -4837,7 +4848,9 @@ class OrderToInventoryViewTests(ViewTestCaseMixin, TestCase): inventory, {"by": self.accounts["team1"], "at": self.now, "order": self.order}, ) - self.assertQuerysetEqual(inventory.articles.all(), map(repr, [self.article])) + self.assertQuerysetEqual( + inventory.articles.all(), map(repr, [self.article]), transform=repr + ) compte = InventoryArticle.objects.get(article=self.article) -- 2.45.1 From 2b2fcb8d528e7d47bfc7f8d81a4da44ea8f73bbf Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 12:15:09 +0200 Subject: [PATCH 32/40] assertDictContainsSubset is deprecated --- kfet/tests/test_views.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py index c6d8a5a7..d09ff3e8 100644 --- a/kfet/tests/test_views.py +++ b/kfet/tests/test_views.py @@ -703,7 +703,7 @@ class AccountStatOperationListViewTests(ViewTestCaseMixin, TestCase): for stat, expected in zip(content["stats"], expected_stats): expected_url = expected.pop("url") self.assertUrlsEqual(stat["url"], expected_url) - self.assertDictContainsSubset(expected, stat) + self.assertEqual(stat, {**stat, **expected}) class AccountStatOperationViewTests(ViewTestCaseMixin, TestCase): @@ -812,7 +812,7 @@ class AccountStatBalanceListViewTests(ViewTestCaseMixin, TestCase): for stat, expected in zip(content["stats"], expected_stats): expected_url = expected.pop("url") self.assertUrlsEqual(stat["url"], expected_url) - self.assertDictContainsSubset(expected, stat) + self.assertEqual(stat, {**stat, **expected}) class AccountStatBalanceViewTests(ViewTestCaseMixin, TestCase): @@ -1647,7 +1647,7 @@ class ArticleStatSalesListViewTests(ViewTestCaseMixin, TestCase): for stat, expected in zip(content["stats"], expected_stats): expected_url = expected.pop("url") self.assertUrlsEqual(stat["url"], expected_url) - self.assertDictContainsSubset(expected, stat) + self.assertEqual(stat, {**stat, **expected}) class ArticleStatSalesViewTests(ViewTestCaseMixin, TestCase): @@ -1716,7 +1716,7 @@ class KPsulCheckoutDataViewTests(ViewTestCaseMixin, TestCase): expected = {"name": "Checkout", "balance": "10.00"} - self.assertDictContainsSubset(expected, content) + self.assertEqual(content, {**content, **expected}) self.assertSetEqual( set(content.keys()), @@ -4100,7 +4100,7 @@ class KPsulArticlesData(ViewTestCaseMixin, TestCase): ] for expected, article in zip(expected_list, articles): - self.assertDictContainsSubset(expected, article) + self.assertEqual(article, {**article, **expected}) self.assertSetEqual( set(article.keys()), set( @@ -4200,7 +4200,7 @@ class AccountReadJSONViewTests(ViewTestCaseMixin, TestCase): content = json.loads(r.content.decode("utf-8")) expected = {"name": "first last", "trigramme": "000", "balance": "0.00"} - self.assertDictContainsSubset(expected, content) + self.assertEqual(content, {**content, **expected}) self.assertSetEqual( set(content.keys()), -- 2.45.1 From ef68e469b5b07d651541e05fbe9817763d51bd94 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 12:18:42 +0200 Subject: [PATCH 33/40] Update django-hCaptcha --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6d09eac0..38e04bd0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ Django==3.2.* django-autocomplete-light==3.9.* django-cas-ng==4.3.* django-djconfig==0.10.0 -django-hCaptcha==0.1.0 +django-hCaptcha==0.2.0 icalendar Pillow django-bootstrap-form==3.3 -- 2.45.1 From d756cb1f5bc3c52b6b60a22704bb5359b6de8b18 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 12:44:19 +0200 Subject: [PATCH 34/40] Remove useless migrations --- bda/migrations/0019_auto_20220628_1621.py | 76 ---------- ...630_1035.py => 0019_auto_20220630_1245.py} | 4 +- bda/migrations/0021_auto_20220630_1103.py | 76 ---------- bds/migrations/0007_alter_bdsprofile_id.py | 20 --- bds/migrations/0008_alter_bdsprofile_id.py | 20 --- clubs/migrations/0002_alter_club_id.py | 20 --- clubs/migrations/0003_alter_club_id.py | 20 --- events/migrations/0005_auto_20220628_1621.py | 55 ------- ...630_1035.py => 0005_auto_20220630_1239.py} | 4 +- events/migrations/0007_auto_20220630_1103.py | 55 ------- .../migrations/0019_auto_20220628_1621.py | 139 ------------------ ...630_1036.py => 0019_auto_20220630_1241.py} | 4 +- .../migrations/0021_auto_20220630_1103.py | 139 ------------------ .../0004_alter_genericteamtoken_id.py | 20 --- .../0005_alter_genericteamtoken_id.py | 20 --- .../migrations/0003_alter_memberteam_id.py | 20 --- .../migrations/0004_alter_memberteam_id.py | 20 --- kfet/migrations/0081_auto_20220628_1621.py | 125 ---------------- kfet/migrations/0082_auto_20220630_1103.py | 125 ---------------- 19 files changed, 6 insertions(+), 956 deletions(-) delete mode 100644 bda/migrations/0019_auto_20220628_1621.py rename bda/migrations/{0020_auto_20220630_1035.py => 0019_auto_20220630_1245.py} (83%) delete mode 100644 bda/migrations/0021_auto_20220630_1103.py delete mode 100644 bds/migrations/0007_alter_bdsprofile_id.py delete mode 100644 bds/migrations/0008_alter_bdsprofile_id.py delete mode 100644 clubs/migrations/0002_alter_club_id.py delete mode 100644 clubs/migrations/0003_alter_club_id.py delete mode 100644 events/migrations/0005_auto_20220628_1621.py rename events/migrations/{0006_auto_20220630_1035.py => 0005_auto_20220630_1239.py} (95%) delete mode 100644 events/migrations/0007_auto_20220630_1103.py delete mode 100644 gestioncof/migrations/0019_auto_20220628_1621.py rename gestioncof/migrations/{0020_auto_20220630_1036.py => 0019_auto_20220630_1241.py} (92%) delete mode 100644 gestioncof/migrations/0021_auto_20220630_1103.py delete mode 100644 kfet/auth/migrations/0004_alter_genericteamtoken_id.py delete mode 100644 kfet/auth/migrations/0005_alter_genericteamtoken_id.py delete mode 100644 kfet/cms/migrations/0003_alter_memberteam_id.py delete mode 100644 kfet/cms/migrations/0004_alter_memberteam_id.py delete mode 100644 kfet/migrations/0081_auto_20220628_1621.py delete mode 100644 kfet/migrations/0082_auto_20220630_1103.py diff --git a/bda/migrations/0019_auto_20220628_1621.py b/bda/migrations/0019_auto_20220628_1621.py deleted file mode 100644 index 3b5d3f2f..00000000 --- a/bda/migrations/0019_auto_20220628_1621.py +++ /dev/null @@ -1,76 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-28 14:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("bda", "0018_auto_20201021_1818"), - ] - - operations = [ - migrations.AlterField( - model_name="attribution", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="categoriespectacle", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="choixspectacle", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="participant", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="quote", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="salle", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="spectacle", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="spectaclerevente", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="tirage", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/bda/migrations/0020_auto_20220630_1035.py b/bda/migrations/0019_auto_20220630_1245.py similarity index 83% rename from bda/migrations/0020_auto_20220630_1035.py rename to bda/migrations/0019_auto_20220630_1245.py index 43075bfe..12b7149d 100644 --- a/bda/migrations/0020_auto_20220630_1035.py +++ b/bda/migrations/0019_auto_20220630_1245.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.13 on 2022-06-30 08:35 +# Generated by Django 3.2.13 on 2022-06-30 10:45 from django.db import migrations, models @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("bda", "0019_auto_20220628_1621"), + ("bda", "0018_auto_20201021_1818"), ] operations = [ diff --git a/bda/migrations/0021_auto_20220630_1103.py b/bda/migrations/0021_auto_20220630_1103.py deleted file mode 100644 index df0aea3c..00000000 --- a/bda/migrations/0021_auto_20220630_1103.py +++ /dev/null @@ -1,76 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-30 09:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("bda", "0020_auto_20220630_1035"), - ] - - operations = [ - migrations.AlterField( - model_name="attribution", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="categoriespectacle", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="choixspectacle", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="participant", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="quote", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="salle", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="spectacle", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="spectaclerevente", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="tirage", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/bds/migrations/0007_alter_bdsprofile_id.py b/bds/migrations/0007_alter_bdsprofile_id.py deleted file mode 100644 index 7da58ca3..00000000 --- a/bds/migrations/0007_alter_bdsprofile_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-28 14:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("bds", "0006_bdsprofile_comments"), - ] - - operations = [ - migrations.AlterField( - model_name="bdsprofile", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/bds/migrations/0008_alter_bdsprofile_id.py b/bds/migrations/0008_alter_bdsprofile_id.py deleted file mode 100644 index 89c74d4e..00000000 --- a/bds/migrations/0008_alter_bdsprofile_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-30 09:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("bds", "0007_alter_bdsprofile_id"), - ] - - operations = [ - migrations.AlterField( - model_name="bdsprofile", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/clubs/migrations/0002_alter_club_id.py b/clubs/migrations/0002_alter_club_id.py deleted file mode 100644 index cbcb1014..00000000 --- a/clubs/migrations/0002_alter_club_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-28 14:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("clubs", "0001_initial"), - ] - - operations = [ - migrations.AlterField( - model_name="club", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/clubs/migrations/0003_alter_club_id.py b/clubs/migrations/0003_alter_club_id.py deleted file mode 100644 index 92819ce7..00000000 --- a/clubs/migrations/0003_alter_club_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-30 09:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("clubs", "0002_alter_club_id"), - ] - - operations = [ - migrations.AlterField( - model_name="club", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/events/migrations/0005_auto_20220628_1621.py b/events/migrations/0005_auto_20220628_1621.py deleted file mode 100644 index f7dd6513..00000000 --- a/events/migrations/0005_auto_20220628_1621.py +++ /dev/null @@ -1,55 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-28 14:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("events", "0004_unique_constraints"), - ] - - operations = [ - migrations.AlterField( - model_name="event", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="extrafield", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="extrafieldcontent", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="option", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="optionchoice", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="registration", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/events/migrations/0006_auto_20220630_1035.py b/events/migrations/0005_auto_20220630_1239.py similarity index 95% rename from events/migrations/0006_auto_20220630_1035.py rename to events/migrations/0005_auto_20220630_1239.py index f6cffcac..d2624da2 100644 --- a/events/migrations/0006_auto_20220630_1035.py +++ b/events/migrations/0005_auto_20220630_1239.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.13 on 2022-06-30 08:35 +# Generated by Django 3.2.13 on 2022-06-30 10:39 from django.db import migrations, models @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("events", "0005_auto_20220628_1621"), + ("events", "0004_unique_constraints"), ] operations = [ diff --git a/events/migrations/0007_auto_20220630_1103.py b/events/migrations/0007_auto_20220630_1103.py deleted file mode 100644 index 6fc3502d..00000000 --- a/events/migrations/0007_auto_20220630_1103.py +++ /dev/null @@ -1,55 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-30 09:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("events", "0006_auto_20220630_1035"), - ] - - operations = [ - migrations.AlterField( - model_name="event", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="extrafield", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="extrafieldcontent", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="option", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="optionchoice", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="registration", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/gestioncof/migrations/0019_auto_20220628_1621.py b/gestioncof/migrations/0019_auto_20220628_1621.py deleted file mode 100644 index 0163eb04..00000000 --- a/gestioncof/migrations/0019_auto_20220628_1621.py +++ /dev/null @@ -1,139 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-28 14:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("gestioncof", "0018_petitscours_email"), - ] - - operations = [ - migrations.AlterField( - model_name="calendarsubscription", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="club", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="cofprofile", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="event", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventcommentfield", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventcommentvalue", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventoption", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventoptionchoice", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventregistration", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcoursability", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcoursattribution", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcoursattributioncounter", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcoursdemande", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcourssubject", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="survey", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="surveyanswer", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="surveyquestion", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="surveyquestionanswer", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/gestioncof/migrations/0020_auto_20220630_1036.py b/gestioncof/migrations/0019_auto_20220630_1241.py similarity index 92% rename from gestioncof/migrations/0020_auto_20220630_1036.py rename to gestioncof/migrations/0019_auto_20220630_1241.py index 2085f329..eec1cfe3 100644 --- a/gestioncof/migrations/0020_auto_20220630_1036.py +++ b/gestioncof/migrations/0019_auto_20220630_1241.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.13 on 2022-06-30 08:36 +# Generated by Django 3.2.13 on 2022-06-30 10:41 from django.db import migrations, models @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("gestioncof", "0019_auto_20220628_1621"), + ("gestioncof", "0018_petitscours_email"), ] operations = [ diff --git a/gestioncof/migrations/0021_auto_20220630_1103.py b/gestioncof/migrations/0021_auto_20220630_1103.py deleted file mode 100644 index 3f1d7dcc..00000000 --- a/gestioncof/migrations/0021_auto_20220630_1103.py +++ /dev/null @@ -1,139 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-30 09:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("gestioncof", "0020_auto_20220630_1036"), - ] - - operations = [ - migrations.AlterField( - model_name="calendarsubscription", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="club", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="cofprofile", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="event", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventcommentfield", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventcommentvalue", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventoption", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventoptionchoice", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="eventregistration", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcoursability", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcoursattribution", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcoursattributioncounter", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcoursdemande", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="petitcourssubject", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="survey", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="surveyanswer", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="surveyquestion", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="surveyquestionanswer", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/kfet/auth/migrations/0004_alter_genericteamtoken_id.py b/kfet/auth/migrations/0004_alter_genericteamtoken_id.py deleted file mode 100644 index 58673a5d..00000000 --- a/kfet/auth/migrations/0004_alter_genericteamtoken_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-28 14:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("kfetauth", "0003_existing_groups"), - ] - - operations = [ - migrations.AlterField( - model_name="genericteamtoken", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/kfet/auth/migrations/0005_alter_genericteamtoken_id.py b/kfet/auth/migrations/0005_alter_genericteamtoken_id.py deleted file mode 100644 index b6be0a0c..00000000 --- a/kfet/auth/migrations/0005_alter_genericteamtoken_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-30 09:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("kfetauth", "0004_alter_genericteamtoken_id"), - ] - - operations = [ - migrations.AlterField( - model_name="genericteamtoken", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/kfet/cms/migrations/0003_alter_memberteam_id.py b/kfet/cms/migrations/0003_alter_memberteam_id.py deleted file mode 100644 index bf170e0e..00000000 --- a/kfet/cms/migrations/0003_alter_memberteam_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-28 14:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("kfetcms", "0002_alter_kfetpage_colcount"), - ] - - operations = [ - migrations.AlterField( - model_name="memberteam", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/kfet/cms/migrations/0004_alter_memberteam_id.py b/kfet/cms/migrations/0004_alter_memberteam_id.py deleted file mode 100644 index 2ca24156..00000000 --- a/kfet/cms/migrations/0004_alter_memberteam_id.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-30 09:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("kfetcms", "0003_alter_memberteam_id"), - ] - - operations = [ - migrations.AlterField( - model_name="memberteam", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/kfet/migrations/0081_auto_20220628_1621.py b/kfet/migrations/0081_auto_20220628_1621.py deleted file mode 100644 index 8763c058..00000000 --- a/kfet/migrations/0081_auto_20220628_1621.py +++ /dev/null @@ -1,125 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-28 14:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("kfet", "0080_accountnegative_last_rappel"), - ] - - operations = [ - migrations.AlterField( - model_name="account", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="accountnegative", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="article", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="articlecategory", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="checkout", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="checkoutstatement", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="inventory", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="inventoryarticle", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="operation", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="operationgroup", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="order", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="orderarticle", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="supplier", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="supplierarticle", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="transfer", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="transfergroup", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] diff --git a/kfet/migrations/0082_auto_20220630_1103.py b/kfet/migrations/0082_auto_20220630_1103.py deleted file mode 100644 index 3be2cfe1..00000000 --- a/kfet/migrations/0082_auto_20220630_1103.py +++ /dev/null @@ -1,125 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-30 09:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("kfet", "0081_auto_20220628_1621"), - ] - - operations = [ - migrations.AlterField( - model_name="account", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="accountnegative", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="article", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="articlecategory", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="checkout", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="checkoutstatement", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="inventory", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="inventoryarticle", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="operation", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="operationgroup", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="order", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="orderarticle", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="supplier", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="supplierarticle", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="transfer", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="transfergroup", - name="id", - field=models.AutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - ] -- 2.45.1 From e2d7bba816dc489311ce0691d457fd8dc947fc1e Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Mon, 3 Oct 2022 13:08:01 +0200 Subject: [PATCH 35/40] dev: update requirements --- requirements.txt | 2 +- shell.nix | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 38e04bd0..6caad72f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ configparser==3.5.0 Django==3.2.* -django-autocomplete-light==3.9.* +django-autocomplete-light==3.9.4 django-cas-ng==4.3.* django-djconfig==0.10.0 django-hCaptcha==0.2.0 diff --git a/shell.nix b/shell.nix index be49fa83..bdfac247 100644 --- a/shell.nix +++ b/shell.nix @@ -1,17 +1,32 @@ +{ pkgs ? (import ) {}}: + let + pypiDataRev = "2505eb53d85cd727c87611ee4aa35152821a12b2"; + pypiDataSha256 = "0nhl0rzlp4fgzxb15pmnq14d0rzcwhvwn40vx7fnk41z9gwxcp4c"; + + pypiData = builtins.fetchTarball { + name = "pypi-deps-db-src"; + url = "https://github.com/DavHau/pypi-deps-db/tarball/${pypiDataRev}"; + sha256 = "${pypiDataSha256}"; + }; + mach-nix = import (builtins.fetchGit { url = "https://github.com/DavHau/mach-nix"; ref = "refs/tags/3.5.0"; }) - { }; + { + inherit pypiData; + + python = "python39"; + }; requirements = builtins.readFile ./requirements.txt; requirements-dev = '' django-debug-toolbar ipython - black + black==22.3.0 isort flake8 ''; -- 2.45.1 From 192b9d1e153420ddd07f11f923d9eb17de4c157f Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Mon, 3 Oct 2022 13:10:20 +0200 Subject: [PATCH 36/40] kfet: remove redundant static tag --- kfet/templates/kfet/transfers.html | 1 - 1 file changed, 1 deletion(-) diff --git a/kfet/templates/kfet/transfers.html b/kfet/templates/kfet/transfers.html index e9ee16b0..549727e0 100644 --- a/kfet/templates/kfet/transfers.html +++ b/kfet/templates/kfet/transfers.html @@ -1,5 +1,4 @@ {% extends 'kfet/base_col_2.html' %} -{% load static %} {% load l10n static widget_tweaks %} {% block title %}Transferts{% endblock %} -- 2.45.1 From 74241e22ccb175794f91ccd33475dbc68e7d7087 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Mon, 3 Oct 2022 13:19:52 +0200 Subject: [PATCH 37/40] dev: set explicitely DJANGO_SETTINGS_MODULE --- manage.py | 2 +- shell.nix | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/manage.py b/manage.py index 913e4f6e..00e46405 100755 --- a/manage.py +++ b/manage.py @@ -3,7 +3,7 @@ import os import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings.local") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gestioasso.settings") from django.core.management import execute_from_command_line diff --git a/shell.nix b/shell.nix index bdfac247..27ebda09 100644 --- a/shell.nix +++ b/shell.nix @@ -1,4 +1,4 @@ -{ pkgs ? (import ) {}}: +{ pkgs ? (import ) { } }: let pypiDataRev = "2505eb53d85cd727c87611ee4aa35152821a12b2"; @@ -30,8 +30,14 @@ let isort flake8 ''; + + pyEnv = mach-nix.mkPython { requirements = requirements + requirements-dev; }; in -mach-nix.mkPythonShell { - requirements = requirements + requirements-dev; +pkgs.mkShell { + buildInputs = [ pyEnv ]; + + shellHook = '' + export DJANGO_SETTINGS_MODULE=gestioasso.settings.local + ''; } -- 2.45.1 From 7968a70236da144c8131a029ca1f4f01ea8e8dae Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Mon, 3 Oct 2022 13:24:00 +0200 Subject: [PATCH 38/40] kfet/open: Add comment to test --- kfet/open/tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kfet/open/tests.py b/kfet/open/tests.py index 295a8cd7..3eabcc02 100644 --- a/kfet/open/tests.py +++ b/kfet/open/tests.py @@ -235,6 +235,9 @@ class OpenKfetConsumerTest(TestCase): async def test_team_user(self): """Team user is added to kfet.open.team group.""" + # On simule l'appartenance de l'user à la team kfet car l'utilisation de + # tests async avec postgres fait tout planter si on modifie la db dans un + # des sous tests. with mock.patch("gestioncof.signals.messages"), mock.patch( "kfet.open.consumers.kfet_is_team", return_value=True ): -- 2.45.1 From 157165a7c95086aaef6e63ba81f19c3c6efff9cb Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Mon, 3 Oct 2022 15:47:31 +0200 Subject: [PATCH 39/40] dev: update requirements and directly read the files --- requirements-devel.txt | 2 +- requirements-prod.txt | 2 +- shell.nix | 28 ++++++++++++++++++---------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/requirements-devel.txt b/requirements-devel.txt index 8dc49eb1..637123a5 100644 --- a/requirements-devel.txt +++ b/requirements-devel.txt @@ -3,6 +3,6 @@ django-debug-toolbar ipython # Tools -black +black==22.3.0 flake8 isort diff --git a/requirements-prod.txt b/requirements-prod.txt index 4037e7fe..b4a99d6b 100644 --- a/requirements-prod.txt +++ b/requirements-prod.txt @@ -1,7 +1,7 @@ -r requirements.txt # Postgresql bindings -psycopg2<2.8 +psycopg2==2.9.* # Redis django-redis-cache==3.0.* diff --git a/shell.nix b/shell.nix index 27ebda09..0476c269 100644 --- a/shell.nix +++ b/shell.nix @@ -1,6 +1,12 @@ -{ pkgs ? (import ) { } }: +{ pkgs ? (import ) { }, lib ? pkgs.lib }: let + mkRequirements = file: + builtins.concatStringsSep "\n" + (builtins.filter + (s: !(lib.hasPrefix "-r" s || lib.hasPrefix "#" s || s == "")) + (lib.splitString "\n" (builtins.readFile file))); + pypiDataRev = "2505eb53d85cd727c87611ee4aa35152821a12b2"; pypiDataSha256 = "0nhl0rzlp4fgzxb15pmnq14d0rzcwhvwn40vx7fnk41z9gwxcp4c"; @@ -21,17 +27,19 @@ let python = "python39"; }; - requirements = builtins.readFile ./requirements.txt; + requirements = mkRequirements ./requirements.txt; - requirements-dev = '' - django-debug-toolbar - ipython - black==22.3.0 - isort - flake8 - ''; + requirements-prod = mkRequirements ./requirements-prod.txt; - pyEnv = mach-nix.mkPython { requirements = requirements + requirements-dev; }; + requirements-dev = mkRequirements ./requirements-devel.txt; + + pyEnv = mach-nix.mkPython { + requirements = builtins.concatStringsSep "\n" [ + requirements + requirements-dev + requirements-prod + ]; + }; in pkgs.mkShell { -- 2.45.1 From b4669847de68151fb09b714fd49897fc867f7599 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 30 Jun 2022 17:02:34 +0200 Subject: [PATCH 40/40] Update tests for BDS --- bds/tests/test_views.py | 180 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 3 deletions(-) diff --git a/bds/tests/test_views.py b/bds/tests/test_views.py index 332db8d7..1cfe6199 100644 --- a/bds/tests/test_views.py +++ b/bds/tests/test_views.py @@ -1,3 +1,4 @@ +from datetime import date from unittest import mock from django.conf import settings @@ -6,6 +7,8 @@ from django.contrib.auth.models import Permission from django.test import Client, TestCase from django.urls import reverse, reverse_lazy +from bds.models import BDSProfile + User = get_user_model() @@ -24,7 +27,7 @@ def login_url(next=None): class TestHomeView(TestCase): @mock.patch("gestioncof.signals.messages") - def test_get(self, mock_messages): + def test_get(self, _): user = User.objects.create_user(username="random_user") give_bds_buro_permissions(user) self.client.force_login( @@ -36,7 +39,7 @@ class TestHomeView(TestCase): class TestRegistrationView(TestCase): @mock.patch("gestioncof.signals.messages") - def test_get_autocomplete(self, mock_messages): + def test_get_autocomplete(self, _): user = User.objects.create_user(username="toto") url = reverse("bds:autocomplete") + "?q=foo" client = Client() @@ -56,7 +59,7 @@ class TestRegistrationView(TestCase): self.assertEqual(resp.status_code, 200) @mock.patch("gestioncof.signals.messages") - def test_get(self, mock_messages): + def test_get(self, _): user = User.objects.create_user(username="toto") url = reverse("bds:user.update", args=(user.id,)) client = Client() @@ -74,3 +77,174 @@ class TestRegistrationView(TestCase): give_bds_buro_permissions(user) resp = client.get(url) self.assertEqual(resp.status_code, 200) + + +class TestResetMemberships(TestCase): + @classmethod + def setUpTestData(cls): + cls.admin = User.objects.create_user(username="bds_admin") + + # Create users and profiles + cls.u_1 = User.objects.create_user(username="bds-01") + cls.p_1 = BDSProfile.objects.create(user=cls.u_1) + + cls.u_2 = User.objects.create_user(username="bds-02") + cls.p_2 = BDSProfile.objects.create(user=cls.u_2) + + cls.u_3 = User.objects.create_user(username="bds-03") + cls.p_3 = BDSProfile.objects.create(user=cls.u_3) + + cls.u_4 = User.objects.create_user(username="bds-04") + cls.p_4 = BDSProfile.objects.create(user=cls.u_4) + + @mock.patch("gestioncof.signals.messages") + def setUp(self, _): + give_bds_buro_permissions(self.admin) + # bds-01 est membre à l'année + self.p_1.is_member = True + self.p_1.mails_bds = True + self.p_1.cotisation_period = "ANN" + self.p_1.save() + + # bds-02 est membre au S1 + self.p_2.is_member = True + self.p_2.mails_bds = True + self.p_2.cotisation_period = "SE1" + self.p_2.save() + + # bds-03 est membre au S2 + self.p_3.is_member = True + self.p_3.mails_bds = True + self.p_3.cotisation_period = "SE2" + self.p_3.save() + + @mock.patch("gestioncof.signals.messages") + def test_get_expired(self, _): + user = User.objects.create_user(username="toto") + url = reverse("bds:members.expired") + client = Client() + + # Anonymous GET + resp = client.get(url) + self.assertRedirects(resp, login_url(next=url)) + + # Logged-in but unprivileged GET + client.force_login(user, backend="django.contrib.auth.backends.ModelBackend") + resp = client.get(url) + self.assertEquals(resp.status_code, 403) + + # Burô user GET + give_bds_buro_permissions(user) + resp = client.get(url) + self.assertEquals(resp.status_code, 200) + + self.assertQuerysetEqual( + resp.context_data["object_list"], BDSProfile.expired_members() + ) + + @mock.patch("gestioncof.signals.messages") + def test_get_reset(self, _): + user = User.objects.create_user(username="tata") + url = reverse("bds:members.reset") + client = Client() + + # Anonymous GET + resp = client.get(url) + self.assertRedirects(resp, login_url(next=url)) + + # Logged-in but unprivileged GET + client.force_login(user, backend="django.contrib.auth.backends.ModelBackend") + resp = client.get(url) + self.assertEquals(resp.status_code, 403) + + # Burô user GET + give_bds_buro_permissions(user) + resp = client.get(url) + self.assertRedirects(resp, reverse("bds:members.expired")) + + def test_expired_memberships(self): + # In september, there is no expired members + with mock.patch("bds.models.timezone.now", return_value=date(2022, 9, 1)): + self.assertQuerysetEqual(BDSProfile.expired_members(), []) + + # In march, only bds-02's membership is expired + with mock.patch("django.utils.timezone.now", return_value=date(2023, 3, 1)): + self.assertQuerysetEqual(BDSProfile.expired_members(), [self.p_2]) + + # During summer, all memberships are expired + with mock.patch("django.utils.timezone.now", return_value=date(2023, 7, 1)): + self.assertQuerysetEqual( + BDSProfile.expired_members(), + [self.p_1, self.p_2, self.p_3], + ordered=False, + ) + + @mock.patch("gestioncof.signals.messages") + def test_reset_memberships(self, _): + url = reverse("bds:members.reset") + + # Reset during S1 does nothig + with mock.patch("bds.models.timezone.now", return_value=date(2022, 9, 1)): + self.client.force_login( + self.admin, backend="django.contrib.auth.backends.ModelBackend" + ) + resp = self.client.get(url) + + self.assertRedirects(resp, reverse("bds:members.expired")) + + self.assertQuerysetEqual(BDSProfile.expired_members(), []) + + self.assertQuerysetEqual( + BDSProfile.objects.filter(is_member=True), + [self.p_1, self.p_2, self.p_3], + ordered=False, + ) + + # Reset in march + with mock.patch("django.utils.timezone.now", return_value=date(2023, 3, 1)): + self.client.force_login( + self.admin, backend="django.contrib.auth.backends.ModelBackend" + ) + resp = self.client.get(url) + + self.assertRedirects(resp, reverse("bds:members.expired")) + + # After a reset we have no expired memberships + self.assertQuerysetEqual(BDSProfile.expired_members(), []) + + # Test reset attributes + self.p_2.refresh_from_db() + self.assertEqual( + [self.p_2.is_member, self.p_2.mails_bds, self.p_2.cotisation_period], + [False, False, "NO"], + ) + + # Reset during summer + with mock.patch("django.utils.timezone.now", return_value=date(2023, 7, 1)): + # bds-02's membership wa already reset + self.assertQuerysetEqual( + BDSProfile.expired_members(), [self.p_1, self.p_3], ordered=False + ) + + self.client.force_login( + self.admin, backend="django.contrib.auth.backends.ModelBackend" + ) + resp = self.client.get(url) + + self.assertRedirects(resp, reverse("bds:members.expired")) + + # After a reset we have no expired memberships + self.assertQuerysetEqual(BDSProfile.expired_members(), []) + + # Test reset attributes + self.p_1.refresh_from_db() + self.assertEqual( + [self.p_1.is_member, self.p_1.mails_bds, self.p_1.cotisation_period], + [False, False, "NO"], + ) + + self.p_3.refresh_from_db() + self.assertEqual( + [self.p_3.is_member, self.p_3.mails_bds, self.p_3.cotisation_period], + [False, False, "NO"], + ) -- 2.45.1

Date {{ demande.created }}
Nom/prénom {{ demande.name }}