From 476fbceeea6e1f63665df9b0eeb782f403571557 Mon Sep 17 00:00:00 2001 From: quark Date: Thu, 10 Oct 2024 01:48:23 +0200 Subject: [PATCH 01/84] Donation goal la note kfet x les SdA --- note_kfet/templates/base.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index 68cbf542..9539efd7 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -158,7 +158,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
-
+
+ {% if user.is_authenticated %} +
+ Venez au festival des Sens de l'Art du 2 au 7 décembre 🐘 ! +
+ {% endif %} {% if user.is_authenticated %} {% if not user|is_member:"BDE" %}
From 2be613345881a157930e4e17e2b732d13d06adf7 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sun, 22 Dec 2024 20:42:20 +0100 Subject: [PATCH 02/84] Update file initial.json --- apps/permission/fixtures/initial.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index baed9a8d..ca9d8810 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -3800,6 +3800,22 @@ "description": "Voir les utilisateurs adhérents au club parent" } }, + { + "model": "permission.permission", + "pk": 242, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"AND\", [{\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}, {\"valid\": false}]]", + "type": "add", + "mask": 2, + "field": "", + "permanent": false, + "description": "Créer une transaction vers la note d'un club" + } + }, { "model": "permission.role", "pk": 1, From a63c34fe371f1079705bd4304bbeee1239409180 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sun, 22 Dec 2024 21:38:17 +0100 Subject: [PATCH 03/84] Update file initial.json --- apps/permission/fixtures/initial.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index ca9d8810..766ea03a 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -3808,7 +3808,7 @@ "note", "transaction" ], - "query": "[\"AND\", [{\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}, {\"valid\": false}]]", + "query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}, {\"valid\": false}]]", "type": "add", "mask": 2, "field": "", From 942d887c2e994bdefee0b0ee950db4d678eb1d14 Mon Sep 17 00:00:00 2001 From: thomasl Date: Mon, 23 Dec 2024 18:31:11 +0100 Subject: [PATCH 04/84] Update file initial.json --- apps/permission/fixtures/initial.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 766ea03a..565a604c 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -3816,6 +3816,22 @@ "description": "Créer une transaction vers la note d'un club" } }, + { + "model": "permission.permission", + "pk": 243, + "fields": { + "model": [ + "member", + "profile" + ], + "query": "{\"user__memberships__club\": [\"club\"], \"user__memberships__date_start__lte\": [\"today\"],\"user__memberships__date_end__gte\": [\"today\"]}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir les profils des membres du club" + } + }, { "model": "permission.role", "pk": 1, From 414e103686df5b604d1b47cad0e1a698294d0040 Mon Sep 17 00:00:00 2001 From: mcngnt Date: Sun, 5 Jan 2025 23:17:01 +0100 Subject: [PATCH 05/84] finitio le message sda --- note_kfet/templates/base.html | 5 ----- 1 file changed, 5 deletions(-) diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index 9539efd7..a84f29b2 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -159,11 +159,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
- {% if user.is_authenticated %} -
- Venez au festival des Sens de l'Art du 2 au 7 décembre 🐘 ! -
- {% endif %} {% if user.is_authenticated %} {% if not user|is_member:"BDE" %}
From 80e109114f99c14bd3df29ad7be2d850d21818b2 Mon Sep 17 00:00:00 2001 From: thomasl Date: Fri, 17 Jan 2025 18:23:28 +0100 Subject: [PATCH 06/84] Update file initial.json --- apps/permission/fixtures/initial.json | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 565a604c..33d71663 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4410,6 +4410,41 @@ ] } }, + { + "model": "permission.role", + "pk": 23, + "fields": { + "for_club": 10, + "name": "Tr\u00e9sorièr\u22c5e BDA", + "permissions": [ + 33, + 55, + 56, + 57, + 58, + 135, + 143, + 176, + 177, + 178 + ] + } + }, + { + "model": "permission.role", + "pk": 24, + "fields": { + "for_club": 10, + "name": "Respo sortie", + "permissions": [ + 239, + 240, + 241, + 242, + 243 + ] + } + }, { "model": "wei.weirole", "pk": 12, From 54ba78688429ad2450935fa561f8abb2d1d304cc Mon Sep 17 00:00:00 2001 From: thomasl Date: Fri, 17 Jan 2025 19:03:59 +0100 Subject: [PATCH 07/84] Update file initial.json --- apps/permission/fixtures/initial.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 33d71663..a5ba4dc9 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4414,7 +4414,7 @@ "model": "permission.role", "pk": 23, "fields": { - "for_club": 10, + "for_club": 2, "name": "Tr\u00e9sorièr\u22c5e BDA", "permissions": [ 33, From caaeab6b0bafc5eda2a041053a577e563cb99162 Mon Sep 17 00:00:00 2001 From: thomasl Date: Fri, 17 Jan 2025 19:39:26 +0100 Subject: [PATCH 08/84] Update file initial.json --- apps/permission/fixtures/initial.json | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index a5ba4dc9..597b7fa7 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4414,7 +4414,7 @@ "model": "permission.role", "pk": 23, "fields": { - "for_club": 2, + "for_club": 10, "name": "Tr\u00e9sorièr\u22c5e BDA", "permissions": [ 33, @@ -4445,6 +4445,28 @@ ] } }, + { + "model": "permission.role", + "pk": 25, + "fields": { + "for_club": 267, + "name": "Tr\u00e9sorièr\u22c5e Terre à terre", + "permissions": [ + 33, + 55, + 56, + 57, + 58, + 135, + 143, + 176, + 177, + 178 + ] + } + }, + + { "model": "wei.weirole", "pk": 12, From 43dc67674740e165ae6a63192155b2a412f1242e Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 18 Jan 2025 12:57:42 +0100 Subject: [PATCH 09/84] Update file initial.json --- apps/permission/fixtures/initial.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 597b7fa7..f1a5a50b 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4465,6 +4465,17 @@ ] } }, + { + "model": "permission.role", + "pk": 26, + "fields": { + "for_club": 282, + "name": "Tr\u00e9sorièr\u22c5e Gala", + "permissions": [ + 33 + ] + } + }, { From bd6ed27ae54c5eea40f66f08045e1519036c5318 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 18 Jan 2025 15:11:57 +0100 Subject: [PATCH 10/84] Update 2 files - /apps/permission/fixtures/initial.json - /apps/permission/admin.py --- apps/permission/admin.py | 1 + apps/permission/fixtures/initial.json | 70 +-------------------------- 2 files changed, 2 insertions(+), 69 deletions(-) diff --git a/apps/permission/admin.py b/apps/permission/admin.py index a6fc713c..82f8d4ab 100644 --- a/apps/permission/admin.py +++ b/apps/permission/admin.py @@ -31,3 +31,4 @@ class RoleAdmin(admin.ModelAdmin): Admin customisation for Role """ list_display = ('name', ) + filter_horizontal = ('permissions',) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index f1a5a50b..00f952cc 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4409,75 +4409,7 @@ 238 ] } - }, - { - "model": "permission.role", - "pk": 23, - "fields": { - "for_club": 10, - "name": "Tr\u00e9sorièr\u22c5e BDA", - "permissions": [ - 33, - 55, - 56, - 57, - 58, - 135, - 143, - 176, - 177, - 178 - ] - } - }, - { - "model": "permission.role", - "pk": 24, - "fields": { - "for_club": 10, - "name": "Respo sortie", - "permissions": [ - 239, - 240, - 241, - 242, - 243 - ] - } - }, - { - "model": "permission.role", - "pk": 25, - "fields": { - "for_club": 267, - "name": "Tr\u00e9sorièr\u22c5e Terre à terre", - "permissions": [ - 33, - 55, - 56, - 57, - 58, - 135, - 143, - 176, - 177, - 178 - ] - } - }, - { - "model": "permission.role", - "pk": 26, - "fields": { - "for_club": 282, - "name": "Tr\u00e9sorièr\u22c5e Gala", - "permissions": [ - 33 - ] - } - }, - - + }, { "model": "wei.weirole", "pk": 12, From a87ce625f3d0fba6c8401cf9ac5953863d47b169 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 25 Jan 2025 13:55:21 +0100 Subject: [PATCH 11/84] Update file note.cron --- note.cron | 2 ++ 1 file changed, 2 insertions(+) diff --git a/note.cron b/note.cron index dc1f6460..2f75f989 100644 --- a/note.cron +++ b/note.cron @@ -26,3 +26,5 @@ MAILTO=notekfet2020@lists.crans.org 00 9 * * * root cd /var/www/note_kfet && env/bin/python manage.py refresh_highlighted_buttons -v 0 # Vider les tokens Oauth2 00 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py cleartokens -v 0 +# Envoyer la liste des abonnés à la NL BDA + 00 8 * * 0 root cd /var/www/note_kfet && env/bin/python manage.py extract_ml_registrations -t art -v 0 \ No newline at end of file From 623290827aa6147c71d9282b962bc87de5ae034f Mon Sep 17 00:00:00 2001 From: thomasl Date: Mon, 27 Jan 2025 16:34:45 +0100 Subject: [PATCH 12/84] Update file forms.py --- apps/member/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index a74ddb90..62b1667e 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -76,7 +76,7 @@ class ProfileForm(forms.ModelForm): class Meta: model = Profile fields = '__all__' - exclude = ('user', 'email_confirmed', 'registration_valid', ) + exclude = ('user', 'email_confirmed', 'registration_valid','ml_sport_registration', ) class ImageForm(forms.Form): From 47fda0ea360de23e073982c9c6c30ff150933f47 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sun, 2 Feb 2025 13:17:19 +0100 Subject: [PATCH 13/84] Update file forms.py --- apps/member/forms.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index 62b1667e..9bc4c8ec 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -76,7 +76,9 @@ class ProfileForm(forms.ModelForm): class Meta: model = Profile fields = '__all__' - exclude = ('user', 'email_confirmed', 'registration_valid','ml_sport_registration', ) + # Remove ml_[asso]_registration from exclude if the concerned association uses nk20 to manage its mailing list. + # Remove report_frequency from exclude if you want to use this feature. + exclude = ('user', 'email_confirmed', 'registration_valid', 'ml_sport_registration', "ml_events_registration", "report_frequency", ) class ImageForm(forms.Form): From 867bf9fd25247ceb8990424288c5d168848a3d20 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sun, 2 Feb 2025 13:33:41 +0100 Subject: [PATCH 14/84] Update file forms.py --- apps/member/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index 9bc4c8ec..95b5978a 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -44,9 +44,9 @@ class ProfileForm(forms.ModelForm): """ A form for the extras field provided by the :model:`member.Profile` model. """ - report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency")) + # report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency")) - last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date")) + # last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date")) VSS_charter_read = forms.BooleanField( required=True, From 1481aa06358d5a2e3c1cb9556449698cce50ef2a Mon Sep 17 00:00:00 2001 From: thomasl Date: Sun, 2 Feb 2025 14:05:05 +0100 Subject: [PATCH 15/84] Update file forms.py --- apps/member/forms.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index 95b5978a..9a5c3166 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -44,9 +44,9 @@ class ProfileForm(forms.ModelForm): """ A form for the extras field provided by the :model:`member.Profile` model. """ - # report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency")) + report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency"), widget=django.forms.HiddenInput()) - # last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date")) + last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date")) VSS_charter_read = forms.BooleanField( required=True, @@ -77,8 +77,7 @@ class ProfileForm(forms.ModelForm): model = Profile fields = '__all__' # Remove ml_[asso]_registration from exclude if the concerned association uses nk20 to manage its mailing list. - # Remove report_frequency from exclude if you want to use this feature. - exclude = ('user', 'email_confirmed', 'registration_valid', 'ml_sport_registration', "ml_events_registration", "report_frequency", ) + exclude = ('user', 'email_confirmed', 'registration_valid', 'ml_sport_registration', "ml_events_registration", ) class ImageForm(forms.Form): From ef485e06281ba12e21b05203b86d3ae3d8e7fb45 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sun, 2 Feb 2025 14:06:22 +0100 Subject: [PATCH 16/84] Update file forms.py --- apps/member/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index 9a5c3166..a8b106d6 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -44,7 +44,7 @@ class ProfileForm(forms.ModelForm): """ A form for the extras field provided by the :model:`member.Profile` model. """ - report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency"), widget=django.forms.HiddenInput()) + report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency"), widget=forms.HiddenInput()) last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date")) From a209e0d36639df404727dea966850bbf1b4a268e Mon Sep 17 00:00:00 2001 From: thomasl Date: Sun, 2 Feb 2025 14:30:53 +0100 Subject: [PATCH 17/84] Update file forms.py --- apps/member/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/member/forms.py b/apps/member/forms.py index a8b106d6..352a5625 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -44,6 +44,7 @@ class ProfileForm(forms.ModelForm): """ A form for the extras field provided by the :model:`member.Profile` model. """ + # Remove widget=forms.HiddenInput() if you want to use report frequency. report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency"), widget=forms.HiddenInput()) last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date")) @@ -77,7 +78,7 @@ class ProfileForm(forms.ModelForm): model = Profile fields = '__all__' # Remove ml_[asso]_registration from exclude if the concerned association uses nk20 to manage its mailing list. - exclude = ('user', 'email_confirmed', 'registration_valid', 'ml_sport_registration', "ml_events_registration", ) + exclude = ('user', 'email_confirmed', 'registration_valid', 'ml_sport_registration', ) class ImageForm(forms.Form): From 7ed544b3acd8f0ea4217e432d8f7881261397811 Mon Sep 17 00:00:00 2001 From: quark Date: Sun, 9 Feb 2025 17:50:15 +0100 Subject: [PATCH 18/84] fix issues with activity entry view --- apps/activity/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/activity/views.py b/apps/activity/views.py index d559255d..579f7dc5 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -329,7 +329,7 @@ class ActivityEntryView(LoginRequiredMixin, SingleTableMixin, TemplateView): context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk - activities_open = Activity.objects.filter(open=True).filter( + activities_open = Activity.objects.filter(open=True,activity_type__manage_entries=True).filter( PermissionBackend.filter_queryset(self.request, Activity, "view")).distinct().all() context["activities_open"] = [a for a in activities_open if PermissionBackend.check_perm(self.request, From 0d0fdef363087603869c0a0c3506b117ce33b243 Mon Sep 17 00:00:00 2001 From: quark Date: Sun, 9 Feb 2025 17:58:38 +0100 Subject: [PATCH 19/84] fix issue with activity entry view --- apps/activity/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/activity/views.py b/apps/activity/views.py index d559255d..17446947 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -329,7 +329,7 @@ class ActivityEntryView(LoginRequiredMixin, SingleTableMixin, TemplateView): context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk - activities_open = Activity.objects.filter(open=True).filter( + activities_open = Activity.objects.filter(open=True, activity_type__manage_entries=True).filter( PermissionBackend.filter_queryset(self.request, Activity, "view")).distinct().all() context["activities_open"] = [a for a in activities_open if PermissionBackend.check_perm(self.request, From cd942779ca7c4175d6f989bf5524516e13ab15b4 Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 11 Feb 2025 18:19:24 +0100 Subject: [PATCH 20/84] Wrapped apps --- apps/api/urls.py | 4 + apps/wrapped/__init__.py | 4 + apps/wrapped/admin.py | 16 ++++ apps/wrapped/api/__init__.py | 0 apps/wrapped/api/serializers.py | 28 ++++++ apps/wrapped/api/urls.py | 11 +++ apps/wrapped/api/views.py | 33 +++++++ apps/wrapped/apps.py | 10 +++ apps/wrapped/migrations/0001_initial.py | 86 ++++++++++++++++++ apps/wrapped/migrations/__init__.py | 0 apps/wrapped/models.py | 89 +++++++++++++++++++ apps/wrapped/tables.py | 72 +++++++++++++++ .../templates/wrapped/1/wrapped_view.html | 8 ++ .../templates/wrapped/wrapped_list.html | 62 +++++++++++++ apps/wrapped/urls.py | 13 +++ apps/wrapped/views.py | 63 +++++++++++++ note_kfet/settings/base.py | 1 + note_kfet/templates/base.html | 6 ++ note_kfet/urls.py | 1 + 19 files changed, 507 insertions(+) create mode 100644 apps/wrapped/__init__.py create mode 100644 apps/wrapped/admin.py create mode 100644 apps/wrapped/api/__init__.py create mode 100644 apps/wrapped/api/serializers.py create mode 100644 apps/wrapped/api/urls.py create mode 100644 apps/wrapped/api/views.py create mode 100644 apps/wrapped/apps.py create mode 100644 apps/wrapped/migrations/0001_initial.py create mode 100644 apps/wrapped/migrations/__init__.py create mode 100644 apps/wrapped/models.py create mode 100644 apps/wrapped/tables.py create mode 100644 apps/wrapped/templates/wrapped/1/wrapped_view.html create mode 100644 apps/wrapped/templates/wrapped/wrapped_list.html create mode 100644 apps/wrapped/urls.py create mode 100644 apps/wrapped/views.py diff --git a/apps/api/urls.py b/apps/api/urls.py index ef631004..ad2daf5f 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -47,6 +47,10 @@ if "wei" in settings.INSTALLED_APPS: from wei.api.urls import register_wei_urls register_wei_urls(router, 'wei') +if "wrapped" in settings.INSTALLED_APPS: + from wrapped.api.urls import register_wrapped_urls + register_wrapped_urls(router, 'wrapped') + app_name = 'api' # Wire up our API using automatic URL routing. diff --git a/apps/wrapped/__init__.py b/apps/wrapped/__init__.py new file mode 100644 index 00000000..e9c45ef0 --- /dev/null +++ b/apps/wrapped/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +default_app_config = 'activity.apps.WrappedConfig' diff --git a/apps/wrapped/admin.py b/apps/wrapped/admin.py new file mode 100644 index 00000000..96a2b397 --- /dev/null +++ b/apps/wrapped/admin.py @@ -0,0 +1,16 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.contrib import admin +from note_kfet.admin import admin_site + +from .models import Bde, Wrapped + + +@admin.register(Bde, site=admin_site) +class BdeAdmin(admin.ModelAdmin): + pass + +@admin.register(Wrapped, site=admin_site) +class WrappedAdmin(admin.ModelAdmin): + pass diff --git a/apps/wrapped/api/__init__.py b/apps/wrapped/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/wrapped/api/serializers.py b/apps/wrapped/api/serializers.py new file mode 100644 index 00000000..d156ae75 --- /dev/null +++ b/apps/wrapped/api/serializers.py @@ -0,0 +1,28 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from rest_framework import serializers + +from ..models import Wrapped, Bde + + +class WrappedSerializer(serializers.ModelSerializer): + """ + REST API Serializer for Wrapped. + The djangorestframework plugin will analyse the model `Wrapped` and parse all fields in the API. + """ + + class Meta: + model = Wrapped + fields = '__all__' + + +class BdeSerializer(serializers.ModelSerializer): + """ + REST API Serializer for Bde. + The djangorestframework plugin will analyse the model `Bde` and parse all fields in the API. + """ + + class Meta: + model = Bde + fields = '__all__' diff --git a/apps/wrapped/api/urls.py b/apps/wrapped/api/urls.py new file mode 100644 index 00000000..a989bb3c --- /dev/null +++ b/apps/wrapped/api/urls.py @@ -0,0 +1,11 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from .views import WrappedViewSet, BdeViewSet + +def register_wrapped_urls(router, path): + """ + Configure router for Wrapped REST API. + """ + router.register(path + '/wrapped', WrappedViewSet) + router.register(path + '/bde', BdeViewSet) diff --git a/apps/wrapped/api/views.py b/apps/wrapped/api/views.py new file mode 100644 index 00000000..5fef0c66 --- /dev/null +++ b/apps/wrapped/api/views.py @@ -0,0 +1,33 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from api.viewsets import ReadProtectedModelViewSet +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework.filters import SearchFilter + +from .serializers import WrappedSerializer, BdeSerializer +from ..models import Wrapped, Bde + +class WrappedViewSet(ReadProtectedModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `Wrapped` objects, serialize it to JSON with the given + serializer, then render it on /api/wrapped/wrapped/ + """ + queryset = Wrapped.objects.order_by('id') + serializer_class = WrappedSerializer + filter_backends = [DjangoFilterBackend, SearchFilter] + filterset_fields = ['note', 'bde', ] + search_fields = ['$note', ] + +class BdeViewSet(ReadProtectedModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `Bde` objects, serialize it to JSON with the given + serializer, then render it on /api/wrapped/bde/ + """ + queryset = Bde.objects.order_by('id') + serializer_class = BdeSerializer + filter_backends = [DjangoFilterBackend, SearchFilter] + filterset_fields = ['name', ] + search_fields = ['$name', ] diff --git a/apps/wrapped/apps.py b/apps/wrapped/apps.py new file mode 100644 index 00000000..83630287 --- /dev/null +++ b/apps/wrapped/apps.py @@ -0,0 +1,10 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class WrappedConfig(AppConfig): + name = 'wrapped' + verbose_name = _('wrapped') diff --git a/apps/wrapped/migrations/0001_initial.py b/apps/wrapped/migrations/0001_initial.py new file mode 100644 index 00000000..15ffbd30 --- /dev/null +++ b/apps/wrapped/migrations/0001_initial.py @@ -0,0 +1,86 @@ +# Generated by Django 4.2.15 on 2025-02-10 12:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("note", "0007_alter_note_polymorphic_ctype_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="Bde", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, verbose_name="name")), + ("date_start", models.DateTimeField(verbose_name="date start")), + ("date_end", models.DateTimeField(verbose_name="date end")), + ], + options={ + "verbose_name": "BDE", + "verbose_name_plural": "BDE", + }, + ), + migrations.CreateModel( + name="Wrapped", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "generated", + models.BooleanField(default=False, verbose_name="generated"), + ), + ("public", models.BooleanField(default=False, verbose_name="public")), + ( + "data_json", + models.TextField( + default="{}", + help_text="data in the wrapped and generated by the script generate_wrapped", + verbose_name="data json", + ), + ), + ( + "bde", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="wrapped.bde", + verbose_name="bde", + ), + ), + ( + "note", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="note.note", + verbose_name="note", + ), + ), + ], + options={ + "verbose_name": "Wrapped", + "verbose_name_plural": "Wrappeds", + "unique_together": {("note", "bde")}, + }, + ), + ] diff --git a/apps/wrapped/migrations/__init__.py b/apps/wrapped/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/wrapped/models.py b/apps/wrapped/models.py new file mode 100644 index 00000000..ea6f0efd --- /dev/null +++ b/apps/wrapped/models.py @@ -0,0 +1,89 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +import os + +from django.db import models +from django.utils.translation import gettext_lazy as _ +from note.models import Note + + +class Bde(models.Model): + """ + describe a BDE + """ + + name = models.CharField( + max_length=255, + verbose_name=_('name'), + ) + + date_start = models.DateTimeField( + verbose_name=_('date start'), + ) + + date_end = models.DateTimeField( + verbose_name=_('date end'), + ) + + class Meta: + verbose_name=_('BDE') + verbose_name_plural=_('BDE') + + def __str__(self): + return self.name + + +class Wrapped(models.Model): + """ + A Wrapped is associated to a note, a BDE year, + """ + generated = models.BooleanField( + verbose_name=_('generated'), + default=False, + ) + + public = models.BooleanField( + verbose_name=_('public'), + default=False, + ) + + bde = models.ForeignKey( + Bde, + on_delete=models.PROTECT, + related_name='+', + verbose_name=_('bde'), + ) + + note = models.ForeignKey( + Note, + on_delete=models.PROTECT, + related_name='+', + verbose_name=_('note'), + ) + + data_json = models.TextField( + default='{}', + verbose_name=_('data json'), + help_text=_('data in the wrapped and generated by the script generate_wrapped'), + ) + + class Meta: + verbose_name=_('Wrapped') + verbose_name_plural=_('Wrappeds') + unique_together=('note','bde') + + def __str__(self): + return 'NoteKfet Wrapped of {note} sponsored by {bde}'.format(bde=str(self.bde),note=str(self.note)) + def makepublic(self): + self.public = not self.public + self.save() + return + + @property + def data(self): + return json.load(self.data_json) + + @data.setter + def data(self, data): + self.data_json = json.dumps(data, indent=2) diff --git a/apps/wrapped/tables.py b/apps/wrapped/tables.py new file mode 100644 index 00000000..4c3cc107 --- /dev/null +++ b/apps/wrapped/tables.py @@ -0,0 +1,72 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.utils import timezone +from django.utils.html import escape +from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ +from note_kfet.middlewares import get_current_request +import django_tables2 as tables +from django_tables2 import A +from permission.backends import PermissionBackend +from note.templatetags.pretty_money import pretty_money + +from .models import Wrapped, Bde + +class WrappedTable(tables.Table): + """ + List all wrapped + """ + class Meta: + attrs = { + 'class': 'table table-condensed table-striped table-hover', + 'id': 'wrapped_table' + } + row_attrs = { + 'class': lambda record: 'bg-danger' if not record.generated else '', + } + model = Wrapped + template_name = 'django_tables2/bootstrap4.html' + fields = ('note', 'bde', 'public', ) + + view = tables.LinkColumn( + 'wrapped:wrapped_detail', + args=[A('pk')], + + attrs={ + 'td': {'class': 'col-sm-2'}, + 'a': { + 'class': 'btn btn-sm btn-primary', + 'data-turbolinks': 'false', + } + }, + text=_('view the wrapped'), + accessor='pk', + verbose_name=_('View'), + orderable=False, + ) + + public = tables.Column( + accessor="pk", + orderable=False, + attrs={ + "td": { + "id": lambda record: "makepublic_"+ str(record.pk), + "class" : 'col-sm-1', + "data-toggle": "tooltip", + "title": lambda record: + (_("Click to make this wrapped private") if record.public else + _("Click to make this wrapped public")) if PermissionBackend.check_perm( + get_current_request(), "wrapped.change_wrapped_public", record) else None, + "onclick" : lambda record: + 'makepublic(' + str(record.id) + ', ' + str(not record.public).lower() + ')' + if PermissionBackend.check_perm(get_current_request(), "wrapped.change_wrapped_public", + record) else None + } + }, + ) + + def render_public(self, value, record): + val = "✔" if record.public else "✖" + return val + diff --git a/apps/wrapped/templates/wrapped/1/wrapped_view.html b/apps/wrapped/templates/wrapped/1/wrapped_view.html new file mode 100644 index 00000000..48ad8efe --- /dev/null +++ b/apps/wrapped/templates/wrapped/1/wrapped_view.html @@ -0,0 +1,8 @@ +{% comment %} +Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% block content %} +{{ wrapped.data_json }} +{% endblock %} + diff --git a/apps/wrapped/templates/wrapped/wrapped_list.html b/apps/wrapped/templates/wrapped/wrapped_list.html new file mode 100644 index 00000000..fd6bcb7e --- /dev/null +++ b/apps/wrapped/templates/wrapped/wrapped_list.html @@ -0,0 +1,62 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load render_table from django_tables2 %} +{% load i18n %} + +{% block content %} +
+
+
+
+
{{ title }}
+
+
+ {% render_table table %} +
+
+
+
+{% endblock %} + +{% block extrajavascript %} + +{% endblock %} diff --git a/apps/wrapped/urls.py b/apps/wrapped/urls.py new file mode 100644 index 00000000..dde4458b --- /dev/null +++ b/apps/wrapped/urls.py @@ -0,0 +1,13 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.urls import path + +from . import views + +app_name = 'wrapped' + +urlpatterns = [ + path('', views.WrappedListView.as_view(), name='wrapped_list'), + path('/', views.WrappedDetailView.as_view(), name='wrapped_detail'), +] diff --git a/apps/wrapped/views.py b/apps/wrapped/views.py new file mode 100644 index 00000000..f6ec97c9 --- /dev/null +++ b/apps/wrapped/views.py @@ -0,0 +1,63 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from hashlib import md5 + +from django.conf import settings +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import PermissionDenied +from django.db import transaction +from django.db.models import F, Q +from django.http import HttpResponse +from django.urls import reverse_lazy +from django.utils import timezone +from django.utils.decorators import method_decorator +from django.utils.translation import gettext_lazy as _ +from django.views import View +from django.views.decorators.cache import cache_page +from django.views.generic import DetailView, TemplateView, UpdateView +from django.views.generic.list import ListView +from django_tables2.views import MultiTableMixin, SingleTableMixin, SingleTableView +from api.viewsets import is_regex +from note.models import Alias, NoteSpecial, NoteUser +from permission.backends import PermissionBackend +from permission.views import ProtectQuerysetMixin, ProtectedCreateView + +from .models import Wrapped +from .tables import WrappedTable + +class WrappedListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): + """ + Display all Wrapped, and classify by year + """ + model = Wrapped + table_class = WrappedTable + template_name = 'wrapped/wrapped_list.html' + extra_context = {'title': _("List of wrapped")} + + def get_queryset(self, **kwargs): + return super().get_queryset(**kwargs).distinct() + + def get_table_data(self): + return Wrapped.objects.filter(PermissionBackend.filter_queryset( + self.request, Wrapped, "change", field='public')).distinct().order_by("-bde__date_start") + + def get_context_data(self, **kwargs): + return super().get_context_data(**kwargs) + +class WrappedDetailView(ProtectQuerysetMixin, DetailView): + """ + View a wrapped + """ + model = Wrapped + template_name = 'wrapped/0/wrapped_view.html' #by default + + def get(self, *args, **kwargs): + bde_id = Wrapped.objects.get(pk=kwargs['pk']).bde.id + self.template_name = 'wrapped/' + str(bde_id) + '/wrapped_view.html' + return super().get(*args, **kwargs) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + return context diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index 8378448d..113cf626 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -79,6 +79,7 @@ INSTALLED_APPS = [ 'scripts', 'treasury', 'wei', + 'wrapped', ] MIDDLEWARE = [ diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index a84f29b2..4f87228a 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -107,6 +107,12 @@ SPDX-License-Identifier: GPL-3.0-or-later {% url 'wei:current_wei_detail' as url %} {% trans 'WEI' %} + {% endif %} + {% if "wrapped.wrapped"|model_list_length >= 1 %} + {% endif %} {% if request.user.is_authenticated %}
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 2af3257e..44ac4a35 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-17 11:57+0200\n" +"POT-Creation-Date: 2025-02-25 13:47+0100\n" "PO-Revision-Date: 2022-04-11 22:05+0200\n" "Last-Translator: bleizi \n" "Language-Team: French \n" @@ -18,38 +18,43 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Poedit 3.0\n" -#: apps/activity/apps.py:10 apps/activity/models.py:127 -#: apps/activity/models.py:167 -#: apps/activity/models.py:323 +#: apps/activity/api/serializers.py:77 +#, fuzzy +#| msgid "This friendship already exists" +msgid "This opener already exists" +msgstr "Cette amitié existe déjà" + +#: apps/activity/apps.py:10 apps/activity/models.py:129 +#: apps/activity/models.py:169 apps/activity/models.py:323 msgid "activity" msgstr "activité" -#: apps/activity/forms.py:34 +#: apps/activity/forms.py:35 msgid "The note of this club is inactive." msgstr "La note du club est inactive." -#: apps/activity/forms.py:41 apps/activity/models.py:142 +#: apps/activity/forms.py:42 apps/activity/models.py:142 msgid "The end date must be after the start date." msgstr "La date de fin doit être après celle de début." -#: apps/activity/forms.py:82 apps/activity/models.py:271 +#: apps/activity/forms.py:83 apps/activity/models.py:271 msgid "You can't invite someone once the activity is started." msgstr "" "Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré." -#: apps/activity/forms.py:85 apps/activity/models.py:274 +#: apps/activity/forms.py:86 apps/activity/models.py:274 msgid "This activity is not validated yet." msgstr "Cette activité n'est pas encore validée." -#: apps/activity/forms.py:95 apps/activity/models.py:282 +#: apps/activity/forms.py:96 apps/activity/models.py:282 msgid "This person has been already invited 5 times this year." msgstr "Cette personne a déjà été invitée 5 fois cette année." -#: apps/activity/forms.py:99 apps/activity/models.py:286 +#: apps/activity/forms.py:100 apps/activity/models.py:286 msgid "This person is already invited." msgstr "Cette personne est déjà invitée." -#: apps/activity/forms.py:103 apps/activity/models.py:290 +#: apps/activity/forms.py:104 apps/activity/models.py:290 msgid "You can't invite more than 3 people to this activity." msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." @@ -63,7 +68,7 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." #: apps/registration/templates/registration/future_profile_detail.html:16 #: apps/wei/models.py:67 apps/wei/models.py:131 apps/wei/tables.py:282 #: apps/wei/templates/wei/base.html:26 -#: apps/wei/templates/wei/weimembership_form.html:14 +#: apps/wei/templates/wei/weimembership_form.html:14 apps/wrapped/models.py:16 msgid "name" msgstr "nom" @@ -115,7 +120,7 @@ msgstr "Lieu où l'activité est organisée, par exemple la Kfet." msgid "type" msgstr "type" -#: apps/activity/models.py:91 apps/logs/models.py:22 apps/member/models.py:318 +#: apps/activity/models.py:91 apps/logs/models.py:22 apps/member/models.py:325 #: apps/note/models/notes.py:148 apps/treasury/models.py:293 #: apps/wei/models.py:171 apps/wei/templates/wei/attribute_bus_1A.html:13 #: apps/wei/templates/wei/survey.html:15 @@ -172,7 +177,7 @@ msgid "entry time" msgstr "heure d'entrée" #: apps/activity/models.py:180 apps/note/apps.py:14 -#: apps/note/models/notes.py:77 +#: apps/note/models/notes.py:77 apps/wrapped/models.py:60 msgid "note" msgstr "note" @@ -200,7 +205,7 @@ msgstr "Entrée de la note {note} pour l'activité « {activity} »" msgid "Already entered on " msgstr "Déjà rentré·e le " -#: apps/activity/models.py:204 apps/activity/tables.py:56 +#: apps/activity/models.py:204 apps/activity/tables.py:58 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "{:%d/%m/%Y %H:%M:%S}" @@ -239,68 +244,96 @@ msgstr "invité·e·s" msgid "Invitation" msgstr "Invitation" -#: apps/activity/models.py:330 -#: apps/activity/models.py:334 +#: apps/activity/models.py:330 apps/activity/models.py:334 msgid "Opener" msgstr "Ouvreur⋅se" #: apps/activity/models.py:335 -#: apps/activity/templates/activity_detail.html:16 +#: apps/activity/templates/activity/activity_detail.html:16 msgid "Openers" msgstr "Ouvreur⋅ses" -#: apps/activity/tables.py:27 +#: apps/activity/models.py:339 +#, fuzzy, python-brace-format +#| msgid "Entry for {note} to the activity {activity}" +msgid "{opener} is opener of activity {acivity}" +msgstr "Entrée de la note {note} pour l'activité « {activity} »" + +#: apps/activity/tables.py:29 msgid "The activity is currently open." msgstr "Cette activité est actuellement ouverte." -#: apps/activity/tables.py:28 +#: apps/activity/tables.py:30 msgid "The validation of the activity is pending." msgstr "La validation de cette activité est en attente." -#: apps/activity/tables.py:43 +#: apps/activity/tables.py:45 #: apps/member/templates/member/picture_update.html:18 -#: apps/treasury/tables.py:107 +#: apps/treasury/tables.py:110 msgid "Remove" msgstr "Supprimer" -#: apps/activity/tables.py:56 +#: apps/activity/tables.py:58 msgid "Entered on " msgstr "Entré·e le " -#: apps/activity/tables.py:58 +#: apps/activity/tables.py:60 msgid "remove" msgstr "supprimer" -#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:208 +#: apps/activity/tables.py:84 apps/note/forms.py:69 apps/treasury/models.py:208 msgid "Type" msgstr "Type" -#: apps/activity/tables.py:84 apps/member/forms.py:196 +#: apps/activity/tables.py:86 apps/member/forms.py:199 #: apps/registration/forms.py:91 apps/treasury/forms.py:131 -#: apps/wei/forms/registration.py:104 +#: apps/wei/forms/registration.py:110 msgid "Last name" msgstr "Nom de famille" -#: apps/activity/tables.py:86 apps/member/forms.py:201 +#: apps/activity/tables.py:88 apps/member/forms.py:204 #: apps/note/templates/note/transaction_form.html:138 #: apps/registration/forms.py:96 apps/treasury/forms.py:133 -#: apps/wei/forms/registration.py:109 +#: apps/wei/forms/registration.py:115 msgid "First name" msgstr "Prénom" -#: apps/activity/tables.py:88 apps/note/models/notes.py:86 +#: apps/activity/tables.py:90 apps/note/models/notes.py:86 msgid "Note" msgstr "Note" -#: apps/activity/tables.py:90 apps/member/tables.py:50 +#: apps/activity/tables.py:92 apps/member/tables.py:50 msgid "Balance" msgstr "Solde du compte" -#: apps/activity/templates/activity/activity_detail.html:15 +#: apps/activity/tables.py:141 apps/activity/tables.py:148 +#: apps/note/tables.py:166 apps/note/tables.py:173 apps/note/tables.py:234 +#: apps/note/tables.py:281 apps/treasury/tables.py:39 +#: apps/treasury/templates/treasury/invoice_confirm_delete.html:30 +#: apps/treasury/templates/treasury/sogecredit_detail.html:65 +#: apps/wei/tables.py:75 apps/wei/tables.py:118 +#: apps/wei/templates/wei/weiregistration_confirm_delete.html:31 +#: note_kfet/templates/oauth2_provider/application_confirm_delete.html:18 +#: note_kfet/templates/oauth2_provider/application_detail.html:39 +#: note_kfet/templates/oauth2_provider/authorized-token-delete.html:12 +msgid "Delete" +msgstr "Supprimer" + +#: apps/activity/templates/activity/activity_detail.html:24 +#: apps/member/templates/member/club_alias.html:20 +#: apps/member/templates/member/profile_alias.html:19 +#: apps/member/templates/member/profile_trust.html:19 +#: apps/treasury/tables.py:101 +#: apps/treasury/templates/treasury/sogecredit_list.html:34 +#: apps/treasury/templates/treasury/sogecredit_list.html:73 +msgid "Add" +msgstr "Ajouter" + +#: apps/activity/templates/activity/activity_detail.html:35 msgid "Guests list" msgstr "Liste des invité·e·s" -#: apps/activity/templates/activity/activity_detail.html:33 +#: apps/activity/templates/activity/activity_detail.html:55 msgid "Guest deleted" msgstr "Invité·e supprimé·e" @@ -347,7 +380,7 @@ msgstr "Entrée effectuée !" #: apps/activity/templates/activity/activity_form.html:16 #: apps/food/templates/food/add_ingredient_form.html:16 #: apps/food/templates/food/basicfood_form.html:16 -#: apps/food/templates/food/create_qrcode_form.html:19 +#: apps/food/templates/food/create_qrcode_form.html:20 #: apps/food/templates/food/transformedfood_form.html:16 #: apps/member/templates/member/add_members.html:46 #: apps/member/templates/member/club_form.html:16 @@ -414,41 +447,41 @@ msgstr "modifier" msgid "Invite" msgstr "Inviter" -#: apps/activity/views.py:37 +#: apps/activity/views.py:38 msgid "Create new activity" msgstr "Créer une nouvelle activité" -#: apps/activity/views.py:67 note_kfet/templates/base.html:96 +#: apps/activity/views.py:71 note_kfet/templates/base.html:96 msgid "Activities" msgstr "Activités" -#: apps/activity/views.py:108 +#: apps/activity/views.py:105 msgid "Activity detail" msgstr "Détails de l'activité" -#: apps/activity/views.py:128 +#: apps/activity/views.py:150 msgid "Update activity" msgstr "Modifier l'activité" -#: apps/activity/views.py:155 +#: apps/activity/views.py:177 msgid "Invite guest to the activity \"{}\"" msgstr "Invitation pour l'activité « {} »" -#: apps/activity/views.py:193 +#: apps/activity/views.py:217 msgid "You are not allowed to display the entry interface for this activity." msgstr "" "Vous n'êtes pas autorisé·e à afficher l'interface des entrées pour cette " "activité." -#: apps/activity/views.py:196 +#: apps/activity/views.py:220 msgid "This activity does not support activity entries." msgstr "Cette activité ne requiert pas d'entrées." -#: apps/activity/views.py:199 +#: apps/activity/views.py:223 msgid "This activity is closed." msgstr "Cette activité est fermée." -#: apps/activity/views.py:295 +#: apps/activity/views.py:328 msgid "Entry for activity \"{}\"" msgstr "Entrées pour l'activité « {} »" @@ -468,7 +501,7 @@ msgstr "Entièrement utilisé" msgid "Pasta METRO 5kg" msgstr "Pâtes METRO 5kg" -#: apps/food/forms.py:96 +#: apps/food/forms.py:100 msgid "Lasagna" msgstr "Lasagnes" @@ -554,7 +587,7 @@ msgstr "ingrédients tranformées" msgid "shelf life" msgstr "durée de vie" -#: apps/food/models.py:225 apps/food/views.py:365 +#: apps/food/models.py:225 apps/food/views.py:375 msgid "Transformed food" msgstr "Aliment transformé" @@ -562,20 +595,20 @@ msgstr "Aliment transformé" msgid "Transformed foods" msgstr "Aliments transformés" -#: apps/food/templates/food/create_qrcode_form.html:31 #: apps/food/templates/food/basicfood_detail.html:14 +#: apps/food/templates/food/create_qrcode_form.html:31 #: apps/food/templates/food/qrcode_detail.html:15 #: apps/food/templates/food/transformedfood_detail.html:14 msgid "Owner" msgstr "Propriétaire" -#: apps/food/templates/food/create_qrcode_form.html:34 #: apps/food/templates/food/basicfood_detail.html:15 +#: apps/food/templates/food/create_qrcode_form.html:34 msgid "Arrival date" msgstr "Date d'arrivée" -#: apps/food/templates/food/create_qrcode_form.html:37 #: apps/food/templates/food/basicfood_detail.html:16 +#: apps/food/templates/food/create_qrcode_form.html:37 #: apps/food/templates/food/qrcode_detail.html:16 #: apps/food/templates/food/transformedfood_detail.html:19 msgid "Expiry date" @@ -604,7 +637,7 @@ msgstr "Modifier" msgid "Add to a meal" msgstr "Ajouter à un plat" -#: apps/food/templates/food/create_qrcode_form.html:14 +#: apps/food/templates/food/create_qrcode_form.html:15 msgid "New basic food" msgstr "Nouvel aliment basique" @@ -612,10 +645,6 @@ msgstr "Nouvel aliment basique" msgid "Copy constructor" msgstr "Constructeur de copie" -#: apps/food/templates/food/qrcode_detail.html:10 -msgid "number" -msgstr "numéro" - #: apps/food/templates/food/create_qrcode_form.html:28 #: apps/food/templates/food/qrcode_detail.html:14 #: apps/note/templates/note/transaction_form.html:132 @@ -623,6 +652,10 @@ msgstr "numéro" msgid "Name" msgstr "Nom" +#: apps/food/templates/food/qrcode_detail.html:10 +msgid "number" +msgstr "numéro" + #: apps/food/templates/food/qrcode_detail.html:29 msgid "View details" msgstr "Voir plus" @@ -692,19 +725,19 @@ msgstr "Détails de:" msgid "Add a new basic food with QRCode" msgstr "Ajouter un nouvel ingrédient avec un QR-code" -#: apps/food/views.py:185 +#: apps/food/views.py:194 msgid "Add a new QRCode" msgstr "Ajouter un nouveau QR-code" -#: apps/food/views.py:235 +#: apps/food/views.py:245 msgid "QRCode" msgstr "QR-code" -#: apps/food/views.py:271 +#: apps/food/views.py:281 msgid "Add a new meal" msgstr "Ajouter un nouveau plat" -#: apps/food/views.py:337 +#: apps/food/views.py:347 msgid "Update a meal" msgstr "Modifier le plat" @@ -736,7 +769,7 @@ msgstr "nouvelles données" msgid "create" msgstr "créer" -#: apps/logs/models.py:65 apps/note/tables.py:230 apps/note/tables.py:277 +#: apps/logs/models.py:65 apps/note/tables.py:230 apps/note/tables.py:279 #: apps/permission/models.py:126 apps/treasury/tables.py:38 #: apps/wei/tables.py:74 msgid "delete" @@ -777,11 +810,11 @@ msgstr "cotisation pour adhérer (normalien·ne élève)" msgid "membership fee (unpaid students)" msgstr "cotisation pour adhérer (normalien·ne étudiant·e)" -#: apps/member/admin.py:65 apps/member/models.py:330 +#: apps/member/admin.py:65 apps/member/models.py:337 msgid "roles" msgstr "rôles" -#: apps/member/admin.py:66 apps/member/models.py:344 +#: apps/member/admin.py:66 apps/member/models.py:351 msgid "fee" msgstr "cotisation" @@ -789,24 +822,24 @@ msgstr "cotisation" msgid "member" msgstr "adhérent·e" -#: apps/member/forms.py:24 +#: apps/member/forms.py:25 msgid "Permission mask" msgstr "Masque de permissions" -#: apps/member/forms.py:46 +#: apps/member/forms.py:48 msgid "Report frequency" msgstr "Fréquence des rapports (en jours)" -#: apps/member/forms.py:48 +#: apps/member/forms.py:50 msgid "Last report date" msgstr "Date de dernier rapport" -#: apps/member/forms.py:52 +#: apps/member/forms.py:54 msgid "" "Anti-VSS (Violences Sexistes et Sexuelles) charter read and approved" msgstr "Charte Anti-VSS (Violences Sexistes et Sexuelles) lue et approuvée" -#: apps/member/forms.py:53 +#: apps/member/forms.py:55 msgid "" "Tick after having read and accepted the anti-VSS charter " @@ -816,65 +849,65 @@ msgstr "" "crans.org/club-bde/Charte-anti-VSS.pdf target=_blank> disponible en pdf ici" -#: apps/member/forms.py:60 +#: apps/member/forms.py:62 msgid "You can't register to the note if you come from the future." msgstr "Vous ne pouvez pas vous inscrire à la note si vous venez du futur." -#: apps/member/forms.py:86 +#: apps/member/forms.py:89 msgid "select an image" msgstr "choisissez une image" -#: apps/member/forms.py:87 +#: apps/member/forms.py:90 msgid "Maximal size: 2MB" msgstr "Taille maximale : 2 Mo" -#: apps/member/forms.py:112 +#: apps/member/forms.py:115 msgid "This image cannot be loaded." msgstr "Cette image ne peut pas être chargée." -#: apps/member/forms.py:151 apps/member/views.py:102 -#: apps/registration/forms.py:33 apps/registration/views.py:276 +#: apps/member/forms.py:154 apps/member/views.py:103 +#: apps/registration/forms.py:33 apps/registration/views.py:282 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." -#: apps/member/forms.py:175 +#: apps/member/forms.py:178 msgid "Inscription paid by Société Générale" msgstr "Inscription payée par la Société générale" -#: apps/member/forms.py:177 +#: apps/member/forms.py:180 msgid "Check this case if the Société Générale paid the inscription." msgstr "Cochez cette case si la Société Générale a payé l'inscription." -#: apps/member/forms.py:182 apps/registration/forms.py:78 -#: apps/wei/forms/registration.py:91 +#: apps/member/forms.py:185 apps/registration/forms.py:78 +#: apps/wei/forms/registration.py:97 msgid "Credit type" msgstr "Type de rechargement" -#: apps/member/forms.py:183 apps/registration/forms.py:79 -#: apps/wei/forms/registration.py:92 +#: apps/member/forms.py:186 apps/registration/forms.py:79 +#: apps/wei/forms/registration.py:98 msgid "No credit" msgstr "Pas de rechargement" -#: apps/member/forms.py:185 +#: apps/member/forms.py:188 msgid "You can credit the note of the user." msgstr "Vous pouvez créditer la note de l'utilisateur⋅rice avant l'adhésion." -#: apps/member/forms.py:189 apps/registration/forms.py:84 -#: apps/wei/forms/registration.py:97 +#: apps/member/forms.py:192 apps/registration/forms.py:84 +#: apps/wei/forms/registration.py:103 msgid "Credit amount" msgstr "Montant à créditer" -#: apps/member/forms.py:206 apps/note/templates/note/transaction_form.html:144 +#: apps/member/forms.py:209 apps/note/templates/note/transaction_form.html:144 #: apps/registration/forms.py:101 apps/treasury/forms.py:135 -#: apps/wei/forms/registration.py:114 +#: apps/wei/forms/registration.py:120 msgid "Bank" msgstr "Banque" -#: apps/member/forms.py:233 +#: apps/member/forms.py:236 msgid "User" msgstr "Utilisateur⋅rice" -#: apps/member/forms.py:247 +#: apps/member/forms.py:250 msgid "Roles" msgstr "Rôles" @@ -1007,7 +1040,7 @@ msgstr "payé⋅e" msgid "Tells if the user receive a salary." msgstr "Indique si l'utilisateur⋅rice perçoit un salaire." -#: apps/member/models.py:99 apps/treasury/tables.py:143 +#: apps/member/models.py:99 apps/treasury/tables.py:149 msgid "No" msgstr "Non" @@ -1126,7 +1159,7 @@ msgstr "" msgid "add to registration form" msgstr "ajouter au formulaire d'inscription" -#: apps/member/models.py:268 apps/member/models.py:324 +#: apps/member/models.py:268 apps/member/models.py:331 #: apps/note/models/notes.py:176 msgid "club" msgstr "club" @@ -1135,37 +1168,37 @@ msgstr "club" msgid "clubs" msgstr "clubs" -#: apps/member/models.py:335 +#: apps/member/models.py:342 msgid "membership starts on" msgstr "l'adhésion commence le" -#: apps/member/models.py:339 +#: apps/member/models.py:346 msgid "membership ends on" msgstr "l'adhésion finit le" -#: apps/member/models.py:348 apps/note/models/transactions.py:385 +#: apps/member/models.py:355 apps/note/models/transactions.py:385 msgid "membership" msgstr "adhésion" -#: apps/member/models.py:349 +#: apps/member/models.py:356 msgid "memberships" msgstr "adhésions" -#: apps/member/models.py:353 +#: apps/member/models.py:360 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "Adhésion de {user} pour le club {club}" -#: apps/member/models.py:372 +#: apps/member/models.py:379 #, python-brace-format msgid "The role {role} does not apply to the club {club}." msgstr "Le rôle {role} ne s'applique pas au club {club}." -#: apps/member/models.py:381 apps/member/views.py:715 +#: apps/member/models.py:388 apps/member/views.py:745 msgid "User is already a member of the club" msgstr "L'utilisateur·rice est déjà membre du club" -#: apps/member/models.py:393 apps/member/views.py:724 +#: apps/member/models.py:400 apps/member/views.py:754 msgid "User is not a member of the parent club" msgstr "L'utilisateur·rice n'est pas membre du club parent" @@ -1180,8 +1213,8 @@ msgid "" "%(pretty_fee)s will be charged to renew automatically the membership in this/" "these club·s." msgstr "" -"Cet·te utilisateur·rice n'est pas membre du/des club·s parent·s %(clubs)s. Un " -"montant supplémentaire de %(pretty_fee)s sera débité afin de renouveler " +"Cet·te utilisateur·rice n'est pas membre du/des club·s parent·s %(clubs)s. " +"Un montant supplémentaire de %(pretty_fee)s sera débité afin de renouveler " "automatiquement l'adhésion dans ce·s club·s." #: apps/member/templates/member/add_members.html:22 @@ -1208,8 +1241,9 @@ msgid "" "This club has parents %(clubs)s. Please make sure that the user is a member " "of this or these club·s, otherwise the creation of this membership will fail." msgstr "" -"Ce club a pour parents %(clubs)s. Merci de vous assurer que l'utilisateur⋅rice " -"est membre de ce·s club·s, sinon la création de cette adhésion va échouer." +"Ce club a pour parents %(clubs)s. Merci de vous assurer que " +"l'utilisateur⋅rice est membre de ce·s club·s, sinon la création de cette " +"adhésion va échouer." #: apps/member/templates/member/base.html:17 #: apps/registration/templates/registration/future_profile_detail.html:12 @@ -1217,7 +1251,7 @@ msgid "Account #" msgstr "Compte n°" #: apps/member/templates/member/base.html:48 -#: apps/member/templates/member/base.html:62 apps/member/views.py:59 +#: apps/member/templates/member/base.html:62 apps/member/views.py:60 #: apps/registration/templates/registration/future_profile_detail.html:48 #: apps/wei/templates/wei/weimembership_form.html:117 msgid "Update Profile" @@ -1278,20 +1312,11 @@ msgstr "" "seront à nouveau possible." #: apps/member/templates/member/club_alias.html:10 -#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:287 -#: apps/member/views.py:520 +#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:304 +#: apps/member/views.py:545 msgid "Note aliases" msgstr "Alias de la note" -#: apps/member/templates/member/club_alias.html:20 -#: apps/member/templates/member/profile_alias.html:19 -#: apps/member/templates/member/profile_trust.html:19 -#: apps/treasury/tables.py:99 -#: apps/treasury/templates/treasury/sogecredit_list.html:34 -#: apps/treasury/templates/treasury/sogecredit_list.html:73 -msgid "Add" -msgstr "Ajouter" - #: apps/member/templates/member/club_detail.html:13 #: apps/permission/templates/permission/all_rights.html:32 msgid "Club managers" @@ -1480,51 +1505,51 @@ msgstr "Sauvegarder les changements" msgid "Registrations" msgstr "Inscriptions" -#: apps/member/views.py:72 apps/registration/forms.py:23 +#: apps/member/views.py:73 apps/registration/forms.py:23 msgid "This address must be valid." msgstr "Cette adresse doit être valide." -#: apps/member/views.py:139 +#: apps/member/views.py:140 msgid "Profile detail" msgstr "Détails de l'utilisateur⋅rice" -#: apps/member/views.py:205 +#: apps/member/views.py:206 msgid "Search user" msgstr "Chercher un·e utilisateur·rice" -#: apps/member/views.py:253 +#: apps/member/views.py:258 msgid "Note friendships" msgstr "Amitiés note" -#: apps/member/views.py:308 +#: apps/member/views.py:328 msgid "Update note picture" msgstr "Modifier la photo de la note" -#: apps/member/views.py:357 +#: apps/member/views.py:377 msgid "Manage auth token" msgstr "Gérer les jetons d'authentification" -#: apps/member/views.py:384 +#: apps/member/views.py:404 msgid "Create new club" msgstr "Créer un nouveau club" -#: apps/member/views.py:403 +#: apps/member/views.py:423 msgid "Search club" msgstr "Chercher un club" -#: apps/member/views.py:436 +#: apps/member/views.py:461 msgid "Club detail" msgstr "Détails du club" -#: apps/member/views.py:543 +#: apps/member/views.py:573 msgid "Update club" msgstr "Modifier le club" -#: apps/member/views.py:577 +#: apps/member/views.py:607 msgid "Add new member to the club" msgstr "Ajouter un·e nouvelleau membre au club" -#: apps/member/views.py:706 apps/wei/views.py:973 +#: apps/member/views.py:736 apps/wei/views.py:991 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -1532,19 +1557,19 @@ msgstr "" "Cet⋅te utilisateur⋅rice n'a pas assez d'argent pour rejoindre ce club et ne " "peut pas avoir un solde négatif." -#: apps/member/views.py:728 +#: apps/member/views.py:758 msgid "The membership must start after {:%m-%d-%Y}." msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}." -#: apps/member/views.py:733 +#: apps/member/views.py:763 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." -#: apps/member/views.py:883 +#: apps/member/views.py:913 msgid "Manage roles of an user in the club" msgstr "Gérer les rôles d'un⋅e utilisateur⋅rice dans le club" -#: apps/member/views.py:908 +#: apps/member/views.py:938 msgid "Members of the club" msgstr "Membres du club" @@ -1575,35 +1600,35 @@ msgstr "" "La transaction ne peut pas être sauvegardée puisque la note source ou la " "note de destination n'est pas active." -#: apps/note/forms.py:39 +#: apps/note/forms.py:40 msgid "Source" msgstr "Source" -#: apps/note/forms.py:53 +#: apps/note/forms.py:54 msgid "Destination" msgstr "Destination" -#: apps/note/forms.py:74 apps/note/templates/note/transaction_form.html:123 +#: apps/note/forms.py:75 apps/note/templates/note/transaction_form.html:123 msgid "Reason" msgstr "Raison" -#: apps/note/forms.py:79 apps/treasury/tables.py:136 +#: apps/note/forms.py:80 apps/treasury/tables.py:141 msgid "Valid" msgstr "Valide" -#: apps/note/forms.py:85 +#: apps/note/forms.py:86 msgid "Total amount greater than" msgstr "Montant total supérieur à" -#: apps/note/forms.py:93 +#: apps/note/forms.py:94 msgid "Total amount less than" msgstr "Montant total inférieur à" -#: apps/note/forms.py:99 +#: apps/note/forms.py:100 msgid "Created after" msgstr "Créé après" -#: apps/note/forms.py:106 +#: apps/note/forms.py:107 msgid "Created before" msgstr "Créé avant" @@ -1656,7 +1681,6 @@ msgstr "" "La note est bloquée de force par le BDE et ne peut pas être débloquée par læ " "propriétaire de la note." - #: apps/note/models/notes.py:78 msgid "notes" msgstr "notes" @@ -1708,7 +1732,9 @@ msgid "trusted" msgstr "ami·e" #: apps/note/models/notes.py:243 -msgid "friendship" +#, fuzzy +#| msgid "friendship" +msgid "frienship" msgstr "amitié" #: apps/note/models/notes.py:248 @@ -1860,8 +1886,8 @@ msgstr "" "mode de paiement et un⋅e utilisateur⋅rice ou un club" #: apps/note/models/transactions.py:357 apps/note/models/transactions.py:360 -#: apps/note/models/transactions.py:363 apps/wei/views.py:978 -#: apps/wei/views.py:982 +#: apps/note/models/transactions.py:363 apps/wei/views.py:996 +#: apps/wei/views.py:1000 msgid "This field is required." msgstr "Ce champ est requis." @@ -1885,18 +1911,6 @@ msgstr "Cliquez pour valider" msgid "No reason specified" msgstr "Pas de motif spécifié" -#: apps/note/tables.py:166 apps/note/tables.py:173 apps/note/tables.py:234 -#: apps/note/tables.py:279 apps/treasury/tables.py:39 -#: apps/treasury/templates/treasury/invoice_confirm_delete.html:30 -#: apps/treasury/templates/treasury/sogecredit_detail.html:65 -#: apps/wei/tables.py:75 apps/wei/tables.py:118 -#: apps/wei/templates/wei/weiregistration_confirm_delete.html:31 -#: note_kfet/templates/oauth2_provider/application_confirm_delete.html:18 -#: note_kfet/templates/oauth2_provider/application_detail.html:39 -#: note_kfet/templates/oauth2_provider/authorized-token-delete.html:12 -msgid "Delete" -msgstr "Supprimer" - #: apps/note/tables.py:191 msgid "Trust back" msgstr "Ajouter en ami·e" @@ -1915,7 +1929,7 @@ msgstr "Ajouter" msgid "Edit" msgstr "Éditer" -#: apps/note/tables.py:266 apps/note/tables.py:293 +#: apps/note/tables.py:267 apps/note/tables.py:296 msgid "Hide/Show" msgstr "Afficher/Masquer" @@ -1975,6 +1989,10 @@ msgstr "Historique des transactions récentes" #: apps/note/templates/note/mails/weekly_report.txt:32 #: apps/registration/templates/registration/mails/email_validation_email.html:40 #: apps/registration/templates/registration/mails/email_validation_email.txt:16 +#: apps/scripts/templates/scripts/horaires.html:35 +#: apps/scripts/templates/scripts/horaires.txt:17 +#: apps/scripts/templates/scripts/intro_mail.html:49 +#: apps/scripts/templates/scripts/intro_mail.txt:25 msgid "Mail generated by the Note Kfet on the" msgstr "Mail généré par la Note Kfet le" @@ -1997,8 +2015,8 @@ msgid "Action" msgstr "Action" #: apps/note/templates/note/transaction_form.html:116 -#: apps/treasury/forms.py:137 apps/treasury/tables.py:67 -#: apps/treasury/tables.py:132 +#: apps/treasury/forms.py:137 apps/treasury/tables.py:68 +#: apps/treasury/tables.py:136 #: apps/treasury/templates/treasury/remittance_form.html:23 msgid "Amount" msgstr "Montant" @@ -2040,15 +2058,21 @@ msgid "New button" msgstr "Nouveau bouton" #: apps/note/templates/note/transactiontemplate_list.html:22 -msgid "buttons listing" +#, fuzzy +#| msgid "buttons listing" +msgid "buttons listing " msgstr "liste des boutons" #: apps/note/templates/note/transactiontemplate_list.html:73 -msgid "button successfully deleted" +#, fuzzy +#| msgid "button successfully deleted" +msgid "button successfully deleted " msgstr "le bouton a bien été supprimé" #: apps/note/templates/note/transactiontemplate_list.html:77 -msgid "Unable to delete button" +#, fuzzy +#| msgid "Unable to delete button" +msgid "Unable to delete button " msgstr "Impossible de supprimer le bouton" #: apps/note/templates/note/transactiontemplate_list.html:95 @@ -2060,34 +2084,35 @@ msgid "Button displayed" msgstr "Bouton affiché" #: apps/note/templates/note/transactiontemplate_list.html:100 +#: apps/wrapped/templates/wrapped/wrapped_list.html:63 msgid "An error occured" msgstr "Une erreur s'est produite" -#: apps/note/views.py:36 +#: apps/note/views.py:37 msgid "Transfer money" msgstr "Transférer de l'argent" -#: apps/note/views.py:74 +#: apps/note/views.py:75 msgid "Create new button" msgstr "Créer un nouveau bouton" -#: apps/note/views.py:83 +#: apps/note/views.py:84 msgid "Search button" msgstr "Chercher un bouton" -#: apps/note/views.py:111 +#: apps/note/views.py:116 msgid "Update button" msgstr "Modifier le bouton" -#: apps/note/views.py:151 note_kfet/templates/base.html:66 +#: apps/note/views.py:156 note_kfet/templates/base.html:66 msgid "Consumptions" msgstr "Consommations" -#: apps/note/views.py:165 +#: apps/note/views.py:170 msgid "You can't see any button." msgstr "Vous ne pouvez pas voir le moindre bouton." -#: apps/note/views.py:204 +#: apps/note/views.py:209 msgid "Search transactions" msgstr "Rechercher des transactions" @@ -2181,7 +2206,7 @@ msgstr "" "Vous n'avez pas la permission de modifier le champ {field} sur l'instance du " "modèle {app_label}.{model_name}." -#: apps/permission/signals.py:83 apps/permission/views.py:105 +#: apps/permission/signals.py:83 apps/permission/views.py:104 #, python-brace-format msgid "" "You don't have the permission to add an instance of model {app_label}." @@ -2206,7 +2231,8 @@ msgstr "Liste des utilisateur·rice·s ayant des droits surnormaux" #: apps/permission/templates/permission/all_rights.html:16 msgid "Superusers have all rights on everything, to manage the website." msgstr "" -"Les super-utilisateur·rice·s ont tous les droits sur tout, afin de gérer le site." +"Les super-utilisateur·rice·s ont tous les droits sur tout, afin de gérer le " +"site." #: apps/permission/templates/permission/all_rights.html:21 msgid "Superusers" @@ -2259,7 +2285,7 @@ msgstr "Cliquez ici" msgid "if you want to register a new one" msgstr "si vous voulez en enregistrer une nouvelle" -#: apps/permission/views.py:72 +#: apps/permission/views.py:71 #, python-brace-format msgid "" "You don't have the permission to update this instance of the model " @@ -2268,7 +2294,7 @@ msgstr "" "Vous n'avez pas la permission de modifier cette instance du modèle « {model} " "» avec ces paramètres. Merci de les corriger et de réessayer." -#: apps/permission/views.py:76 +#: apps/permission/views.py:75 #, python-brace-format msgid "" "You don't have the permission to create an instance of the model \"{model}\" " @@ -2277,11 +2303,11 @@ msgstr "" "Vous n'avez pas la permission d'ajouter une instance du modèle « {model} » " "avec ces paramètres. Merci de les corriger et de réessayer." -#: apps/permission/views.py:112 note_kfet/templates/base.html:114 +#: apps/permission/views.py:111 note_kfet/templates/base.html:120 msgid "Rights" msgstr "Droits" -#: apps/permission/views.py:117 +#: apps/permission/views.py:137 msgid "All rights" msgstr "Tous les droits" @@ -2365,7 +2391,8 @@ msgstr "Valider le compte" #: apps/registration/templates/registration/future_profile_detail.html:63 msgid "" "The user declared that he/she opened a bank account in the Société générale." -msgstr "L'utilisateur·rice a déclaré avoir ouvert un compte à la société générale." +msgstr "" +"L'utilisateur·rice a déclaré avoir ouvert un compte à la société générale." #: apps/registration/templates/registration/future_profile_detail.html:73 #: apps/wei/templates/wei/weimembership_form.html:127 @@ -2417,59 +2444,67 @@ msgstr "Merci" msgid "The Note Kfet team." msgstr "L'équipe de la Note Kfet." -#: apps/registration/views.py:42 +#: apps/registration/views.py:43 msgid "Register new user" msgstr "Enregistrer un⋅e nouvel⋅le utilisateur⋅rice" -#: apps/registration/views.py:100 +#: apps/registration/views.py:101 msgid "Email validation" msgstr "Validation de l'adresse e-mail" -#: apps/registration/views.py:102 +#: apps/registration/views.py:103 msgid "Validate email" msgstr "Valider l'adresse e-mail" -#: apps/registration/views.py:146 +#: apps/registration/views.py:147 msgid "Email validation unsuccessful" msgstr "La validation de l'adresse e-mail a échoué" -#: apps/registration/views.py:157 +#: apps/registration/views.py:158 msgid "Email validation email sent" msgstr "L'e-mail de vérification de l'adresse e-mail a bien été envoyé" -#: apps/registration/views.py:165 +#: apps/registration/views.py:166 msgid "Resend email validation link" msgstr "Renvoyer le lien de validation" -#: apps/registration/views.py:183 +#: apps/registration/views.py:184 msgid "Pre-registered users list" msgstr "Liste des utilisateur⋅rices en attente d'inscription" -#: apps/registration/views.py:207 +#: apps/registration/views.py:213 msgid "Unregistered users" msgstr "Utilisateur·rices en attente d'inscription" -#: apps/registration/views.py:220 +#: apps/registration/views.py:226 msgid "Registration detail" msgstr "Détails de l'inscription" -#: apps/registration/views.py:256 +#: apps/registration/views.py:262 #, python-format msgid "Join %(club)s Club" msgstr "Adhérer au club %(club)s" -#: apps/registration/views.py:299 -msgid "You must join the BDE." +#: apps/registration/views.py:305 +#, fuzzy +#| msgid "You must join the BDE." +msgid "You must join a club." msgstr "Vous devez adhérer au BDE." -#: apps/registration/views.py:330 +#: apps/registration/views.py:309 +#, fuzzy +#| msgid "You must join the BDE." +msgid "You must also join the parent club BDE." +msgstr "Vous devez adhérer au BDE." + +#: apps/registration/views.py:340 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" "Le montant crédité est trop faible pour adhérer, il doit être au minimum de " "{}" -#: apps/registration/views.py:425 +#: apps/registration/views.py:435 msgid "Invalidate pre-registration" msgstr "Invalider l'inscription" @@ -2491,7 +2526,7 @@ msgid "You can't change the type of the remittance." msgstr "Vous ne pouvez pas changer le type de la remise." #: apps/treasury/forms.py:125 apps/treasury/models.py:275 -#: apps/treasury/tables.py:97 apps/treasury/tables.py:105 +#: apps/treasury/tables.py:99 apps/treasury/tables.py:108 #: apps/treasury/templates/treasury/invoice_list.html:16 #: apps/treasury/templates/treasury/remittance_list.html:16 #: apps/treasury/templates/treasury/sogecredit_list.html:17 @@ -2506,7 +2541,8 @@ msgstr "Pas de remise associée" msgid "Invoice identifier" msgstr "Numéro de facture" -#: apps/treasury/models.py:42 +#: apps/treasury/models.py:42 apps/wrapped/models.py:28 +#: apps/wrapped/models.py:29 msgid "BDE" msgstr "BDE" @@ -2642,8 +2678,9 @@ msgid "" "This user doesn't have enough money to pay the memberships with its note. " "Please ask her/him to credit the note before invalidating this credit." msgstr "" -"Cet·te utilisateur·rice n'a pas assez d'argent pour payer les adhésions avec sa " -"note. Merci de lui demander de recharger sa note avant d'invalider ce crédit." +"Cet·te utilisateur·rice n'a pas assez d'argent pour payer les adhésions avec " +"sa note. Merci de lui demander de recharger sa note avant d'invalider ce " +"crédit." #: apps/treasury/tables.py:20 msgid "Invoice #{:d}" @@ -2656,25 +2693,26 @@ msgstr "Facture n°{:d}" msgid "Invoice" msgstr "Facture" -#: apps/treasury/tables.py:65 +#: apps/treasury/tables.py:66 msgid "Transaction count" msgstr "Nombre de transactions" -#: apps/treasury/tables.py:70 apps/treasury/tables.py:72 +#: apps/treasury/tables.py:71 apps/treasury/tables.py:73 +#: apps/wrapped/tables.py:42 msgid "View" msgstr "Voir" -#: apps/treasury/tables.py:143 +#: apps/treasury/tables.py:149 msgid "Yes" msgstr "Oui" #: apps/treasury/templates/treasury/invoice_confirm_delete.html:10 -#: apps/treasury/views.py:173 +#: apps/treasury/views.py:174 msgid "Delete invoice" msgstr "Supprimer la facture" #: apps/treasury/templates/treasury/invoice_confirm_delete.html:15 -#: apps/treasury/views.py:177 +#: apps/treasury/views.py:178 msgid "This invoice is locked and can't be deleted." msgstr "Cette facture est verrouillée et ne peut pas être supprimée." @@ -2794,8 +2832,8 @@ msgid "" "If this credit is validated, then the user won't be able to ask for a credit " "from the Société générale." msgstr "" -"Si ce crédit est validé, alors l'utilisateur·rice ne pourra plus demander d'être " -"crédité·e par la Société générale à l'avenir." +"Si ce crédit est validé, alors l'utilisateur·rice ne pourra plus demander " +"d'être crédité·e par la Société générale à l'avenir." #: apps/treasury/templates/treasury/sogecredit_detail.html:44 msgid "If you think there is an error, please contact the \"respos info\"." @@ -2811,14 +2849,14 @@ msgid "" "Warning: if you don't validate this credit, the note of the user doesn't " "have enough money to pay its memberships." msgstr "" -"Attention : si vous ne validez pas ce crédit, la note de l'utilisateur·rice n'a " -"pas assez d'argent pour payer les adhésions." +"Attention : si vous ne validez pas ce crédit, la note de l'utilisateur·rice " +"n'a pas assez d'argent pour payer les adhésions." #: apps/treasury/templates/treasury/sogecredit_detail.html:56 msgid "Please ask the user to credit its note before deleting this credit." msgstr "" -"Merci de demander à l'utilisateur·rice de recharger sa note avant de supprimer la " -"demande de crédit." +"Merci de demander à l'utilisateur·rice de recharger sa note avant de " +"supprimer la demande de crédit." #: apps/treasury/templates/treasury/sogecredit_detail.html:63 #: apps/wei/tables.py:60 apps/wei/tables.py:102 @@ -2836,8 +2874,8 @@ msgstr "Filtrer avec uniquement les crédits non valides" #: apps/treasury/templates/treasury/sogecredit_list.html:50 msgid "There is no matched user that have asked for a Société générale credit." msgstr "" -"Il n'y a pas d'utilisateur·rice trouvé·e ayant demandé un crédit de la Société " -"générale." +"Il n'y a pas d'utilisateur·rice trouvé·e ayant demandé un crédit de la " +"Société générale." #: apps/treasury/templates/treasury/sogecredit_list.html:63 msgid "Add credit from the Société générale" @@ -2847,44 +2885,44 @@ msgstr "Ajouter un crédit de la Société générale" msgid "Credit successfully registered" msgstr "Le crédit a bien été enregistré" -#: apps/treasury/views.py:40 +#: apps/treasury/views.py:41 msgid "Create new invoice" msgstr "Créer une nouvelle facture" -#: apps/treasury/views.py:97 +#: apps/treasury/views.py:98 msgid "Invoices list" msgstr "Liste des factures" -#: apps/treasury/views.py:105 apps/treasury/views.py:275 -#: apps/treasury/views.py:401 +#: apps/treasury/views.py:106 apps/treasury/views.py:281 +#: apps/treasury/views.py:394 msgid "You are not able to see the treasury interface." msgstr "Vous n'êtes pas autorisé·e à voir l'interface de trésorerie." -#: apps/treasury/views.py:115 +#: apps/treasury/views.py:116 msgid "Update an invoice" msgstr "Modifier la facture" -#: apps/treasury/views.py:240 +#: apps/treasury/views.py:241 msgid "Create a new remittance" msgstr "Créer une nouvelle remise" -#: apps/treasury/views.py:267 +#: apps/treasury/views.py:265 msgid "Remittances list" msgstr "Liste des remises" -#: apps/treasury/views.py:326 +#: apps/treasury/views.py:320 msgid "Update a remittance" msgstr "Modifier la remise" -#: apps/treasury/views.py:349 +#: apps/treasury/views.py:342 msgid "Attach a transaction to a remittance" msgstr "Joindre une transaction à une remise" -#: apps/treasury/views.py:393 +#: apps/treasury/views.py:386 msgid "List of credits from the Société générale" msgstr "Liste des crédits de la Société générale" -#: apps/treasury/views.py:438 +#: apps/treasury/views.py:436 msgid "Manage credits from the Société générale" msgstr "Gérer les crédits de la Société générale" @@ -2894,31 +2932,31 @@ msgstr "Gérer les crédits de la Société générale" msgid "WEI" msgstr "WEI" -#: apps/wei/forms/registration.py:35 +#: apps/wei/forms/registration.py:36 msgid "The selected user is not validated. Please validate its account first" msgstr "" -"L'utilisateur·rice sélectionné·e n'est pas validé·e. Merci de d'abord valider son " -"compte" +"L'utilisateur·rice sélectionné·e n'est pas validé·e. Merci de d'abord " +"valider son compte" -#: apps/wei/forms/registration.py:59 apps/wei/models.py:126 +#: apps/wei/forms/registration.py:60 apps/wei/models.py:126 #: apps/wei/models.py:324 msgid "bus" msgstr "bus" -#: apps/wei/forms/registration.py:60 +#: apps/wei/forms/registration.py:61 msgid "" "This choice is not definitive. The WEI organizers are free to attribute for " "you a bus and a team, in particular if you are a free eletron." msgstr "" -"Ce choix n'est pas définitif. Les organisateur·rice·s du WEI sont libres de vous " -"attribuer un bus et une équipe, en particulier si vous êtes un·e électron " -"libre." +"Ce choix n'est pas définitif. Les organisateur·rice·s du WEI sont libres de " +"vous attribuer un bus et une équipe, en particulier si vous êtes un·e " +"électron libre." -#: apps/wei/forms/registration.py:67 +#: apps/wei/forms/registration.py:68 msgid "Team" msgstr "Équipe" -#: apps/wei/forms/registration.py:69 +#: apps/wei/forms/registration.py:70 msgid "" "Leave this field empty if you won't be in a team (staff, bus chief, free " "electron)" @@ -2926,16 +2964,20 @@ msgstr "" "Laissez ce champ vide si vous ne serez pas dans une équipe (staff, chef de " "bus ou électron libre)" -#: apps/wei/forms/registration.py:75 apps/wei/forms/registration.py:85 +#: apps/wei/forms/registration.py:76 apps/wei/forms/registration.py:91 #: apps/wei/models.py:160 msgid "WEI Roles" msgstr "Rôles au WEI" -#: apps/wei/forms/registration.py:76 +#: apps/wei/forms/registration.py:77 msgid "Select the roles that you are interested in." msgstr "Sélectionnez les rôles qui vous intéressent." -#: apps/wei/forms/registration.py:122 +#: apps/wei/forms/registration.py:86 apps/wei/models.py:188 +msgid "Caution check given" +msgstr "Chèque de caution donné" + +#: apps/wei/forms/registration.py:128 msgid "This team doesn't belong to the given bus." msgstr "Cette équipe n'appartient pas à ce bus." @@ -2948,10 +2990,12 @@ msgid "year" msgstr "année" #: apps/wei/models.py:29 apps/wei/templates/wei/base.html:30 +#: apps/wrapped/models.py:20 msgid "date start" msgstr "début" #: apps/wei/models.py:33 apps/wei/templates/wei/base.html:33 +#: apps/wrapped/models.py:24 msgid "date end" msgstr "fin" @@ -3001,11 +3045,6 @@ msgstr "Rôle au WEI" msgid "Credit from Société générale" msgstr "Crédit de la Société générale" -#: apps/wei/models.py:188 -#: apps/wei/forms/registration.py:84 -msgid "Caution check given" -msgstr "Chèque de caution donné" - #: apps/wei/models.py:192 apps/wei/templates/wei/weimembership_form.html:64 msgid "birth date" msgstr "date de naissance" @@ -3039,8 +3078,7 @@ msgstr "coupe de vêtement" msgid "clothing size" msgstr "taille de vêtement" -#: apps/wei/models.py:232 apps/wei/templates/wei/attribute_bus_1A.html:28 -#: apps/wei/templates/wei/weimembership_form.html:67 +#: apps/wei/models.py:232 msgid "health issues" msgstr "problèmes de santé" @@ -3160,13 +3198,22 @@ msgid "Attribute first year members into buses" msgstr "Attribuer les 1A dans les bus" #: apps/wei/templates/wei/1A_list.html:15 -msgid "Start attribution!" +#, fuzzy +#| msgid "Start attribution!" +msgid "Start attribution !" msgstr "Démarrer l'attribution !" #: apps/wei/templates/wei/attribute_bus_1A.html:8 msgid "Bus attribution" msgstr "Répartition des bus" +#: apps/wei/templates/wei/attribute_bus_1A.html:28 +#: apps/wei/templates/wei/weimembership_form.html:67 +#, fuzzy +#| msgid "health issues" +msgid "health issues or specific diet" +msgstr "problèmes de santé" + #: apps/wei/templates/wei/attribute_bus_1A.html:31 msgid "suggested bus" msgstr "bus suggéré" @@ -3195,11 +3242,11 @@ msgstr "Prix du WEI (étudiant⋅es)" msgid "WEI list" msgstr "Liste des WEI" -#: apps/wei/templates/wei/base.html:81 apps/wei/views.py:528 +#: apps/wei/templates/wei/base.html:81 apps/wei/views.py:540 msgid "Register 1A" msgstr "Inscrire un⋅e 1A" -#: apps/wei/templates/wei/base.html:85 apps/wei/views.py:614 +#: apps/wei/templates/wei/base.html:85 apps/wei/views.py:626 msgid "Register 2A+" msgstr "Inscrire un⋅e 2A+" @@ -3228,8 +3275,8 @@ msgstr "Télécharger au format PDF" #: apps/wei/templates/wei/survey.html:11 #: apps/wei/templates/wei/survey_closed.html:11 -#: apps/wei/templates/wei/survey_end.html:11 apps/wei/views.py:1028 -#: apps/wei/views.py:1083 apps/wei/views.py:1130 +#: apps/wei/templates/wei/survey_end.html:11 apps/wei/views.py:1046 +#: apps/wei/views.py:1101 apps/wei/views.py:1148 msgid "Survey WEI" msgstr "Questionnaire WEI" @@ -3274,7 +3321,7 @@ msgstr "Inscriptions non validées" msgid "Attribute buses" msgstr "Répartition dans les bus" -#: apps/wei/templates/wei/weiclub_list.html:14 apps/wei/views.py:79 +#: apps/wei/templates/wei/weiclub_list.html:14 apps/wei/views.py:80 msgid "Create WEI" msgstr "Créer un WEI" @@ -3408,70 +3455,72 @@ msgid "There is no pre-registration found with this pattern." msgstr "Il n'y a pas de pré-inscription en attente avec cette entrée." #: apps/wei/templates/wei/weiregistration_list.html:27 -msgid "View validated membershipis..." +#, fuzzy +#| msgid "View validated membershipis..." +msgid "View validated memberships..." msgstr "Voir les adhésions validées..." -#: apps/wei/views.py:58 +#: apps/wei/views.py:59 msgid "Search WEI" msgstr "Chercher un WEI" -#: apps/wei/views.py:109 +#: apps/wei/views.py:110 msgid "WEI Detail" msgstr "Détails du WEI" -#: apps/wei/views.py:208 +#: apps/wei/views.py:210 msgid "View members of the WEI" msgstr "Voir les membres du WEI" -#: apps/wei/views.py:236 +#: apps/wei/views.py:243 msgid "Find WEI Membership" msgstr "Trouver une adhésion au WEI" -#: apps/wei/views.py:246 +#: apps/wei/views.py:253 msgid "View registrations to the WEI" msgstr "Voir les inscriptions au WEI" -#: apps/wei/views.py:270 +#: apps/wei/views.py:282 msgid "Find WEI Registration" msgstr "Trouver une inscription au WEI" -#: apps/wei/views.py:281 +#: apps/wei/views.py:293 msgid "Update the WEI" msgstr "Modifier le WEI" -#: apps/wei/views.py:302 +#: apps/wei/views.py:314 msgid "Create new bus" msgstr "Ajouter un nouveau bus" -#: apps/wei/views.py:340 +#: apps/wei/views.py:352 msgid "Update bus" msgstr "Modifier le bus" -#: apps/wei/views.py:372 +#: apps/wei/views.py:384 msgid "Manage bus" msgstr "Gérer le bus" -#: apps/wei/views.py:399 +#: apps/wei/views.py:411 msgid "Create new team" msgstr "Créer une nouvelle équipe" -#: apps/wei/views.py:439 +#: apps/wei/views.py:451 msgid "Update team" msgstr "Modifier l'équipe" -#: apps/wei/views.py:470 +#: apps/wei/views.py:482 msgid "Manage WEI team" msgstr "Gérer l'équipe WEI" -#: apps/wei/views.py:492 +#: apps/wei/views.py:504 msgid "Register first year student to the WEI" msgstr "Inscrire un⋅e 1A au WEI" -#: apps/wei/views.py:550 apps/wei/views.py:649 +#: apps/wei/views.py:562 apps/wei/views.py:661 msgid "This user is already registered to this WEI." msgstr "Cette personne est déjà inscrite au WEI." -#: apps/wei/views.py:555 +#: apps/wei/views.py:567 msgid "" "This user can't be in her/his first year since he/she has already " "participated to a WEI." @@ -3479,51 +3528,214 @@ msgstr "" "Cet⋅te utilisateur⋅rice ne peut pas être en première année puisqu'iel a déjà " "participé à un WEI." -#: apps/wei/views.py:578 +#: apps/wei/views.py:590 msgid "Register old student to the WEI" msgstr "Inscrire un⋅e 2A+ au WEI" -#: apps/wei/views.py:633 apps/wei/views.py:721 +#: apps/wei/views.py:645 apps/wei/views.py:733 msgid "You already opened an account in the Société générale." msgstr "Vous avez déjà ouvert un compte auprès de la société générale." -#: apps/wei/views.py:685 +#: apps/wei/views.py:697 msgid "Update WEI Registration" msgstr "Modifier l'inscription WEI" -#: apps/wei/views.py:795 +#: apps/wei/views.py:807 msgid "Delete WEI registration" msgstr "Supprimer l'inscription WEI" -#: apps/wei/views.py:806 +#: apps/wei/views.py:818 msgid "You don't have the right to delete this WEI registration." msgstr "Vous n'avez pas la permission de supprimer cette inscription au WEI." -#: apps/wei/views.py:824 +#: apps/wei/views.py:836 msgid "Validate WEI registration" msgstr "Valider l'inscription WEI" -#: apps/wei/views.py:1223 +#: apps/wei/views.py:1241 msgid "Attribute buses to first year members" msgstr "Répartir les 1A dans les bus" -#: apps/wei/views.py:1248 +#: apps/wei/views.py:1266 msgid "Attribute bus" msgstr "Attribuer un bus" -#: note_kfet/settings/base.py:173 +#: apps/wrapped/apps.py:10 +msgid "wrapped" +msgstr "wrapped" + +#: apps/wrapped/models.py:40 +msgid "generated" +msgstr "generé" + +#: apps/wrapped/models.py:45 +msgid "public" +msgstr "public" + +#: apps/wrapped/models.py:53 +msgid "bde" +msgstr "bde" + +#: apps/wrapped/models.py:65 +msgid "data json" +msgstr "donnée json" + +#: apps/wrapped/models.py:66 +msgid "data in the wrapped and generated by the script generate_wrapped" +msgstr "donnée dans le wrapped et générée par le script generate_wrapped" + +#: apps/wrapped/models.py:70 note_kfet/templates/base.html:114 +msgid "Wrapped" +msgstr "Wrapped" + +#: apps/wrapped/models.py:71 +msgid "Wrappeds" +msgstr "Wrappeds" + +#: apps/wrapped/tables.py:40 +msgid "view the wrapped" +msgstr "voir le wrapped" + +#: apps/wrapped/tables.py:55 +msgid "Click to make this wrapped private" +msgstr "Cliquer pour rendre ce wrapped privé" + +#: apps/wrapped/tables.py:56 +msgid "Click to make this wrapped public" +msgstr "Cliquer pour rendre ce wrapped public" + +#: apps/wrapped/tables.py:67 +msgid "Share" +msgstr "Partager" + +#: apps/wrapped/tables.py:73 +msgid "Click to copy the link in the press paper" +msgstr "Cliquer pour copier le lien" + +#: apps/wrapped/tables.py:81 +msgid "Copy link" +msgstr "Copier le lien" + +#: apps/wrapped/templates/wrapped/1/wrapped_base.html:16 +#: note_kfet/templates/base.html:14 +msgid "The ENS Paris-Saclay BDE note." +msgstr "La note du BDE de l'ENS Paris-Saclay." + +#: apps/wrapped/templates/wrapped/1/wrapped_base.html:58 +msgid "The NoteKfet this year it's also" +msgstr "La NoteKfet cette année, c'est aussi :" + +#: apps/wrapped/templates/wrapped/1/wrapped_base.html:60 +msgid " transactions" +msgstr " transactions" + +#: apps/wrapped/templates/wrapped/1/wrapped_base.html:61 +msgid " parties" +msgstr " soirées" + +#: apps/wrapped/templates/wrapped/1/wrapped_base.html:62 +msgid " Pot entries" +msgstr " entrées au Pot" + +#: apps/wrapped/templates/wrapped/1/wrapped_base.html:72 +msgid " old dickhead behind the bar" +msgstr " vieilleux con·ne·s derrière le bar" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_club.html:9 +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:9 +msgid "NoteKfet Wrapped" +msgstr "NoteKfet Wrapped" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_club.html:11 +msgid "Your best consumer:" +msgstr "Ton plus gros consommateur :" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_club.html:13 +msgid "Your worst creditor:" +msgstr "Ton pire créancier :" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_club.html:16 +msgid "party·ies organised" +msgstr "soirée·s organisée·s" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_club.html:17 +msgid "distinct members" +msgstr "Membres distinct·e·s" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:13 +msgid "You participate to the wei: " +msgstr "Tu as participé au wei : " + +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:13 +msgid "in the" +msgstr "dans le" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:18 +msgid "pots !" +msgstr "pots !" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:27 +msgid "Your first conso of the year: " +msgstr "Ta première conso de l'année : " + +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:28 +msgid "Your prefered consumtion category: " +msgstr "Ta catégorie de bouton préférée : " + +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:41 +msgid ": it's the number of time your reload your note" +msgstr ": c'est le nombre de fois où tu as rechargé·e ta note" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:44 +msgid "Your overall expenses: " +msgstr "Tes dépenses totales : " + +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:47 +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:60 +msgid "with" +msgstr "avec" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:57 +msgid "Your expenses to BDE: " +msgstr "Tes dépenses au BDE : " + +#: apps/wrapped/templates/wrapped/wrapped_list.html:26 +msgid "" +"Do not forget to ask permission to people who are in your wrapped before to " +"make them public" +msgstr "" +"N'oublies pas de demander la permission des personnes apparaissant dans un " +"wrapped avant de le rendre public" + +#: apps/wrapped/templates/wrapped/wrapped_list.html:33 +msgid "Link copied" +msgstr "Lien copié" + +#: apps/wrapped/templates/wrapped/wrapped_list.html:58 +msgid "Wrapped is private" +msgstr "Le wrapped est privé" + +#: apps/wrapped/templates/wrapped/wrapped_list.html:59 +msgid "Wrapped is public" +msgstr "Le wrapped est public" + +#: apps/wrapped/views.py:24 +msgid "List of wrapped" +msgstr "Liste des wrapped" + +#: note_kfet/settings/base.py:177 msgid "German" msgstr "Allemand" -#: note_kfet/settings/base.py:174 +#: note_kfet/settings/base.py:178 msgid "English" msgstr "Anglais" -#: note_kfet/settings/base.py:175 +#: note_kfet/settings/base.py:179 msgid "Spanish" msgstr "Espagnol" -#: note_kfet/settings/base.py:176 +#: note_kfet/settings/base.py:180 msgid "French" msgstr "Français" @@ -3575,19 +3787,15 @@ msgid "" "sent to webmasters with the detail of the error, and this will be fixed " "soon. You can now drink a beer." msgstr "" -"Désolé, une erreur est survenue lors de l'analyse de votre requête. Un e-mail " -"a été envoyé aux responsables de la plateforme avec les détails de cette " -"erreur, qui sera corrigée rapidement. Vous pouvez désormais aller boire une " -"bière, avec modération." +"Désolé, une erreur est survenue lors de l'analyse de votre requête. Un e-" +"mail a été envoyé aux responsables de la plateforme avec les détails de " +"cette erreur, qui sera corrigée rapidement. Vous pouvez désormais aller " +"boire une bière, avec modération." #: note_kfet/templates/autocomplete_model.html:15 msgid "Reset" msgstr "Réinitialiser" -#: note_kfet/templates/base.html:14 -msgid "The ENS Paris-Saclay BDE note." -msgstr "La note du BDE de l'ENS Paris-Saclay." - #: note_kfet/templates/base.html:72 msgid "Food" msgstr "Bouffe" @@ -3600,26 +3808,26 @@ msgstr "Utilisateur·rices" msgid "Clubs" msgstr "Clubs" -#: note_kfet/templates/base.html:119 +#: note_kfet/templates/base.html:125 msgid "Admin" msgstr "Admin" -#: note_kfet/templates/base.html:133 +#: note_kfet/templates/base.html:139 msgid "My account" msgstr "Mon compte" -#: note_kfet/templates/base.html:136 +#: note_kfet/templates/base.html:142 msgid "Log out" msgstr "Se déconnecter" -#: note_kfet/templates/base.html:144 +#: note_kfet/templates/base.html:150 #: note_kfet/templates/registration/signup.html:6 #: note_kfet/templates/registration/signup.html:11 #: note_kfet/templates/registration/signup.html:28 msgid "Sign up" msgstr "Inscription" -#: note_kfet/templates/base.html:151 +#: note_kfet/templates/base.html:157 #: note_kfet/templates/registration/login.html:6 #: note_kfet/templates/registration/login.html:15 #: note_kfet/templates/registration/login.html:38 @@ -3627,7 +3835,7 @@ msgstr "Inscription" msgid "Log in" msgstr "Se connecter" -#: note_kfet/templates/base.html:165 +#: note_kfet/templates/base.html:171 msgid "" "You are not a BDE member anymore. Please renew your membership if you want " "to use the note." @@ -3635,7 +3843,7 @@ msgstr "" "Vous n'êtes plus adhérent·e BDE. Merci de réadhérer si vous voulez profiter " "de la note." -#: note_kfet/templates/base.html:171 +#: note_kfet/templates/base.html:177 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -3643,7 +3851,7 @@ msgstr "" "Votre adresse e-mail n'est pas validée. Merci de vérifier votre boîte mail " "et de cliquer sur le lien de validation." -#: note_kfet/templates/base.html:177 +#: note_kfet/templates/base.html:183 msgid "" "You declared that you opened a bank account in the Société générale. The " "bank did not validate the creation of the account to the BDE, so the " @@ -3657,19 +3865,19 @@ msgstr "" "vérification peut durer quelques jours. Merci de vous assurer de bien aller " "au bout de vos démarches." -#: note_kfet/templates/base.html:200 +#: note_kfet/templates/base.html:206 msgid "Contact us" msgstr "Nous contacter" -#: note_kfet/templates/base.html:202 +#: note_kfet/templates/base.html:208 msgid "Technical Support" msgstr "Support technique" -#: note_kfet/templates/base.html:204 +#: note_kfet/templates/base.html:210 msgid "Charte Info (FR)" msgstr "Charte Info (FR)" -#: note_kfet/templates/base.html:206 +#: note_kfet/templates/base.html:212 msgid "FAQ (FR)" msgstr "FAQ (FR)" diff --git a/locale/fr/LC_MESSAGES/djangojs.po b/locale/fr/LC_MESSAGES/djangojs.po index 90f85fc4..59989ae6 100644 --- a/locale/fr/LC_MESSAGES/djangojs.po +++ b/locale/fr/LC_MESSAGES/djangojs.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-10-07 09:07+0200\n" +"POT-Creation-Date: 2025-02-25 13:27+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,11 +17,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: apps/member/static/member/js/alias.js:17 +#: apps/activity/static/activity/js/opener.js:31 msgid "Opener successfully added" msgstr "Ouvreureuse ajouté avec succès" -#: apps/member/static/member/js/alias.js:17 +#: apps/activity/static/activity/js/opener.js:47 msgid "Opener successfully deleted" msgstr "Ouvreureuse supprimé avec succès" From ac4574200d4dbeb80f819175894b133616cd7483 Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 4 Mar 2025 18:45:22 +0100 Subject: [PATCH 43/84] Modify font --- .../wrapped/fonts/1/JEMROKtrial-Regular.ttf | Bin 137336 -> 137336 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/wrapped/static/wrapped/fonts/1/JEMROKtrial-Regular.ttf b/apps/wrapped/static/wrapped/fonts/1/JEMROKtrial-Regular.ttf index 201f7f5e7443386db4411d38876ddb3a178b3c9a..ef731e1a02f64884c0dc0ac0db0809950164e519 100644 GIT binary patch delta 876 zcmWksO=w(I6u#%&zjOcId-L9#nKzTP+L=)N$ohddUqLfOhKcxhrb)ki(#6nQ9 zpcG`Jb)g7C9GC7CDcD6Zpdz@@y&x2XE);a3hzl1AT_xUL?!AZeclpkDzXx~e!CfjD zFvjwD3D!GvX6xC{<6hwl80KWaFEk7=zW=GTDg% zvnvPDzY_f;JG%Vm^Y9}4 zFuMb{dw7G5;62vEQ}L{`8ZWXne4ee~dA5pOwzz*CKV5_E{p#2`p+|=Y_yWE*yLRI2 zbhA)-*R1r{J{fcts7jO4KwJi!zU7vhk6w6lWmtsN6bT4CXxO<0n>FFPC#h9Yw~Ixk zo6YiMxBqcn)pWEjUAF<8L)}Q`veBTvH7q_%+pmAn^8LPR`**hH%c2$MWWB=!xQ0*d zkH&8cy~+T4cwzR{<5OtPCa0FCj%#IdrDe2Ewo_{qp0pV+E1e6SOD~l{w^UYAq2|u6m1Qm~k zg(~ZMD-9P{hr_M|fnNqSc7KYpf!T9FZa{n^}_-&imkV zLAH3v;UeSk4_=zxT;HCKC*#TS$#4Z=BP`F=No-ZxB0vy<()%CfK?o5a>f@pU*xiXd<1{M?~{A0TjO^_ z;!D|W=058bQzEw}l@LSHRJX8h^)KA$&IWB|Fu>q9r^+eiQrKD+X(sXG+RLYjiiox>hZ`xn2gJ#($Dt6Qiwi8Z8GnyQP(t38frp!oUe5(O9vBN@5e)C`B2is1%y0K`5+?QE+9-sG&VR zrbxe@e187U_`Tto;pyRE0r-Yr==6g`C6u86KJl2o__pvo;c*vJTKNzXrXpr7GK~F% zTc$Z$Jw4ODI?Qrmb)=q9!9|r6s2%2@tcbpN0X}_8350U8w~bgz6kO7gR#>4oF4Rq9 ze8=O7WvR2e(t~=&1u-oG*Fx|Fc+{P0 Date: Wed, 5 Mar 2025 13:28:55 +0100 Subject: [PATCH 44/84] change icon --- note_kfet/templates/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index 4f87228a..1c601c50 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -111,7 +111,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% if "wrapped.wrapped"|model_list_length >= 1 %} {% endif %} {% if request.user.is_authenticated %} From dc14ba010191d6162d2aa399a82d1ddf989002eb Mon Sep 17 00:00:00 2001 From: quark Date: Fri, 7 Mar 2025 18:42:41 +0100 Subject: [PATCH 45/84] Suppression article TVA --- apps/treasury/templates/treasury/invoice_sample.tex | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/treasury/templates/treasury/invoice_sample.tex b/apps/treasury/templates/treasury/invoice_sample.tex index 3ce36505..16782ef9 100644 --- a/apps/treasury/templates/treasury/invoice_sample.tex +++ b/apps/treasury/templates/treasury/invoice_sample.tex @@ -183,10 +183,6 @@ Facture n°\FactureNum } -\begin{center} -TVA non applicable, article 293 B du CGI. -\end{center} - \end{document} {% endlanguage %} From 4563b2b640d456e541524c29886b5b050e0222a8 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Sat, 8 Mar 2025 16:04:25 +0100 Subject: [PATCH 46/84] Added configusation for OpenID support, along with installation information --- README.md | 14 +++++++++++++- note_kfet/settings/base.py | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7297a1b6..97a0760d 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,13 @@ Bien que cela permette de créer une instance sur toutes les distributions, (env)$ ./manage.py createsuperuser # Création d'un⋅e utilisateur⋅rice initial ``` -6. Enjoy : +6. (Optionnel) **Création d'une clé privée OpenID Connect** + +Pour activer le support d'OpenID Connect, il faut générer une clé privée, par +exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner son +emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`) + +7. Enjoy : ```bash (env)$ ./manage.py runserver 0.0.0.0:8000 @@ -228,6 +234,12 @@ Sinon vous pouvez suivre les étapes décrites ci-dessous. (env)$ ./manage.py check # pas de bêtise qui traine (env)$ ./manage.py migrate +7. **Création d'une clé privée OpenID Connect** + +Pour activer le support d'OpenID Connect, il faut générer une clé privée, par +exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner son +emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`) + 7. *Enjoy \o/* ### Installation avec Docker diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index 113cf626..d982ac3a 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -268,6 +268,10 @@ OAUTH2_PROVIDER = { 'OAUTH2_VALIDATOR_CLASS': "permission.scopes.PermissionOAuth2Validator", 'REFRESH_TOKEN_EXPIRE_SECONDS': timedelta(days=14), 'PKCE_REQUIRED': False, # PKCE (fix a breaking change of django-oauth-toolkit 2.0.0) + 'OIDC_ENABLED': True, + 'OIDC_RSA_PRIVATE_KEY': + os.getenv('OIDC_RSA_PRIVATE_KEY', '/var/secrets/oidc.key'), + 'SCOPES': { 'openid': "OpenID Connect scope" }, } # Take control on how widget templates are sourced From 6c63c6417ca8f33afeeff718f3b7db2056dcda22 Mon Sep 17 00:00:00 2001 From: Nicolas Margulies Date: Sat, 8 Mar 2025 16:08:40 +0100 Subject: [PATCH 47/84] Typesetting --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 97a0760d..02d589f2 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Bien que cela permette de créer une instance sur toutes les distributions, Pour activer le support d'OpenID Connect, il faut générer une clé privée, par exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner son -emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`) +emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`). 7. Enjoy : @@ -238,7 +238,7 @@ Sinon vous pouvez suivre les étapes décrites ci-dessous. Pour activer le support d'OpenID Connect, il faut générer une clé privée, par exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner son -emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`) +emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`). 7. *Enjoy \o/* From e5f9fe2cf5df153268546b6ece7180989edbbd07 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 20:00:20 +0100 Subject: [PATCH 48/84] Update file initial.json --- apps/permission/fixtures/initial.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index bf9171fc..1ce91ff1 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -3938,7 +3938,7 @@ ], "query": "{\"activity__organizer\": [\"club\"]}", "type": "delete", - "mask": 1, + "mask": 2, "field": "", "permanent": false, "description": "Supprimer des personnes invitées aux événements organisés par son club" @@ -4088,6 +4088,22 @@ "description": "Modifier la visibilité des wrapped de son club" } }, + { + "model": "permission.permission", + "pk": 260, + "fields": { + "model": [ + "member", + "club" + ], + "query": "{\"parent_club\": [\"club\"]}", + "type": "view", + "mask": 2, + "field": "", + "permanent": false, + "description": "Voir les informations d'un club enfant" + } + }, { "model": "permission.role", "pk": 1, From 51315a0555dc72ced980a6c9cbead15e84c29866 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 20:25:16 +0100 Subject: [PATCH 49/84] Update file initial.json --- apps/permission/fixtures/initial.json | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 1ce91ff1..f55abce0 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4104,6 +4104,70 @@ "description": "Voir les informations d'un club enfant" } }, + { + "model": "permission.permission", + "pk": 261, + "fields": { + "model": [ + "member", + "club" + ], + "query": "{\"parent_club\": [\"club\"]}", + "type": "change", + "mask": 2, + "field": "", + "permanent": false, + "description": "Modifier un club enfant" + } + }, + { + "model": "permission.permission", + "pk": 262, + "fields": { + "model": [ + "member", + "membership" + ], + "query": "{\"club__parent_club\": [\"club\"]}", + "type": "add", + "mask": 2, + "field": "", + "permanent": false, + "description": "Ajouter un⋅e membre à un club enfant" + } + }, + { + "model": "permission.permission", + "pk": 263, + "fields": { + "model": [ + "member", + "membership" + ], + "query": "{\"club__parent_club\": [\"club\"]}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir les adhérent⋅es du club enfant" + } + }, + { + "model": "permission.permission", + "pk": 264, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"OR\", {\"source.club_parent\": [\"club\", \"note\"]}, {\"destination.club_parent\": [\"club\", \"note\"]}]", + "type": "view", + "mask": 2, + "field": "", + "permanent": false, + "description": "Voir les transactions d'un club" + } + }, { "model": "permission.role", "pk": 1, From c356534309df01545ecc73466e5107719be995ac Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 20:25:33 +0100 Subject: [PATCH 50/84] Update file initial.json --- apps/permission/fixtures/initial.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index f55abce0..68679c40 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4165,7 +4165,7 @@ "mask": 2, "field": "", "permanent": false, - "description": "Voir les transactions d'un club" + "description": "Voir les transactions d'un club enfant" } }, { From 55a0fbb6cbcd3aef0cce500856e3c8b2b4b3f75b Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 21:15:56 +0100 Subject: [PATCH 51/84] Update file initial.json --- apps/permission/fixtures/initial.json | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 68679c40..fd8b227c 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4152,22 +4152,6 @@ "description": "Voir les adhérent⋅es du club enfant" } }, - { - "model": "permission.permission", - "pk": 264, - "fields": { - "model": [ - "note", - "transaction" - ], - "query": "[\"OR\", {\"source.club_parent\": [\"club\", \"note\"]}, {\"destination.club_parent\": [\"club\", \"note\"]}]", - "type": "view", - "mask": 2, - "field": "", - "permanent": false, - "description": "Voir les transactions d'un club enfant" - } - }, { "model": "permission.role", "pk": 1, From b27bdb090d0252eae0c4be255440ffb42b19f78e Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 21:30:16 +0100 Subject: [PATCH 52/84] Update file initial.json --- apps/permission/fixtures/initial.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index fd8b227c..ee480975 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4152,6 +4152,22 @@ "description": "Voir les adhérent⋅es du club enfant" } }, + { + "model": "permission.permission", + "pk": 264, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"OR\", {\"source__club__parent_club\": [\"club\", \"note\"]}, {\"destination__club__parent_club\": [\"club\", \"note\"]}]", + "type": "view", + "mask": 2, + "field": "", + "permanent": false, + "description": "Voir les transactions d'un club" + } + }, { "model": "permission.role", "pk": 1, From 9752a030d97c384d6ecc85bfddc8c64bc6ebafe1 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 21:30:25 +0100 Subject: [PATCH 53/84] Update file initial.json --- apps/permission/fixtures/initial.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index ee480975..858a0980 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4165,7 +4165,7 @@ "mask": 2, "field": "", "permanent": false, - "description": "Voir les transactions d'un club" + "description": "Voir les transactions d'un club enfant" } }, { From 018f6e3f13bb21ab0ef34999d075a5830a458d8d Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 21:37:53 +0100 Subject: [PATCH 54/84] Update file initial.json --- apps/permission/fixtures/initial.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 858a0980..51f59b83 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4160,7 +4160,7 @@ "note", "transaction" ], - "query": "[\"OR\", {\"source__club__parent_club\": [\"club\", \"note\"]}, {\"destination__club__parent_club\": [\"club\", \"note\"]}]", + "query": "[\"OR\", {\"source__note__club__parent_club\": [\"club\", \"note\"]}, {\"destination__note__club__parent_club\": [\"club\", \"note\"]}]", "type": "view", "mask": 2, "field": "", From 1f53ad44072ca2f6017aba504f9f0bfcea9f97a9 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 21:47:21 +0100 Subject: [PATCH 55/84] Update file initial.json --- apps/permission/fixtures/initial.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 51f59b83..17a5748b 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4160,7 +4160,7 @@ "note", "transaction" ], - "query": "[\"OR\", {\"source__note__club__parent_club\": [\"club\", \"note\"]}, {\"destination__note__club__parent_club\": [\"club\", \"note\"]}]", + "query": "[\"OR\", {\"source\": [\"IN\", {\"noteclub__club__parent_club\": [\"club\", \"note\"]}]}, {\"destination\": [\"IN\", {\"noteclub__club__parent_club\": [\"club\", \"note\"]}]}]", "type": "view", "mask": 2, "field": "", From 0196db7fff2ed3e75e4decd3c7598fe146117548 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 21:54:28 +0100 Subject: [PATCH 56/84] Update file initial.json --- apps/permission/fixtures/initial.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 17a5748b..98971447 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4160,7 +4160,7 @@ "note", "transaction" ], - "query": "[\"OR\", {\"source\": [\"IN\", {\"noteclub__club__parent_club\": [\"club\", \"note\"]}]}, {\"destination\": [\"IN\", {\"noteclub__club__parent_club\": [\"club\", \"note\"]}]}]", + "query": "[\"OR\", {\"source__noteclub__club__parent_club\": [\"club\", \"note\"]}, {\"destination__noteclub__club__parent_club\": [\"club\", \"note\"]}]", "type": "view", "mask": 2, "field": "", From 1bdad76fe9b6b042cff4e6d6c45fe51252bbb710 Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 22:00:46 +0100 Subject: [PATCH 57/84] Update file initial.json --- apps/permission/fixtures/initial.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 98971447..bd00ca6b 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4160,7 +4160,7 @@ "note", "transaction" ], - "query": "[\"OR\", {\"source__noteclub__club__parent_club\": [\"club\", \"note\"]}, {\"destination__noteclub__club__parent_club\": [\"club\", \"note\"]}]", + "query": "[\"OR\", {\"source__noteclub__club__parent_club\": [\"club\"]}, {\"destination__noteclub__club__parent_club\": [\"club\"]}]", "type": "view", "mask": 2, "field": "", From 767e98c2a37f422404e032bd590a906a1e6f7a5c Mon Sep 17 00:00:00 2001 From: thomasl Date: Sat, 8 Mar 2025 22:05:22 +0100 Subject: [PATCH 58/84] Update file initial.json --- apps/permission/fixtures/initial.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index bd00ca6b..27afc9aa 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4168,6 +4168,22 @@ "description": "Voir les transactions d'un club enfant" } }, + { + "model": "permission.permission", + "pk": 265, + "fields": { + "model": [ + "note", + "note" + ], + "query": "{\"noteclub__club__parent_club\": [\"club\"]}", + "type": "view", + "mask": 2, + "field": "", + "permanent": false, + "description": "Voir la note d'un club enfant" + } + }, { "model": "permission.role", "pk": 1, From 5fb12a1388bf2a845400754070436d978eb70bbe Mon Sep 17 00:00:00 2001 From: quark Date: Sun, 9 Mar 2025 13:11:46 +0100 Subject: [PATCH 59/84] improve permissions for GC anti-VSS --- apps/permission/fixtures/initial.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 27afc9aa..fdc6cc12 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4742,10 +4742,11 @@ "for_club": 1, "name": "GC anti-VSS", "permissions": [ - 150, + 42, + 135, + 150, 163, - 164, - 182 + 164 ] } }, From 423454ba5d07fda58b0a36e071f695c4ed026131 Mon Sep 17 00:00:00 2001 From: quark Date: Sun, 9 Mar 2025 14:37:35 +0100 Subject: [PATCH 60/84] nerf PC Kfet perms --- apps/note/static/note/js/consos.js | 2 +- apps/permission/fixtures/initial.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/note/static/note/js/consos.js b/apps/note/static/note/js/consos.js index 4f096207..d812e451 100644 --- a/apps/note/static/note/js/consos.js +++ b/apps/note/static/note/js/consos.js @@ -245,7 +245,7 @@ function consume (source, source_alias, dest, quantity, amount, reason, type, ca invalidity_reason: 'Solde insuffisant', polymorphic_ctype: type, resourcetype: 'RecurrentTransaction', - source: source, + source: source.id, source_alias: source_alias, destination: dest, template: template diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index fdc6cc12..0f785c2a 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -2591,7 +2591,7 @@ "note", "transaction" ], - "query": "[\"OR\", {\"source__balance__gte\": 0}, [\"AND\", [\"NOT\", {\"recurrenttransaction__template__category__name\": \"Alcool\"}], {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}], {\"valid\": false}]", + "query": "[\"OR\", [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}, {\"valid\": false}], [\"NOT\", {\"recurrenttransaction__template__category__name\": \"Alcool\"}]], [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 0]}}, {\"valid\": false}], {\"recurrenttransaction__template__category__name\": \"Alcool\"}]]", "type": "add", "mask": 2, "field": "", @@ -2607,7 +2607,7 @@ "note", "transaction" ], - "query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}]", + "query": "[\"OR\", [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], [\"NOT\", {\"recurrenttransaction__template__category__name\":\"Alcool\"}]], [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 0]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], {\"recurrenttransaction__template__category__name\":\"Alcool\"}]]", "type": "change", "mask": 2, "field": "valid", @@ -2623,7 +2623,7 @@ "note", "transaction" ], - "query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}]", + "query": "[\"OR\", [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], [\"NOT\", {\"recurrenttransaction__template__category__name\":\"Alcool\"}]], [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 0]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], {\"recurrenttransaction__template__category__name\":\"Alcool\"}]]", "type": "change", "mask": 2, "field": "invalidity_reason", From a0ebf8658dcaa4ead844840b76858a0136ba6290 Mon Sep 17 00:00:00 2001 From: quark Date: Sun, 9 Mar 2025 17:01:57 +0100 Subject: [PATCH 61/84] nerf invalidate perm --- apps/permission/backends.py | 3 ++- apps/permission/fixtures/initial.json | 6 ++++-- apps/permission/tests/test_permission_queries.py | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/permission/backends.py b/apps/permission/backends.py index f64fd461..f246f0eb 100644 --- a/apps/permission/backends.py +++ b/apps/permission/backends.py @@ -1,7 +1,7 @@ # Copyright (C) 2018-2024 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from datetime import date +from datetime import date, timedelta from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User @@ -106,6 +106,7 @@ class PermissionBackend(ModelBackend): Q=Q, now=timezone.now(), today=date.today(), + week=timedelta(days=7), ) yield permission diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 0f785c2a..15f96657 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -2607,7 +2607,8 @@ "note", "transaction" ], - "query": "[\"OR\", [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], [\"NOT\", {\"recurrenttransaction__template__category__name\":\"Alcool\"}]], [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 0]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], {\"recurrenttransaction__template__category__name\":\"Alcool\"}]]", + "query": "[\"AND\", [\"OR\", [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], [\"NOT\", {\"recurrenttransaction__template__category__name\": \"Alcool\"}]], [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 0]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], {\"recurrenttransaction__template__category__name\": \"Alcool\"}]], {\"created_at__gte\": {\"F\": [\"SUB\", [\"now\"], [\"week\"]]}}]" +, "type": "change", "mask": 2, "field": "valid", @@ -2623,7 +2624,8 @@ "note", "transaction" ], - "query": "[\"OR\", [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], [\"NOT\", {\"recurrenttransaction__template__category__name\":\"Alcool\"}]], [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 0]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], {\"recurrenttransaction__template__category__name\":\"Alcool\"}]]", + "query": "[\"AND\", [\"OR\", [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], [\"NOT\", {\"recurrenttransaction__template__category__name\": \"Alcool\"}]], [\"AND\", [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 0]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}, \"valid\": true}], {\"recurrenttransaction__template__category__name\": \"Alcool\"}]], {\"created_at__gte\": {\"F\": [\"SUB\", [\"now\"], [\"week\"]]}}]" +, "type": "change", "mask": 2, "field": "invalidity_reason", diff --git a/apps/permission/tests/test_permission_queries.py b/apps/permission/tests/test_permission_queries.py index 95e303e4..11634993 100644 --- a/apps/permission/tests/test_permission_queries.py +++ b/apps/permission/tests/test_permission_queries.py @@ -1,7 +1,7 @@ # Copyright (C) 2018-2024 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from datetime import date +from datetime import date, timedelta from json.decoder import JSONDecodeError from django.contrib.auth.models import User @@ -73,6 +73,7 @@ class PermissionQueryTestCase(TestCase): Q=Q, now=timezone.now(), today=date.today(), + week=timedelta(days=7), ) instanced.update_query() query = instanced.query From ae7d5d54895cda71c49fc0caccff4068c565af48 Mon Sep 17 00:00:00 2001 From: quark Date: Sun, 9 Mar 2025 18:14:58 +0100 Subject: [PATCH 62/84] update copyright --- apps/activity/__init__.py | 2 +- apps/activity/admin.py | 2 +- apps/activity/api/serializers.py | 2 +- apps/activity/api/urls.py | 2 +- apps/activity/api/views.py | 2 +- apps/activity/apps.py | 2 +- apps/activity/forms.py | 2 +- apps/activity/models.py | 2 +- apps/activity/tables.py | 2 +- apps/activity/tests/test_activities.py | 2 +- apps/activity/urls.py | 2 +- apps/activity/views.py | 2 +- apps/api/__init__.py | 2 +- apps/api/apps.py | 2 +- apps/api/serializers.py | 2 +- apps/api/tests.py | 2 +- apps/api/urls.py | 2 +- apps/api/views.py | 2 +- apps/api/viewsets.py | 2 +- apps/food/admin.py | 2 +- apps/food/api/serializers.py | 2 +- apps/food/api/urls.py | 2 +- apps/food/api/views.py | 2 +- apps/food/apps.py | 2 +- apps/food/forms.py | 2 +- apps/food/models.py | 2 +- apps/food/tables.py | 2 +- apps/food/urls.py | 2 +- apps/food/views.py | 2 +- apps/logs/__init__.py | 2 +- apps/logs/api/serializers.py | 2 +- apps/logs/api/urls.py | 2 +- apps/logs/api/views.py | 2 +- apps/logs/apps.py | 2 +- apps/logs/models.py | 2 +- apps/logs/signals.py | 2 +- apps/member/__init__.py | 2 +- apps/member/admin.py | 2 +- apps/member/api/serializers.py | 2 +- apps/member/api/urls.py | 2 +- apps/member/api/views.py | 2 +- apps/member/apps.py | 2 +- apps/member/auth.py | 2 +- apps/member/forms.py | 2 +- apps/member/hashers.py | 2 +- apps/member/models.py | 2 +- apps/member/signals.py | 2 +- apps/member/tables.py | 2 +- apps/member/templatetags/memberinfo.py | 2 +- apps/member/urls.py | 2 +- apps/member/views.py | 2 +- apps/note/__init__.py | 2 +- apps/note/admin.py | 2 +- apps/note/api/serializers.py | 2 +- apps/note/api/urls.py | 2 +- apps/note/api/views.py | 2 +- apps/note/apps.py | 2 +- apps/note/forms.py | 2 +- apps/note/models/__init__.py | 2 +- apps/note/models/notes.py | 2 +- apps/note/models/transactions.py | 2 +- apps/note/signals.py | 2 +- apps/note/static/note/js/consos.js | 2 +- apps/note/tables.py | 2 +- apps/note/templatetags/getenv.py | 2 +- apps/note/templatetags/pretty_money.py | 2 +- apps/note/tests/test_transactions.py | 2 +- apps/note/urls.py | 2 +- apps/note/views.py | 2 +- apps/permission/__init__.py | 2 +- apps/permission/admin.py | 2 +- apps/permission/api/serializers.py | 2 +- apps/permission/api/urls.py | 2 +- apps/permission/api/views.py | 2 +- apps/permission/apps.py | 2 +- apps/permission/backends.py | 2 +- apps/permission/decorators.py | 2 +- apps/permission/models.py | 2 +- apps/permission/permissions.py | 2 +- apps/permission/scopes.py | 2 +- apps/permission/signals.py | 2 +- apps/permission/tables.py | 2 +- apps/permission/templatetags/perms.py | 2 +- apps/permission/tests/test_oauth2.py | 2 +- apps/permission/tests/test_permission_denied.py | 2 +- apps/permission/tests/test_permission_queries.py | 2 +- apps/permission/tests/test_rights_page.py | 2 +- apps/permission/urls.py | 2 +- apps/permission/views.py | 2 +- apps/registration/__init__.py | 2 +- apps/registration/apps.py | 2 +- apps/registration/forms.py | 2 +- apps/registration/tables.py | 2 +- apps/registration/tests/test_registration.py | 2 +- apps/registration/tokens.py | 2 +- apps/registration/urls.py | 2 +- apps/registration/views.py | 2 +- apps/treasury/__init__.py | 2 +- apps/treasury/admin.py | 2 +- apps/treasury/api/serializers.py | 2 +- apps/treasury/api/urls.py | 2 +- apps/treasury/api/views.py | 2 +- apps/treasury/apps.py | 2 +- apps/treasury/forms.py | 2 +- apps/treasury/models.py | 2 +- apps/treasury/signals.py | 2 +- apps/treasury/tables.py | 2 +- apps/treasury/templatetags/escape_tex.py | 2 +- apps/treasury/tests/test_treasury.py | 2 +- apps/treasury/urls.py | 2 +- apps/treasury/views.py | 2 +- apps/wei/__init__.py | 2 +- apps/wei/admin.py | 2 +- apps/wei/api/serializers.py | 2 +- apps/wei/api/urls.py | 2 +- apps/wei/api/views.py | 2 +- apps/wei/apps.py | 2 +- apps/wei/forms/__init__.py | 2 +- apps/wei/forms/registration.py | 2 +- apps/wei/forms/surveys/__init__.py | 2 +- apps/wei/forms/surveys/base.py | 2 +- apps/wei/forms/surveys/wei2021.py | 2 +- apps/wei/forms/surveys/wei2022.py | 2 +- apps/wei/forms/surveys/wei2023.py | 2 +- apps/wei/forms/surveys/wei2024.py | 2 +- apps/wei/management/commands/export_wei_registrations.py | 2 +- apps/wei/management/commands/import_scores.py | 2 +- apps/wei/management/commands/wei_algorithm.py | 2 +- apps/wei/models.py | 2 +- apps/wei/tables.py | 2 +- apps/wei/tests/test_wei_algorithm_2021.py | 2 +- apps/wei/tests/test_wei_algorithm_2022.py | 2 +- apps/wei/tests/test_wei_algorithm_2023.py | 2 +- apps/wei/tests/test_wei_algorithm_2024.py | 2 +- apps/wei/tests/test_wei_registration.py | 2 +- apps/wei/urls.py | 2 +- apps/wei/views.py | 2 +- apps/wrapped/__init__.py | 2 +- apps/wrapped/admin.py | 2 +- apps/wrapped/api/serializers.py | 2 +- apps/wrapped/api/urls.py | 2 +- apps/wrapped/api/views.py | 2 +- apps/wrapped/apps.py | 2 +- apps/wrapped/models.py | 2 +- apps/wrapped/tables.py | 2 +- apps/wrapped/templates/wrapped/1/wrapped_base.html | 2 +- apps/wrapped/templates/wrapped/1/wrapped_view_club.html | 2 +- apps/wrapped/templates/wrapped/1/wrapped_view_user.html | 2 +- apps/wrapped/urls.py | 2 +- apps/wrapped/views.py | 2 +- entrypoint.sh | 2 +- note_kfet/admin.py | 2 +- note_kfet/inputs.py | 2 +- note_kfet/middlewares.py | 2 +- note_kfet/settings/__init__.py | 2 +- note_kfet/settings/base.py | 2 +- note_kfet/settings/development.py | 2 +- note_kfet/settings/secrets_example.py | 2 +- note_kfet/static/js/base.js | 2 +- note_kfet/urls.py | 2 +- note_kfet/views.py | 2 +- 161 files changed, 161 insertions(+), 161 deletions(-) diff --git a/apps/activity/__init__.py b/apps/activity/__init__.py index 713ba510..ac885e3a 100644 --- a/apps/activity/__init__.py +++ b/apps/activity/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'activity.apps.ActivityConfig' diff --git a/apps/activity/admin.py b/apps/activity/admin.py index 3355d1aa..6f85001a 100644 --- a/apps/activity/admin.py +++ b/apps/activity/admin.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib import admin diff --git a/apps/activity/api/serializers.py b/apps/activity/api/serializers.py index 31c23cb8..c2e7ada8 100644 --- a/apps/activity/api/serializers.py +++ b/apps/activity/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.utils.translation import gettext_lazy as _ diff --git a/apps/activity/api/urls.py b/apps/activity/api/urls.py index 4ff977fe..5da3e208 100644 --- a/apps/activity/api/urls.py +++ b/apps/activity/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .views import ActivityTypeViewSet, ActivityViewSet, EntryViewSet, GuestViewSet, OpenerViewSet diff --git a/apps/activity/api/views.py b/apps/activity/api/views.py index afa41ea7..86205e98 100644 --- a/apps/activity/api/views.py +++ b/apps/activity/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from api.filters import RegexSafeSearchFilter diff --git a/apps/activity/apps.py b/apps/activity/apps.py index f3ee3936..394c715e 100644 --- a/apps/activity/apps.py +++ b/apps/activity/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/activity/forms.py b/apps/activity/forms.py index 1070f19d..e39eeda3 100644 --- a/apps/activity/forms.py +++ b/apps/activity/forms.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import timedelta diff --git a/apps/activity/models.py b/apps/activity/models.py index c9f5842e..698b0c9f 100644 --- a/apps/activity/models.py +++ b/apps/activity/models.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import os diff --git a/apps/activity/tables.py b/apps/activity/tables.py index 0e7ab270..00238ae7 100644 --- a/apps/activity/tables.py +++ b/apps/activity/tables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.utils import timezone diff --git a/apps/activity/tests/test_activities.py b/apps/activity/tests/test_activities.py index 379f5d1b..bd85803a 100644 --- a/apps/activity/tests/test_activities.py +++ b/apps/activity/tests/test_activities.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import timedelta diff --git a/apps/activity/urls.py b/apps/activity/urls.py index 3578c340..962be72a 100644 --- a/apps/activity/urls.py +++ b/apps/activity/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.urls import path diff --git a/apps/activity/views.py b/apps/activity/views.py index 17446947..fd640e13 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from hashlib import md5 diff --git a/apps/api/__init__.py b/apps/api/__init__.py index 2e6addb0..70529f08 100644 --- a/apps/api/__init__.py +++ b/apps/api/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'api.apps.APIConfig' diff --git a/apps/api/apps.py b/apps/api/apps.py index 46fa6979..c778bac6 100644 --- a/apps/api/apps.py +++ b/apps/api/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/api/serializers.py b/apps/api/serializers.py index 74a95775..7891bfc6 100644 --- a/apps/api/serializers.py +++ b/apps/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/apps/api/tests.py b/apps/api/tests.py index 6be818bf..912044df 100644 --- a/apps/api/tests.py +++ b/apps/api/tests.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import json diff --git a/apps/api/urls.py b/apps/api/urls.py index ad2daf5f..c9e0dfa4 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.conf import settings diff --git a/apps/api/views.py b/apps/api/views.py index 43ef3f78..a6667042 100644 --- a/apps/api/views.py +++ b/apps/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib.auth.models import User diff --git a/apps/api/viewsets.py b/apps/api/viewsets.py index b1d42c50..5c17eab4 100644 --- a/apps/api/viewsets.py +++ b/apps/api/viewsets.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import re diff --git a/apps/food/admin.py b/apps/food/admin.py index fa32755a..89f042e1 100644 --- a/apps/food/admin.py +++ b/apps/food/admin.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib import admin diff --git a/apps/food/api/serializers.py b/apps/food/api/serializers.py index 9c42013f..acac2ba9 100644 --- a/apps/food/api/serializers.py +++ b/apps/food/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from rest_framework import serializers diff --git a/apps/food/api/urls.py b/apps/food/api/urls.py index acfb635d..23c67bdd 100644 --- a/apps/food/api/urls.py +++ b/apps/food/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .views import AllergenViewSet, BasicFoodViewSet, QRCodeViewSet, TransformedFoodViewSet diff --git a/apps/food/api/views.py b/apps/food/api/views.py index 824ff809..af616074 100644 --- a/apps/food/api/views.py +++ b/apps/food/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from api.viewsets import ReadProtectedModelViewSet diff --git a/apps/food/apps.py b/apps/food/apps.py index 62ede85f..094d9e27 100644 --- a/apps/food/apps.py +++ b/apps/food/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/apps/food/forms.py b/apps/food/forms.py index 4f567e59..af468c7f 100644 --- a/apps/food/forms.py +++ b/apps/food/forms.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from random import shuffle diff --git a/apps/food/models.py b/apps/food/models.py index 97e00ff9..199dcdd7 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import timedelta diff --git a/apps/food/tables.py b/apps/food/tables.py index 4a180c76..4ab15879 100644 --- a/apps/food/tables.py +++ b/apps/food/tables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import django_tables2 as tables diff --git a/apps/food/urls.py b/apps/food/urls.py index 09bb8ebe..59063cfe 100644 --- a/apps/food/urls.py +++ b/apps/food/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.urls import path diff --git a/apps/food/views.py b/apps/food/views.py index 88964a5f..8c63530c 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.db import transaction diff --git a/apps/logs/__init__.py b/apps/logs/__init__.py index 5271d575..fb9151c9 100644 --- a/apps/logs/__init__.py +++ b/apps/logs/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'logs.apps.LogsConfig' diff --git a/apps/logs/api/serializers.py b/apps/logs/api/serializers.py index cca94155..313db8e6 100644 --- a/apps/logs/api/serializers.py +++ b/apps/logs/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from rest_framework import serializers diff --git a/apps/logs/api/urls.py b/apps/logs/api/urls.py index 9f255dd5..2c9462c2 100644 --- a/apps/logs/api/urls.py +++ b/apps/logs/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .views import ChangelogViewSet diff --git a/apps/logs/api/views.py b/apps/logs/api/views.py index 655a473a..6bd4da70 100644 --- a/apps/logs/api/views.py +++ b/apps/logs/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django_filters.rest_framework import DjangoFilterBackend diff --git a/apps/logs/apps.py b/apps/logs/apps.py index 366981ec..2e3dd6f9 100644 --- a/apps/logs/apps.py +++ b/apps/logs/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/logs/models.py b/apps/logs/models.py index 8f2c2eda..79fb8524 100644 --- a/apps/logs/models.py +++ b/apps/logs/models.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.conf import settings diff --git a/apps/logs/signals.py b/apps/logs/signals.py index c4fc08f3..98d0270a 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib.contenttypes.models import ContentType diff --git a/apps/member/__init__.py b/apps/member/__init__.py index d0ab8586..6fce9278 100644 --- a/apps/member/__init__.py +++ b/apps/member/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'member.apps.MemberConfig' diff --git a/apps/member/admin.py b/apps/member/admin.py index 3adc54ed..f2683619 100644 --- a/apps/member/admin.py +++ b/apps/member/admin.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib import admin diff --git a/apps/member/api/serializers.py b/apps/member/api/serializers.py index 31bb1655..a39aa6e3 100644 --- a/apps/member/api/serializers.py +++ b/apps/member/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from rest_framework import serializers diff --git a/apps/member/api/urls.py b/apps/member/api/urls.py index 6f323186..3f5c7d64 100644 --- a/apps/member/api/urls.py +++ b/apps/member/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .views import ProfileViewSet, ClubViewSet, MembershipViewSet diff --git a/apps/member/api/views.py b/apps/member/api/views.py index a24a12d6..6d035016 100644 --- a/apps/member/api/views.py +++ b/apps/member/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django_filters.rest_framework import DjangoFilterBackend diff --git a/apps/member/apps.py b/apps/member/apps.py index 840c8783..d5b1f630 100644 --- a/apps/member/apps.py +++ b/apps/member/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/member/auth.py b/apps/member/auth.py index 06e48afa..67b2c966 100644 --- a/apps/member/auth.py +++ b/apps/member/auth.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from cas_server.auth import DjangoAuthUser # pragma: no cover diff --git a/apps/member/forms.py b/apps/member/forms.py index ef9cb24d..efa2e4a2 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import io diff --git a/apps/member/hashers.py b/apps/member/hashers.py index 659aab39..797ec485 100644 --- a/apps/member/hashers.py +++ b/apps/member/hashers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import hashlib diff --git a/apps/member/models.py b/apps/member/models.py index 78d59667..54637847 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import datetime diff --git a/apps/member/signals.py b/apps/member/signals.py index 94dd4021..869f9117 100644 --- a/apps/member/signals.py +++ b/apps/member/signals.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/apps/member/tables.py b/apps/member/tables.py index 836b3555..475c6eb2 100644 --- a/apps/member/tables.py +++ b/apps/member/tables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import date diff --git a/apps/member/templatetags/memberinfo.py b/apps/member/templatetags/memberinfo.py index c9b3b60c..224c2f9e 100644 --- a/apps/member/templatetags/memberinfo.py +++ b/apps/member/templatetags/memberinfo.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import date diff --git a/apps/member/urls.py b/apps/member/urls.py index 51d506ab..31fde8c6 100644 --- a/apps/member/urls.py +++ b/apps/member/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.urls import path diff --git a/apps/member/views.py b/apps/member/views.py index 4d18a40a..19f9b46f 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import timedelta, date diff --git a/apps/note/__init__.py b/apps/note/__init__.py index 828d8ed2..c28d3890 100644 --- a/apps/note/__init__.py +++ b/apps/note/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'note.apps.NoteConfig' diff --git a/apps/note/admin.py b/apps/note/admin.py index 9bd8add1..7397c895 100644 --- a/apps/note/admin.py +++ b/apps/note/admin.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib import admin diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index 980d992d..5c516c8e 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.conf import settings diff --git a/apps/note/api/urls.py b/apps/note/api/urls.py index 0522ebea..67d7371e 100644 --- a/apps/note/api/urls.py +++ b/apps/note/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .views import NotePolymorphicViewSet, AliasViewSet, ConsumerViewSet, \ diff --git a/apps/note/api/views.py b/apps/note/api/views.py index 5acda17c..aaab550f 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.conf import settings diff --git a/apps/note/apps.py b/apps/note/apps.py index 3d9c8583..ef674ac7 100644 --- a/apps/note/apps.py +++ b/apps/note/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/note/forms.py b/apps/note/forms.py index aa722b57..4b0a056a 100644 --- a/apps/note/forms.py +++ b/apps/note/forms.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import datetime diff --git a/apps/note/models/__init__.py b/apps/note/models/__init__.py index 4beb8112..daa3f676 100644 --- a/apps/note/models/__init__.py +++ b/apps/note/models/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser, Trust diff --git a/apps/note/models/notes.py b/apps/note/models/notes.py index a1697fd9..6b9e29d9 100644 --- a/apps/note/models/notes.py +++ b/apps/note/models/notes.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import unicodedata diff --git a/apps/note/models/transactions.py b/apps/note/models/transactions.py index 0fc299b0..0442129b 100644 --- a/apps/note/models/transactions.py +++ b/apps/note/models/transactions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.core.exceptions import ValidationError diff --git a/apps/note/signals.py b/apps/note/signals.py index 6ca702b9..ab1f405f 100644 --- a/apps/note/signals.py +++ b/apps/note/signals.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.utils import timezone diff --git a/apps/note/static/note/js/consos.js b/apps/note/static/note/js/consos.js index d812e451..2ca9ad00 100644 --- a/apps/note/static/note/js/consos.js +++ b/apps/note/static/note/js/consos.js @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +// Copyright (C) 2018-2025 by BDE ENS Paris-Saclay // SPDX-License-Identifier: GPL-3.0-or-later // When a transaction is performed, lock the interface to prevent spam clicks. diff --git a/apps/note/tables.py b/apps/note/tables.py index a4e944f9..d3dd6272 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import html diff --git a/apps/note/templatetags/getenv.py b/apps/note/templatetags/getenv.py index 3d97b6e9..684fedf8 100644 --- a/apps/note/templatetags/getenv.py +++ b/apps/note/templatetags/getenv.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django import template diff --git a/apps/note/templatetags/pretty_money.py b/apps/note/templatetags/pretty_money.py index 2bf838df..87974d35 100644 --- a/apps/note/templatetags/pretty_money.py +++ b/apps/note/templatetags/pretty_money.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django import template diff --git a/apps/note/tests/test_transactions.py b/apps/note/tests/test_transactions.py index 574e3575..4990c085 100644 --- a/apps/note/tests/test_transactions.py +++ b/apps/note/tests/test_transactions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from api.tests import TestAPI diff --git a/apps/note/urls.py b/apps/note/urls.py index f3d50845..81aa2648 100644 --- a/apps/note/urls.py +++ b/apps/note/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.urls import path diff --git a/apps/note/views.py b/apps/note/views.py index 7055ea12..4b800835 100644 --- a/apps/note/views.py +++ b/apps/note/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import json diff --git a/apps/permission/__init__.py b/apps/permission/__init__.py index de866996..27035e72 100644 --- a/apps/permission/__init__.py +++ b/apps/permission/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'permission.apps.PermissionConfig' diff --git a/apps/permission/admin.py b/apps/permission/admin.py index 82f8d4ab..7517aaf2 100644 --- a/apps/permission/admin.py +++ b/apps/permission/admin.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-lateré from django.contrib import admin diff --git a/apps/permission/api/serializers.py b/apps/permission/api/serializers.py index c1426a15..6e9e4909 100644 --- a/apps/permission/api/serializers.py +++ b/apps/permission/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from rest_framework import serializers diff --git a/apps/permission/api/urls.py b/apps/permission/api/urls.py index fece3d7a..e43101fc 100644 --- a/apps/permission/api/urls.py +++ b/apps/permission/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .views import PermissionViewSet, RoleViewSet diff --git a/apps/permission/api/views.py b/apps/permission/api/views.py index 39ff353c..09247f7c 100644 --- a/apps/permission/api/views.py +++ b/apps/permission/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django_filters.rest_framework import DjangoFilterBackend diff --git a/apps/permission/apps.py b/apps/permission/apps.py index 1531479c..3f8e9732 100644 --- a/apps/permission/apps.py +++ b/apps/permission/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/permission/backends.py b/apps/permission/backends.py index f246f0eb..a7beeeb6 100644 --- a/apps/permission/backends.py +++ b/apps/permission/backends.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import date, timedelta diff --git a/apps/permission/decorators.py b/apps/permission/decorators.py index da36b7dc..a3a030f1 100644 --- a/apps/permission/decorators.py +++ b/apps/permission/decorators.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import sys from functools import lru_cache diff --git a/apps/permission/models.py b/apps/permission/models.py index e975485a..c6c5065a 100644 --- a/apps/permission/models.py +++ b/apps/permission/models.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import functools diff --git a/apps/permission/permissions.py b/apps/permission/permissions.py index 3840a3cd..34875774 100644 --- a/apps/permission/permissions.py +++ b/apps/permission/permissions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from rest_framework.permissions import DjangoObjectPermissions diff --git a/apps/permission/scopes.py b/apps/permission/scopes.py index af504b18..6ee5818f 100644 --- a/apps/permission/scopes.py +++ b/apps/permission/scopes.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from oauth2_provider.oauth2_validators import OAuth2Validator from oauth2_provider.scopes import BaseScopes diff --git a/apps/permission/signals.py b/apps/permission/signals.py index 37b7f7c3..b2394c6f 100644 --- a/apps/permission/signals.py +++ b/apps/permission/signals.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.core.exceptions import PermissionDenied diff --git a/apps/permission/tables.py b/apps/permission/tables.py index 955b14bb..ec83f233 100644 --- a/apps/permission/tables.py +++ b/apps/permission/tables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import django_tables2 as tables diff --git a/apps/permission/templatetags/perms.py b/apps/permission/templatetags/perms.py index 241b2c73..4d766f41 100644 --- a/apps/permission/templatetags/perms.py +++ b/apps/permission/templatetags/perms.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib.contenttypes.models import ContentType diff --git a/apps/permission/tests/test_oauth2.py b/apps/permission/tests/test_oauth2.py index 889ad588..579dc5c8 100644 --- a/apps/permission/tests/test_oauth2.py +++ b/apps/permission/tests/test_oauth2.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import timedelta diff --git a/apps/permission/tests/test_permission_denied.py b/apps/permission/tests/test_permission_denied.py index 7d3fef5f..792bd1de 100644 --- a/apps/permission/tests/test_permission_denied.py +++ b/apps/permission/tests/test_permission_denied.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import timedelta, date diff --git a/apps/permission/tests/test_permission_queries.py b/apps/permission/tests/test_permission_queries.py index 11634993..768e9047 100644 --- a/apps/permission/tests/test_permission_queries.py +++ b/apps/permission/tests/test_permission_queries.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import date, timedelta diff --git a/apps/permission/tests/test_rights_page.py b/apps/permission/tests/test_rights_page.py index ee777f48..44066b36 100644 --- a/apps/permission/tests/test_rights_page.py +++ b/apps/permission/tests/test_rights_page.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib.auth.models import User diff --git a/apps/permission/urls.py b/apps/permission/urls.py index 31e44b77..e7ceb5ae 100644 --- a/apps/permission/urls.py +++ b/apps/permission/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.conf import settings diff --git a/apps/permission/views.py b/apps/permission/views.py index 053efa39..e7de920e 100644 --- a/apps/permission/views.py +++ b/apps/permission/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from collections import OrderedDict from datetime import date diff --git a/apps/registration/__init__.py b/apps/registration/__init__.py index 024c5572..a7a4d512 100644 --- a/apps/registration/__init__.py +++ b/apps/registration/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'registration.apps.RegistrationConfig' diff --git a/apps/registration/apps.py b/apps/registration/apps.py index 44067233..37ce34b9 100644 --- a/apps/registration/apps.py +++ b/apps/registration/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/registration/forms.py b/apps/registration/forms.py index 89dc5de4..673cc500 100644 --- a/apps/registration/forms.py +++ b/apps/registration/forms.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django import forms diff --git a/apps/registration/tables.py b/apps/registration/tables.py index 151d5d74..2d1df2c3 100644 --- a/apps/registration/tables.py +++ b/apps/registration/tables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import django_tables2 as tables diff --git a/apps/registration/tests/test_registration.py b/apps/registration/tests/test_registration.py index a37ad12a..1950586c 100644 --- a/apps/registration/tests/test_registration.py +++ b/apps/registration/tests/test_registration.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib.auth.models import User diff --git a/apps/registration/tokens.py b/apps/registration/tokens.py index 332c4ee1..587e8847 100644 --- a/apps/registration/tokens.py +++ b/apps/registration/tokens.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later # Copied from https://gitlab.crans.org/bombar/codeflix/-/blob/master/codeflix/codeflix/tokens.py diff --git a/apps/registration/urls.py b/apps/registration/urls.py index 45ca4c60..25b16116 100644 --- a/apps/registration/urls.py +++ b/apps/registration/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.urls import path diff --git a/apps/registration/views.py b/apps/registration/views.py index bb23a0b4..ec2e07a7 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django import forms diff --git a/apps/treasury/__init__.py b/apps/treasury/__init__.py index 21ef103b..edb5217a 100644 --- a/apps/treasury/__init__.py +++ b/apps/treasury/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'treasury.apps.TreasuryConfig' diff --git a/apps/treasury/admin.py b/apps/treasury/admin.py index c435a4ff..2adaf7c5 100644 --- a/apps/treasury/admin.py +++ b/apps/treasury/admin.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-lateré from django.contrib import admin diff --git a/apps/treasury/api/serializers.py b/apps/treasury/api/serializers.py index 79c2445d..c70912ef 100644 --- a/apps/treasury/api/serializers.py +++ b/apps/treasury/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.db import transaction from rest_framework import serializers diff --git a/apps/treasury/api/urls.py b/apps/treasury/api/urls.py index a4ba7c1e..5f59e6d1 100644 --- a/apps/treasury/api/urls.py +++ b/apps/treasury/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .views import InvoiceViewSet, ProductViewSet, RemittanceViewSet, RemittanceTypeViewSet, SogeCreditViewSet diff --git a/apps/treasury/api/views.py b/apps/treasury/api/views.py index 418bf220..d65024ac 100644 --- a/apps/treasury/api/views.py +++ b/apps/treasury/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django_filters.rest_framework import DjangoFilterBackend diff --git a/apps/treasury/apps.py b/apps/treasury/apps.py index e25accc0..b12235c3 100644 --- a/apps/treasury/apps.py +++ b/apps/treasury/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/treasury/forms.py b/apps/treasury/forms.py index c3a3fc49..911db2ec 100644 --- a/apps/treasury/forms.py +++ b/apps/treasury/forms.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from crispy_forms.helper import FormHelper diff --git a/apps/treasury/models.py b/apps/treasury/models.py index 69fc6e6b..525264f8 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import date diff --git a/apps/treasury/signals.py b/apps/treasury/signals.py index 67b516fc..5ec3cd47 100644 --- a/apps/treasury/signals.py +++ b/apps/treasury/signals.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from treasury.models import SpecialTransactionProxy, RemittanceType diff --git a/apps/treasury/tables.py b/apps/treasury/tables.py index ae87ab1a..cf17a2c8 100644 --- a/apps/treasury/tables.py +++ b/apps/treasury/tables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import django_tables2 as tables diff --git a/apps/treasury/templatetags/escape_tex.py b/apps/treasury/templatetags/escape_tex.py index 656e53ac..b8a1c589 100644 --- a/apps/treasury/templatetags/escape_tex.py +++ b/apps/treasury/templatetags/escape_tex.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django import template diff --git a/apps/treasury/tests/test_treasury.py b/apps/treasury/tests/test_treasury.py index 1bfc8993..8feb5485 100644 --- a/apps/treasury/tests/test_treasury.py +++ b/apps/treasury/tests/test_treasury.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from api.tests import TestAPI diff --git a/apps/treasury/urls.py b/apps/treasury/urls.py index 6e2517b3..6b319d4c 100644 --- a/apps/treasury/urls.py +++ b/apps/treasury/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.urls import path diff --git a/apps/treasury/views.py b/apps/treasury/views.py index 33bdb88c..eab144c3 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import os diff --git a/apps/wei/__init__.py b/apps/wei/__init__.py index c4ea977d..966f3ee5 100644 --- a/apps/wei/__init__.py +++ b/apps/wei/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'wei.apps.WeiConfig' diff --git a/apps/wei/admin.py b/apps/wei/admin.py index f7c94227..b80895be 100644 --- a/apps/wei/admin.py +++ b/apps/wei/admin.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from note_kfet.admin import admin_site diff --git a/apps/wei/api/serializers.py b/apps/wei/api/serializers.py index 93915ea9..06c8d356 100644 --- a/apps/wei/api/serializers.py +++ b/apps/wei/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from rest_framework import serializers diff --git a/apps/wei/api/urls.py b/apps/wei/api/urls.py index e5692224..f9ecbd12 100644 --- a/apps/wei/api/urls.py +++ b/apps/wei/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .views import WEIClubViewSet, BusViewSet, BusTeamViewSet, WEIRoleViewSet, WEIRegistrationViewSet, \ diff --git a/apps/wei/api/views.py b/apps/wei/api/views.py index 13f04a91..f6eecbfc 100644 --- a/apps/wei/api/views.py +++ b/apps/wei/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django_filters.rest_framework import DjangoFilterBackend diff --git a/apps/wei/apps.py b/apps/wei/apps.py index 2f89420b..57c6a238 100644 --- a/apps/wei/apps.py +++ b/apps/wei/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/wei/forms/__init__.py b/apps/wei/forms/__init__.py index fce2373a..e1a09c3a 100644 --- a/apps/wei/forms/__init__.py +++ b/apps/wei/forms/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .registration import WEIForm, WEIRegistrationForm, WEIMembership1AForm, WEIMembershipForm, BusForm, BusTeamForm diff --git a/apps/wei/forms/registration.py b/apps/wei/forms/registration.py index cf0579aa..38568b93 100644 --- a/apps/wei/forms/registration.py +++ b/apps/wei/forms/registration.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from bootstrap_datepicker_plus.widgets import DatePickerInput diff --git a/apps/wei/forms/surveys/__init__.py b/apps/wei/forms/surveys/__init__.py index 733f7c37..0a33bc16 100644 --- a/apps/wei/forms/surveys/__init__.py +++ b/apps/wei/forms/surveys/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm diff --git a/apps/wei/forms/surveys/base.py b/apps/wei/forms/surveys/base.py index 02bf0be8..3f3bff3b 100644 --- a/apps/wei/forms/surveys/base.py +++ b/apps/wei/forms/surveys/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from typing import Optional, List diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index 83f2d8ce..3e5b384f 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import time diff --git a/apps/wei/forms/surveys/wei2022.py b/apps/wei/forms/surveys/wei2022.py index 2550bc94..037fe7e1 100644 --- a/apps/wei/forms/surveys/wei2022.py +++ b/apps/wei/forms/surveys/wei2022.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import time diff --git a/apps/wei/forms/surveys/wei2023.py b/apps/wei/forms/surveys/wei2023.py index 488ee7c3..c29c4f26 100644 --- a/apps/wei/forms/surveys/wei2023.py +++ b/apps/wei/forms/surveys/wei2023.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from functools import lru_cache diff --git a/apps/wei/forms/surveys/wei2024.py b/apps/wei/forms/surveys/wei2024.py index 518d58f0..95419f49 100644 --- a/apps/wei/forms/surveys/wei2024.py +++ b/apps/wei/forms/surveys/wei2024.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from functools import lru_cache diff --git a/apps/wei/management/commands/export_wei_registrations.py b/apps/wei/management/commands/export_wei_registrations.py index 39fd238a..a754e40a 100644 --- a/apps/wei/management/commands/export_wei_registrations.py +++ b/apps/wei/management/commands/export_wei_registrations.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.core.management import BaseCommand, CommandError diff --git a/apps/wei/management/commands/import_scores.py b/apps/wei/management/commands/import_scores.py index 915ce696..acce9193 100644 --- a/apps/wei/management/commands/import_scores.py +++ b/apps/wei/management/commands/import_scores.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import argparse import sys diff --git a/apps/wei/management/commands/wei_algorithm.py b/apps/wei/management/commands/wei_algorithm.py index 8d070242..68811d63 100644 --- a/apps/wei/management/commands/wei_algorithm.py +++ b/apps/wei/management/commands/wei_algorithm.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from argparse import ArgumentParser, FileType diff --git a/apps/wei/models.py b/apps/wei/models.py index 76fd465d..f4169316 100644 --- a/apps/wei/models.py +++ b/apps/wei/models.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import json diff --git a/apps/wei/tables.py b/apps/wei/tables.py index 7cf26b02..de5c84af 100644 --- a/apps/wei/tables.py +++ b/apps/wei/tables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from datetime import date diff --git a/apps/wei/tests/test_wei_algorithm_2021.py b/apps/wei/tests/test_wei_algorithm_2021.py index d6782fe8..5983d556 100644 --- a/apps/wei/tests/test_wei_algorithm_2021.py +++ b/apps/wei/tests/test_wei_algorithm_2021.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import random diff --git a/apps/wei/tests/test_wei_algorithm_2022.py b/apps/wei/tests/test_wei_algorithm_2022.py index 2ee3da6e..ab7c8021 100644 --- a/apps/wei/tests/test_wei_algorithm_2022.py +++ b/apps/wei/tests/test_wei_algorithm_2022.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import random diff --git a/apps/wei/tests/test_wei_algorithm_2023.py b/apps/wei/tests/test_wei_algorithm_2023.py index 55c4a6d7..be557322 100644 --- a/apps/wei/tests/test_wei_algorithm_2023.py +++ b/apps/wei/tests/test_wei_algorithm_2023.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import random diff --git a/apps/wei/tests/test_wei_algorithm_2024.py b/apps/wei/tests/test_wei_algorithm_2024.py index a22b0a3a..bae36399 100644 --- a/apps/wei/tests/test_wei_algorithm_2024.py +++ b/apps/wei/tests/test_wei_algorithm_2024.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import random diff --git a/apps/wei/tests/test_wei_registration.py b/apps/wei/tests/test_wei_registration.py index 21da07f8..ffff04c9 100644 --- a/apps/wei/tests/test_wei_registration.py +++ b/apps/wei/tests/test_wei_registration.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import subprocess diff --git a/apps/wei/urls.py b/apps/wei/urls.py index c96fa040..3084dd51 100644 --- a/apps/wei/urls.py +++ b/apps/wei/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.urls import path diff --git a/apps/wei/views.py b/apps/wei/views.py index 76943198..a2e8ccff 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import os diff --git a/apps/wrapped/__init__.py b/apps/wrapped/__init__.py index e9c45ef0..7ea5af65 100644 --- a/apps/wrapped/__init__.py +++ b/apps/wrapped/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later default_app_config = 'activity.apps.WrappedConfig' diff --git a/apps/wrapped/admin.py b/apps/wrapped/admin.py index a50ec0ad..c245e71f 100644 --- a/apps/wrapped/admin.py +++ b/apps/wrapped/admin.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib import admin diff --git a/apps/wrapped/api/serializers.py b/apps/wrapped/api/serializers.py index d156ae75..46e5a6ff 100644 --- a/apps/wrapped/api/serializers.py +++ b/apps/wrapped/api/serializers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from rest_framework import serializers diff --git a/apps/wrapped/api/urls.py b/apps/wrapped/api/urls.py index b06cddb9..181806cf 100644 --- a/apps/wrapped/api/urls.py +++ b/apps/wrapped/api/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from .views import WrappedViewSet, BdeViewSet diff --git a/apps/wrapped/api/views.py b/apps/wrapped/api/views.py index 72294230..da048b1c 100644 --- a/apps/wrapped/api/views.py +++ b/apps/wrapped/api/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from api.viewsets import ReadProtectedModelViewSet diff --git a/apps/wrapped/apps.py b/apps/wrapped/apps.py index 83630287..2a126f3b 100644 --- a/apps/wrapped/apps.py +++ b/apps/wrapped/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.apps import AppConfig diff --git a/apps/wrapped/models.py b/apps/wrapped/models.py index b9ebc0e2..a19ddb3f 100644 --- a/apps/wrapped/models.py +++ b/apps/wrapped/models.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.db import models diff --git a/apps/wrapped/tables.py b/apps/wrapped/tables.py index 6ec37543..eeaef332 100644 --- a/apps/wrapped/tables.py +++ b/apps/wrapped/tables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.utils.html import format_html diff --git a/apps/wrapped/templates/wrapped/1/wrapped_base.html b/apps/wrapped/templates/wrapped/1/wrapped_base.html index 3cd72a99..2c0bcf13 100644 --- a/apps/wrapped/templates/wrapped/1/wrapped_base.html +++ b/apps/wrapped/templates/wrapped/1/wrapped_base.html @@ -1,6 +1,6 @@ {% load static i18n pretty_money getenv %} {% comment %} -Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +Copyright (C) 2018-2025 by BDE ENS Paris-Saclay SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} diff --git a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html index 57da4a3f..f9c2d178 100644 --- a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html +++ b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html @@ -1,6 +1,6 @@ {% extends "wrapped/1/wrapped_base.html" %} {% comment %} -COPYRIGHT (C) 2018-2024 BDE ENS Paris-Saclay +COPYRIGHT (C) 2018-2025 BDE ENS Paris-Saclay SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} {% load i18n pretty_money %} diff --git a/apps/wrapped/templates/wrapped/1/wrapped_view_user.html b/apps/wrapped/templates/wrapped/1/wrapped_view_user.html index 3e421171..09d413cf 100644 --- a/apps/wrapped/templates/wrapped/1/wrapped_view_user.html +++ b/apps/wrapped/templates/wrapped/1/wrapped_view_user.html @@ -1,6 +1,6 @@ {% extends "wrapped/1/wrapped_base.html" %} {% comment %} -COPYRIGHT (C) 2018-2024 BDE ENS Paris-Saclay +COPYRIGHT (C) 2018-2025 BDE ENS Paris-Saclay SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} {% load i18n pretty_money %} diff --git a/apps/wrapped/urls.py b/apps/wrapped/urls.py index dde4458b..0d663c01 100644 --- a/apps/wrapped/urls.py +++ b/apps/wrapped/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.urls import path diff --git a/apps/wrapped/views.py b/apps/wrapped/views.py index 0a16fd92..8c7f834d 100644 --- a/apps/wrapped/views.py +++ b/apps/wrapped/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import json diff --git a/entrypoint.sh b/entrypoint.sh index 60a0a912..7f19eb59 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later if [ -z ${NOTE_URL+x} ]; then diff --git a/note_kfet/admin.py b/note_kfet/admin.py index 8b47de06..6bda409f 100644 --- a/note_kfet/admin.py +++ b/note_kfet/admin.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.conf import settings diff --git a/note_kfet/inputs.py b/note_kfet/inputs.py index b68adf4d..a05b61e4 100644 --- a/note_kfet/inputs.py +++ b/note_kfet/inputs.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from json import dumps as json_dumps diff --git a/note_kfet/middlewares.py b/note_kfet/middlewares.py index 47948adb..ac3c88aa 100644 --- a/note_kfet/middlewares.py +++ b/note_kfet/middlewares.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from threading import local diff --git a/note_kfet/settings/__init__.py b/note_kfet/settings/__init__.py index 22d5fd0c..2dfccfd5 100644 --- a/note_kfet/settings/__init__.py +++ b/note_kfet/settings/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.utils.translation import gettext_lazy as _ diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index 113cf626..0c0642f9 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later # This file implements sane defaults to use in production. diff --git a/note_kfet/settings/development.py b/note_kfet/settings/development.py index c83f301a..b8cf3735 100644 --- a/note_kfet/settings/development.py +++ b/note_kfet/settings/development.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later ######################## diff --git a/note_kfet/settings/secrets_example.py b/note_kfet/settings/secrets_example.py index 49a77a95..c8023cfd 100644 --- a/note_kfet/settings/secrets_example.py +++ b/note_kfet/settings/secrets_example.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later OPTIONAL_APPS = [ diff --git a/note_kfet/static/js/base.js b/note_kfet/static/js/base.js index 3111c362..d7814485 100644 --- a/note_kfet/static/js/base.js +++ b/note_kfet/static/js/base.js @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +// Copyright (C) 2018-2025 by BDE ENS Paris-Saclay // SPDX-License-Identifier: GPL-3.0-or-later /** diff --git a/note_kfet/urls.py b/note_kfet/urls.py index 3bc3da41..fb4b2323 100644 --- a/note_kfet/urls.py +++ b/note_kfet/urls.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.conf import settings diff --git a/note_kfet/views.py b/note_kfet/views.py index faf14a67..9ea86d30 100644 --- a/note_kfet/views.py +++ b/note_kfet/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib.auth.mixins import LoginRequiredMixin From 7f0a3784e914d045a4eacdeb3af80bfbc66c961d Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 11 Mar 2025 10:15:11 +0100 Subject: [PATCH 63/84] Rave Part[list] colors --- note_kfet/static/css/custom.css | 84 ++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/note_kfet/static/css/custom.css b/note_kfet/static/css/custom.css index 15efa3f9..d95ffaae 100755 --- a/note_kfet/static/css/custom.css +++ b/note_kfet/static/css/custom.css @@ -61,6 +61,11 @@ mark { /* Make navbar more readable */ .navbar-dark .navbar-nav .nav-link { color: rgba(255, 255, 255, .75); + text-shadow: 2px 2px 15px #ffeb40; +} + +.navbar-brand { + text-shadow: 2px 2px 15px #ffeb40; } /* Last BDE colors */ @@ -68,7 +73,8 @@ mark { /* background-color: rgb(18, 67, 4) !important; */ /* MODE VIEUXCON=ON */ /* background-color: rgb(166, 0, 2) !important; */ - background-color: rgb(100, 30, 100) !important; + background-color: rgb(0, 0, 0); + background-image: url('/static/wrapped/img/1/bg.png'); } html { @@ -83,81 +89,95 @@ body { .btn-outline-primary:hover, .btn-outline-primary:not(:disabled):not(.disabled).active, .btn-outline-primary:not(:disabled):not(.disabled):active { - color: rgb(240, 200, 240); - background-color: rgb(30, 120, 150); - border-color: rgb(190, 150, 190); + color: rgb(0, 0, 0); + background-color: rgb(255, 0, 101); + border-color: rgb(255, 203, 32); } .btn-outline-primary { - color: #a2a; - background-color: #6bc; - border-color: #719; + color: #000; + background-color: #ffcb20; + border-color: #000; } .turbolinks-progress-bar { - background-color: #12342E; + background-color: #ffffff; } .btn-primary:hover, .btn-primary:not(:disabled):not(.disabled).active, .btn-primary:not(:disabled):not(.disabled):active { - color: rgb(150, 200, 240); - background-color: rgb(50, 100, 140); - border-color: rgb(0, 0, 0); + color: rgb(0, 0, 0); + background-color: rgb(255, 0, 101); + border-color: rgb(255, 203, 32); } .btn-primary { - color: #eae; - background-color: #616; - border-color: #000000; + color: #ffcb20; + background-color: #000000; + border-color: #ffcd20; } .border-primary { - border-color: rgb(222, 180, 222) !important; + border-color: rgb(255, 255, 255) !important; } .btn-secondary { - color: #eae; - background-color: #616; - border-color: #000000; + color: #ff0065; + background-color: #000000; + border-color: #ff0065; } .btn-secondary:hover, .btn-secondary:not(:disabled):not(.disabled).active, .btn-secondary:not(:disabled):not(.disabled):active { - color: rgb(150, 200, 240); - background-color: rgb(50, 100, 140); - border-color: rgb(0, 0, 0); + color: rgb(0, 0, 0); + background-color: rgb(255, 203, 32); + border-color: rgb(255, 0, 101); } +.btn-outline-dark:nth-child(even) { + color: rgba(255, 203, 32, 75%); +} + +.btn-outline-dark:nth-child(odd) { + color: rgba(255, 0, 101, 75%); +} .btn-outline-dark { - color: #000000; - border-color: #000000; + background-color: #222; + border-color: #61605b; } -.btn-outline-dark:hover, +.btn-outline-dark:hover:nth-child(even), .btn-outline-dark:not(:disabled):not(.disabled).active, .btn-outline-dark:not(:disabled):not(.disabled):active { - color: rgb(50, 100, 160); - background-color: rgb(240, 150, 240); - border-color: rgb(50, 100, 160); + color: rgb(0, 0, 0); + background-color: rgb(255, 203, 32); + border-color: rgb(255, 0, 101); } +.btn-outline-dark:hover:nth-child(odd), +.btn-outline-dark:not(:disabled):not(.disabled).active, +.btn-outline-dark:not(:disabled):not(.disabled):active { + color: rgb(0, 0, 0); + background-color: rgb(255, 203, 32); + border-color: rgb(255, 0, 101); +} a { - color: rgb(0, 150, 150); + color: rgb(255, 0, 101); } a:hover { - color: rgb(200, 0, 200); + color: rgb(255, 203, 32); } .form-control:focus { - box-shadow: 0 0 0 0.25rem rgb(0 150 150 / 50%); - border-color: rgb(0, 200, 200); + box-shadow: 0 0 0 0.25rem rgb(255 0 101 / 50%); + border-color: rgb(255, 0, 101); } .btn-outline-primary.focus { - box-shadow: 0 0 0 0.25rem rgb(0 150 150 / 22%); + box-shadow: 0 0 0 0.25rem rgb(255 203 32 / 22%); } From ec1bd454810d940625838b222af1d638816aba1f Mon Sep 17 00:00:00 2001 From: thomasl Date: Tue, 11 Mar 2025 19:14:09 +0100 Subject: [PATCH 64/84] Update file initial.json --- apps/permission/fixtures/initial.json | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 15f96657..a9333822 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4186,6 +4186,54 @@ "description": "Voir la note d'un club enfant" } }, + { + "model": "permission.permission", + "pk": 266, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"OR\", {\"source_alias\": \"Carte bancaire\"}, {\"source_alias\": \"Espèces\"}, {\"source_alias\": \"Chèque\"}, {\"source_alias\": \"Virement bancaire\"}]", + "type": "view", + "mask": 2, + "field": "", + "permanent": false, + "description": "Voir les transactions de rechargement" + } + }, + { + "model": "permission.permission", + "pk": 267, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"OR\", {\"source_alias\": \"Carte bancaire\"}, {\"source_alias\": \"Espèces\"}, {\"source_alias\": \"Chèque\"}, {\"source_alias\": \"Virement bancaire\"}]", + "type": "change", + "mask": 2, + "field": "valid", + "permanent": false, + "description": "Mettre à jour le statut de validation d'une transaction de rechargement" + } + }, + { + "model": "permission.permission", + "pk": 268, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"OR\", {\"source_alias\": \"Carte bancaire\"}, {\"source_alias\": \"Espèces\"}, {\"source_alias\": \"Chèque\"}, {\"source_alias\": \"Virement bancaire\"}]", + "type": "change", + "mask": 2, + "field": "invalidity_reason", + "permanent": false, + "description": "Modifier la raison d'invalidité d'une transaction de rechargement" + } + }, { "model": "permission.role", "pk": 1, From 2ae32ee3b6b08054fabbd0e06766eaa266d309ca Mon Sep 17 00:00:00 2001 From: thomasl Date: Tue, 11 Mar 2025 19:26:49 +0100 Subject: [PATCH 65/84] Update file initial.json --- apps/permission/fixtures/initial.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index a9333822..ede52ed6 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4234,6 +4234,22 @@ "description": "Modifier la raison d'invalidité d'une transaction de rechargement" } }, + { + "model": "permission.permission", + "pk": 268, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}}, {\"valid\": false}]]", + "type": "add", + "mask": 2, + "field": "", + "permanent": false, + "description": "Créer une transaction de ou vers la note d'un club tant que la source reste au dessus de -50 €" + } + }, { "model": "permission.role", "pk": 1, From 12ef258ff09bf4abfb14b4b014bcfe2b3bfe5e13 Mon Sep 17 00:00:00 2001 From: thomasl Date: Tue, 11 Mar 2025 19:27:02 +0100 Subject: [PATCH 66/84] Update file initial.json --- apps/permission/fixtures/initial.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index ede52ed6..34372e08 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4236,7 +4236,7 @@ }, { "model": "permission.permission", - "pk": 268, + "pk": 269, "fields": { "model": [ "note", From 562dcfb908bdb3d35ca9ffb11611b8d97214ca8c Mon Sep 17 00:00:00 2001 From: thomasl Date: Tue, 11 Mar 2025 19:34:56 +0100 Subject: [PATCH 67/84] Update file initial.json --- apps/permission/fixtures/initial.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 34372e08..879bc1d4 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -4237,6 +4237,22 @@ { "model": "permission.permission", "pk": 269, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"OR\", {\"source_alias\": \"Carte bancaire\"}, {\"source_alias\": \"Espèces\"}, {\"source_alias\": \"Chèque\"}, {\"source_alias\": \"Virement bancaire\"}]", + "type": "add", + "mask": 2, + "field": "", + "permanent": false, + "description": "Créer une transaction de rechargement" + } + }, + { + "model": "permission.permission", + "pk": 270, "fields": { "model": [ "note", From 4799b2c52d09e438f5ef0da44a9232a4c9505490 Mon Sep 17 00:00:00 2001 From: quark Date: Wed, 12 Mar 2025 23:42:37 +0100 Subject: [PATCH 68/84] Resize and compress image, add shiny button --- apps/note/static/note/js/consos.js | 7 ++++++ note_kfet/static/css/custom.css | 35 ++++++++++++++--------------- note_kfet/static/img/rp_bg.png | Bin 0 -> 84089 bytes note_kfet/static/js/konami.js | 2 +- 4 files changed, 25 insertions(+), 19 deletions(-) create mode 100644 note_kfet/static/img/rp_bg.png diff --git a/apps/note/static/note/js/consos.js b/apps/note/static/note/js/consos.js index 2ca9ad00..d08d93bd 100644 --- a/apps/note/static/note/js/consos.js +++ b/apps/note/static/note/js/consos.js @@ -294,3 +294,10 @@ searchbar.addEventListener("keyup", function (e) { if (firstMatch && e.key === "Enter") firstMatch.click() }); + +function createshiny() { + const list_btn = document.querySelectorAll('.btn-outline-dark') + const shiny_class = list_btn[Math.floor(Math.random() * list_btn.length)].classList + shiny_class.replace('btn-outline-dark', 'btn-outline-dark-shiny') +} +createshiny() diff --git a/note_kfet/static/css/custom.css b/note_kfet/static/css/custom.css index d95ffaae..4145745c 100755 --- a/note_kfet/static/css/custom.css +++ b/note_kfet/static/css/custom.css @@ -74,7 +74,7 @@ mark { /* MODE VIEUXCON=ON */ /* background-color: rgb(166, 0, 2) !important; */ background-color: rgb(0, 0, 0); - background-image: url('/static/wrapped/img/1/bg.png'); + background-image: url('/static/img/rp_bg.png'); } html { @@ -136,33 +136,32 @@ body { border-color: rgb(255, 0, 101); } -.btn-outline-dark:nth-child(even) { - color: rgba(255, 203, 32, 75%); -} - -.btn-outline-dark:nth-child(odd) { +.btn-outline-dark-shiny { + background-color: #222; + border-color: #61605b; color: rgba(255, 0, 101, 75%); } -.btn-outline-dark { - background-color: #222; - border-color: #61605b; -} - -.btn-outline-dark:hover:nth-child(even), -.btn-outline-dark:not(:disabled):not(.disabled).active, -.btn-outline-dark:not(:disabled):not(.disabled):active { +.btn-outline-dark-shiny:hover, +.btn-outline-dark-shiny:not(:disabled):not(.disabled).active, +.btn-outline-dark-shiny:not(:disabled):not(.disabled):active { color: rgb(0, 0, 0); background-color: rgb(255, 203, 32); border-color: rgb(255, 0, 101); } -.btn-outline-dark:hover:nth-child(odd), +.btn-outline-dark { + background-color: #222; + border-color: #61605b; + color: rgba(255, 203, 32, 75%); +} + +.btn-outline-dark:hover, .btn-outline-dark:not(:disabled):not(.disabled).active, .btn-outline-dark:not(:disabled):not(.disabled):active { - color: rgb(0, 0, 0); - background-color: rgb(255, 203, 32); - border-color: rgb(255, 0, 101); + color: rgb(0, 0, 0); + background-color: rgb(255, 0, 101); + border-color: rgb(255, 203, 32); } a { diff --git a/note_kfet/static/img/rp_bg.png b/note_kfet/static/img/rp_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..6a1fbf3ad9b413a9b56569f55446373715724e42 GIT binary patch literal 84089 zcmXVXWn5I>_w|(+n&AsWcXvs5cXxM4Gk|nTcgN5n-5@0$l2XEegdmMHf|MZq`8}WK z<+*RpzIU&^&tB`U6Q`-JfQ3$i4gdg_lA^3O0HDkR00K2C;_DM?oNt=1i@%zxx^l0} zXTT00K>Y{UA^>kt07NhVdI9Wize>CTfK$MX7(gcjFqiz#awEIRQKgfS3Zn zWC8wz1G+tcG8%xi2N+KQn$!SR7{K!ufQ$ngp8+KyfKUaX5CI640P+pMkPY}x51^3+ zNcjN#C%{%6ptJxO-T}>)I01E=39s#C50D}=gZ3s}^1&sFrlQ}?p6EIu=bQl5f3ZVZBFy#jDz5{v#fa(&U zy9Vfs1H?0cRv{o>3&{8ZT%-VoJU})9XwU=Ten7<-pz8n>mjS(OK;#>sV+Szt0{C74 zM?0Ws1uzT)YJGr8Ai!M$Nb~~Ab$~28fLjm9t^)dI0KF7IA`Or*0AO2ysS1#m0Z5Yp zK@WgE0T4(7grWexBtTFHpl$->!T}ypfJhgh?gSJ+0SYLfHyCIL7h_^zOhhIs^qVOO zJRD?NGE_b`tg8ueZ5jNn&q8uS{~;m1nXTo`3SSevA?wcLd~ZqKlFpv^o~|Z=HQbq+l@7y1 zh0soixF~{&gaC9lBHCBL^=m-nOX=G*UwSJ|;uv?DP)Dlqa-PK|{vdnG-5%k=BJR>C zW;IDjei&0{4ks*xG2WBbLY=58o~1m7|^<^RJkNtA4?z%3$ zrLOqv)3)iV_HQ$6Td(<7H>UGfr~7T^Rt55d-33)O$o(q67(8xEFPDVxaBN`5pU!}igG&`RzIDE zRXA#VT4@-wHvuF1=Xj5PEm&sn6))r8%B@}mY=YY|YM#P?$6|s3)QBR}cD_gkr{YY| zlqFb-do(3WWG&0_p~?KdhMOE^Ii%5_I*M<|sG;0o4ET~3e3!bnLxe19ufD~RhxR*S z<9!sg>?7wkst}wdB?f0)Bei%5<~qj=XXrwgI7YIJuBg-uGi5f_em5(vj`nGr;7nYYF zzuvAW_)TlhWSC!V4f72UG$o)L$kd0$GV;w;G|T7tMc7U?8BooB4JYulDWnv&*S{sn zTU|jBLA%(OeJ+GXT02$9O2xtmv~!b;3}mc&&4;|n%JlK=*A!Wi4dKW+AN7NOmEgm$ z1SVLBMCPUcqnNUEGU7~X&z|Bskd&9>XV?x7L}6UfJ5D05h_!fZ&jOt}XDcX|(u)

)u{W0E%48+pNq>HZnMANeU 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 literal 0 HcmV?d00001 diff --git a/note_kfet/static/js/konami.js b/note_kfet/static/js/konami.js index add7fb0b..91e4e2ce 100644 --- a/note_kfet/static/js/konami.js +++ b/note_kfet/static/js/konami.js @@ -26,7 +26,7 @@ function afterKonami () { }) rythm.addRythm('d-flex', 'color', 50, 50, { from: [64, 64, 64], - to: [128, 64, 128] + to: [255, 0, 101] }) rythm.addRythm('nav-link', 'jump', 150, 50, { min: 0, From bd7e6b8ad471185c737c8923bd00af634881603c Mon Sep 17 00:00:00 2001 From: quark Date: Thu, 13 Mar 2025 21:01:50 +0100 Subject: [PATCH 69/84] add table, add some translation --- .../wrapped/1/wrapped_view_club.html | 6 +- .../templates/wrapped/wrapped_list.html | 31 +++++--- apps/wrapped/views.py | 23 ++++-- locale/fr/LC_MESSAGES/django.po | 74 +++++++++++-------- 4 files changed, 83 insertions(+), 51 deletions(-) diff --git a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html index 57da4a3f..6d295e9e 100644 --- a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html +++ b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html @@ -1,6 +1,6 @@ {% extends "wrapped/1/wrapped_base.html" %} {% comment %} -COPYRIGHT (C) 2018-2024 BDE ENS Paris-Saclay +COPYRIGHT (C) 2018-2025 BDE ENS Paris-Saclay SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} {% load i18n pretty_money %} @@ -23,9 +23,9 @@ SPDX-License-Identifier: GPL-3.0-or-later let d1 = document.getElementById("consumer"); let d2 = document.getElementById("creditor"); if (con) { d1.textContent = {{ big_consumer | safe }}[0] + " " + gettext("with") + " " + {{ big_consumer | safe}}[1] + "€";} - else { d1.textContent = gettext("Infortunately, you doesn't have consumer this year");}; + else { d1.textContent = gettext({% trans "Infortunately, you doesn't have consumer this year" %});}; if (cre) { d2.textContent = {{ big_creancier | safe}}[0] + " " + gettext("with") + " " + {{ big_creancier | safe}}[1] + "€";} - else { d2.textContent = gettext("Congratulations you are a real rat !"); }; + else { d2.textContent = gettext({% trans "Congratulations you are a real rat !" %}); }; {% endblock %} diff --git a/apps/wrapped/templates/wrapped/wrapped_list.html b/apps/wrapped/templates/wrapped/wrapped_list.html index 28892de5..c15d4ada 100644 --- a/apps/wrapped/templates/wrapped/wrapped_list.html +++ b/apps/wrapped/templates/wrapped/wrapped_list.html @@ -6,17 +6,24 @@ SPDX-License-Identifier: GPL-3.0-or-later {% load i18n %} {% block content %} -

-
-
-
-
{{ title }}
-
-
- {% render_table table %} -
-
-
+
+{% if tables|length > 0 %} +
+

+ {% trans "My wrapped" %} +

+ {% render_table tables.1 %} +
+{% endif %} + +{% if tables|length > 0 %} +
+

+ {% trans "Public wrapped" %} +

+ {% render_table tables.0 %} +
+{% endif %}
{% endblock %} @@ -25,7 +32,7 @@ SPDX-License-Identifier: GPL-3.0-or-later let club_not_public = {{ club_not_public }}; if (club_not_public) { (addMsg("{% trans "Do not forget to ask permission to people who are in your wrapped before to make them public" %}", 'warning'));} function refreshTable() { - $("#wrapped_table").load(location.pathname + " #wrapped_table"); + $("#wrapped_tables").load(location.pathname + " #wrapped_tables"); } function copylink(id) { diff --git a/apps/wrapped/views.py b/apps/wrapped/views.py index 0a16fd92..c787fd96 100644 --- a/apps/wrapped/views.py +++ b/apps/wrapped/views.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later import json @@ -6,7 +6,8 @@ import json from django.contrib.auth.mixins import LoginRequiredMixin from django.utils.translation import gettext_lazy as _ from django.views.generic import DetailView -from django_tables2.views import SingleTableView +from django.views.generic.list import ListView +from django_tables2.views import MultiTableMixin from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin @@ -14,21 +15,29 @@ from .models import Wrapped from .tables import WrappedTable -class WrappedListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): +class WrappedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView): """ Display all Wrapped, and classify by year """ model = Wrapped - table_class = WrappedTable + tables =[ + lambda data: WrappedTable(data, prefix="public-"), + lambda data: WrappedTable(data, prefix="personnal-"), + ] template_name = 'wrapped/wrapped_list.html' extra_context = {'title': _("List of wrapped")} def get_queryset(self, **kwargs): return super().get_queryset(**kwargs).distinct() - def get_table_data(self): - return Wrapped.objects.filter(PermissionBackend.filter_queryset( - self.request, Wrapped, "change", field='public')).distinct().order_by("-bde__date_start") + def get_tables_data(self): + return [ + Wrapped.objects.filter(public=True), + Wrapped.objects + .filter(PermissionBackend.filter_queryset(self.request, Wrapped, "change", field='public')) + .distinct() + .order_by("-bde__date_start") + ] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 79814474..3f4c22d7 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-25 13:47+0100\n" +"POT-Creation-Date: 2025-03-13 21:08+0100\n" "PO-Revision-Date: 2022-04-11 22:05+0200\n" "Last-Translator: bleizi \n" "Language-Team: French \n" @@ -865,7 +865,7 @@ msgstr "Taille maximale : 2 Mo" msgid "This image cannot be loaded." msgstr "Cette image ne peut pas être chargée." -#: apps/member/forms.py:154 apps/member/views.py:103 +#: apps/member/forms.py:154 apps/member/views.py:117 #: apps/registration/forms.py:33 apps/registration/views.py:282 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." @@ -1194,11 +1194,11 @@ msgstr "Adhésion de {user} pour le club {club}" msgid "The role {role} does not apply to the club {club}." msgstr "Le rôle {role} ne s'applique pas au club {club}." -#: apps/member/models.py:388 apps/member/views.py:745 +#: apps/member/models.py:388 apps/member/views.py:759 msgid "User is already a member of the club" msgstr "L'utilisateur·rice est déjà membre du club" -#: apps/member/models.py:400 apps/member/views.py:754 +#: apps/member/models.py:400 apps/member/views.py:768 msgid "User is not a member of the parent club" msgstr "L'utilisateur·rice n'est pas membre du club parent" @@ -1251,7 +1251,7 @@ msgid "Account #" msgstr "Compte n°" #: apps/member/templates/member/base.html:48 -#: apps/member/templates/member/base.html:62 apps/member/views.py:60 +#: apps/member/templates/member/base.html:62 apps/member/views.py:61 #: apps/registration/templates/registration/future_profile_detail.html:48 #: apps/wei/templates/wei/weimembership_form.html:117 msgid "Update Profile" @@ -1312,8 +1312,8 @@ msgstr "" "seront à nouveau possible." #: apps/member/templates/member/club_alias.html:10 -#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:304 -#: apps/member/views.py:545 +#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:318 +#: apps/member/views.py:559 msgid "Note aliases" msgstr "Alias de la note" @@ -1505,51 +1505,51 @@ msgstr "Sauvegarder les changements" msgid "Registrations" msgstr "Inscriptions" -#: apps/member/views.py:73 apps/registration/forms.py:23 +#: apps/member/views.py:74 apps/registration/forms.py:23 msgid "This address must be valid." msgstr "Cette adresse doit être valide." -#: apps/member/views.py:140 +#: apps/member/views.py:154 msgid "Profile detail" msgstr "Détails de l'utilisateur⋅rice" -#: apps/member/views.py:206 +#: apps/member/views.py:220 msgid "Search user" msgstr "Chercher un·e utilisateur·rice" -#: apps/member/views.py:258 +#: apps/member/views.py:272 msgid "Note friendships" msgstr "Amitiés note" -#: apps/member/views.py:328 +#: apps/member/views.py:342 msgid "Update note picture" msgstr "Modifier la photo de la note" -#: apps/member/views.py:377 +#: apps/member/views.py:391 msgid "Manage auth token" msgstr "Gérer les jetons d'authentification" -#: apps/member/views.py:404 +#: apps/member/views.py:418 msgid "Create new club" msgstr "Créer un nouveau club" -#: apps/member/views.py:423 +#: apps/member/views.py:437 msgid "Search club" msgstr "Chercher un club" -#: apps/member/views.py:461 +#: apps/member/views.py:475 msgid "Club detail" msgstr "Détails du club" -#: apps/member/views.py:573 +#: apps/member/views.py:587 msgid "Update club" msgstr "Modifier le club" -#: apps/member/views.py:607 +#: apps/member/views.py:621 msgid "Add new member to the club" msgstr "Ajouter un·e nouvelleau membre au club" -#: apps/member/views.py:736 apps/wei/views.py:991 +#: apps/member/views.py:750 apps/wei/views.py:991 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -1557,19 +1557,19 @@ msgstr "" "Cet⋅te utilisateur⋅rice n'a pas assez d'argent pour rejoindre ce club et ne " "peut pas avoir un solde négatif." -#: apps/member/views.py:758 +#: apps/member/views.py:772 msgid "The membership must start after {:%m-%d-%Y}." msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}." -#: apps/member/views.py:763 +#: apps/member/views.py:777 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." -#: apps/member/views.py:913 +#: apps/member/views.py:927 msgid "Manage roles of an user in the club" msgstr "Gérer les rôles d'un⋅e utilisateur⋅rice dans le club" -#: apps/member/views.py:938 +#: apps/member/views.py:952 msgid "Members of the club" msgstr "Membres du club" @@ -2084,7 +2084,7 @@ msgid "Button displayed" msgstr "Bouton affiché" #: apps/note/templates/note/transactiontemplate_list.html:100 -#: apps/wrapped/templates/wrapped/wrapped_list.html:63 +#: apps/wrapped/templates/wrapped/wrapped_list.html:70 msgid "An error occured" msgstr "Une erreur s'est produite" @@ -3662,6 +3662,14 @@ msgstr "soirée·s organisée·s" msgid "distinct members" msgstr "Membres distinct·e·s" +#: apps/wrapped/templates/wrapped/1/wrapped_view_club.html:26 +msgid "Infortunately, you doesn't have consumer this year" +msgstr "Malheureusement, tu n'as pas de consommateur cette année" + +#: apps/wrapped/templates/wrapped/1/wrapped_view_club.html:28 +msgid "Congratulations you are a real rat !" +msgstr "Félicitations, tu es un vrai rat !" + #: apps/wrapped/templates/wrapped/1/wrapped_view_user.html:13 msgid "You participate to the wei: " msgstr "Tu as participé au wei : " @@ -3699,7 +3707,15 @@ msgstr "avec" msgid "Your expenses to BDE: " msgstr "Tes dépenses au BDE : " -#: apps/wrapped/templates/wrapped/wrapped_list.html:26 +#: apps/wrapped/templates/wrapped/wrapped_list.html:13 +msgid "My wrapped" +msgstr "Mes wrapped" + +#: apps/wrapped/templates/wrapped/wrapped_list.html:22 +msgid "Public wrapped" +msgstr "Wrapped public" + +#: apps/wrapped/templates/wrapped/wrapped_list.html:33 msgid "" "Do not forget to ask permission to people who are in your wrapped before to " "make them public" @@ -3707,19 +3723,19 @@ msgstr "" "N'oublies pas de demander la permission des personnes apparaissant dans un " "wrapped avant de le rendre public" -#: apps/wrapped/templates/wrapped/wrapped_list.html:33 +#: apps/wrapped/templates/wrapped/wrapped_list.html:40 msgid "Link copied" msgstr "Lien copié" -#: apps/wrapped/templates/wrapped/wrapped_list.html:58 +#: apps/wrapped/templates/wrapped/wrapped_list.html:65 msgid "Wrapped is private" msgstr "Le wrapped est privé" -#: apps/wrapped/templates/wrapped/wrapped_list.html:59 +#: apps/wrapped/templates/wrapped/wrapped_list.html:66 msgid "Wrapped is public" msgstr "Le wrapped est public" -#: apps/wrapped/views.py:24 +#: apps/wrapped/views.py:28 msgid "List of wrapped" msgstr "Liste des wrapped" From b293904525b6f9a98349c352f62d7397bb988c32 Mon Sep 17 00:00:00 2001 From: quark Date: Thu, 13 Mar 2025 23:56:10 +0100 Subject: [PATCH 70/84] Another tables and doc --- apps/wrapped/views.py | 6 +- docs/_static/img/graphs/wrapped.svg | 118 ++++++++++++++++++++++++++++ docs/apps/index.rst | 3 + docs/apps/wrapped.rst | 108 +++++++++++++++++++++++++ 4 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 docs/_static/img/graphs/wrapped.svg create mode 100644 docs/apps/wrapped.rst diff --git a/apps/wrapped/views.py b/apps/wrapped/views.py index c787fd96..1300e1bc 100644 --- a/apps/wrapped/views.py +++ b/apps/wrapped/views.py @@ -20,9 +20,9 @@ class WrappedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Display all Wrapped, and classify by year """ model = Wrapped - tables =[ - lambda data: WrappedTable(data, prefix="public-"), - lambda data: WrappedTable(data, prefix="personnal-"), + tables = [ + lambda data: WrappedTable(data, prefix="public-"), + lambda data: WrappedTable(data, prefix="personnal-"), ] template_name = 'wrapped/wrapped_list.html' extra_context = {'title': _("List of wrapped")} diff --git a/docs/_static/img/graphs/wrapped.svg b/docs/_static/img/graphs/wrapped.svg new file mode 100644 index 00000000..e76497dc --- /dev/null +++ b/docs/_static/img/graphs/wrapped.svg @@ -0,0 +1,118 @@ + + + + + + +model_graph + + + +wrapped_models_Bde + + +     +    Bde     +     +id +     +     +AutoField +     +     +date_end +     +     +DateTimeField +     +     +date_start +     +     +DateTimeField +     +     +name +     +     +CharField +     + + + + +wrapped_models_Wrapped + + +     +    Wrapped     +     +id +     +     +AutoField +     +     +bde +     +     +ForeignKey (id) +     +     +note +     +     +ForeignKey (id) +     +     +data_json +     +     +TextField +     +     +generated +     +     +BooleanField +     +     +public +     +     +BooleanField +     + + + + +wrapped_models_Wrapped->wrapped_models_Bde + + + bde (+) + + + +note_models_notes_Note + + +   +Note +   + + + +wrapped_models_Wrapped->note_models_notes_Note + + + note (+) + + + +\n\n\n + + + diff --git a/docs/apps/index.rst b/docs/apps/index.rst index e16f5196..95315eb8 100644 --- a/docs/apps/index.rst +++ b/docs/apps/index.rst @@ -14,6 +14,7 @@ Applications de la Note Kfet 2020 logs treasury wei + wrapped La Note Kfet 2020 est un projet Django, décomposé en applications. Certaines applications sont développées uniquement pour ce projet, et sont indispensables, @@ -69,4 +70,6 @@ Applications facultatives Interface de gestion pour les trésorièr⋅es, émission de factures, remises de chèque, statistiques... * `WEI `_ : Interface de gestion du WEI. +* `Wrapped `_ : + Récapitulatif personnalisé annuel de statitiques globales et personnelles. diff --git a/docs/apps/wrapped.rst b/docs/apps/wrapped.rst new file mode 100644 index 00000000..199572dc --- /dev/null +++ b/docs/apps/wrapped.rst @@ -0,0 +1,108 @@ +Wrapped +======= + +Cette application montre les statistiques annuelles des utilisateur·ice·s et/ou des clubs. + +Modèles +------- + +Bde +~~~ + +Le modèle ``Bde`` contient des informations relatifs à un BDE : + +* ``name`` : ``CharField``, nom du BDE. +* ``date_start`` : ``DateField``, date de prise de fonction du bureau BDE considéré. +* ``date_end`` : ``DateField``, date de démission du bureau BDE considéré. + +Wrapped +~~~~~~~ + +Contient les informations sur un wrapped : + +* ``generated`` : ``BooleanField``, indique si le wrapped a été généré ou non. +* ``public`` : ``BooleanField``, indique si le wrapped est visible de tous les utilisateur·ice·s ou non. +* ``bde`` : ``ForeignKey(Bde)``, BDE auquel le wrapped correspond. +* ``note`` : ``ForeignKey(Note)``, note à laquelle le wrapped correspond. +* ``data_json`` : ``TextField``, diverses statistique concernant les notes durant le mandat BDE + considéré ou sur la NoteKfet dans sa globalité. + +Graphe des modèles +~~~~~~~~~~~~~~~~~~ + +.. image:: ../_static/img/graphs/wrapped.svg + :width: 960 + :alt: Graphe des modèles de l'application Wrapped + +Fonctionnement +-------------- + +Création d'un BDE +~~~~~~~~~~~~~~~~~ + +Seul un⋅e respo info peut créer un BDE. Pour cela, se rendre dans l'onglet « Admin »., puis « BDE » et +enfin « + Ajouter BDE ». Iel doit renseigner, les dates de début et de fin du bureau BDE ainsi que le +nom de la liste. + +Génération des wrappeds +~~~~~~~~~~~~~~~~~~~~~~~ + +Seul un·e respo info peut générer des wrappeds. Pour une utilisation annuelle classique, iel exécute la +commande : + +``./manage.py generate_wrapped -b "bde_name" -u adh -c active`` + +Pour une utilisation plus technique de cette commande se référer à sa documentation + +``./manage.py help generate_wrapped`` + +Le script prend une dizaine de minutes pour générer tous les wrappeds. + +Créer ses propres wrappeds +-------------------------- + +Cette section est plus technique et s'addresse plutôt à des respos infos en cours de mandat qui voudrai +faire les wrappeds de leur propre BDE. + +Contenu +~~~~~~~ + +Il est fortement conseillé de bien réfléchir à ce que l'on souhaite mettre sur un wrapped, plusieurs +critères sont à prendre compte : + +* compréhension, est-ce que la donnée fait sens auprès des utilisateur·ice·s. +* pertinence, est-ce que la donnée fonctionne pour un grand nombre d'utilisateur. +* faisabilité, est-ce que le temps de calcul est suffisament rapide. +* complexité, est-ce que c'est trop compliqué à coder. + +Script +~~~~~~ + +Le script *generate_wrapped* fonctionne de la manière suivante : + +* ``convert_to_note`` : en fonction des arguments d'entrée, il récupére toutes les notes dont le·s + wrapped·s va/vont être généré·s + ou regénéré·s. +* ``global_data`` : le script génére ensuite des statistiques globales qui concernent pas qu'une seule + note (nombre de soirée, classement, etc). +* ``unique_data`` : le script génére les statitiques uniques à chaque note, et rajoute des données + globales si nécessaire, pour chaque note on souhaite avoir un json avec toutes les données qui + seront dans le wrapped. +* ``make_wrapped`` : enfin, le cas échéant, pour chaque bde, et pour chaque note, le wrapped est crée + ou modifié, et enregistré, s'il est crée il est par défault non public. + +Seules les fonctions ``global_data`` et ``unique_data`` sont à modifier, pour implementer un nouveau +BDE. + +Template +~~~~~~~~ + +Il y a au moins deux templates a écrire pour chaque bde : + +* ``templates/wrapped/{bde_id}/wrapped_view_club.html``: le template pour les wrappeds des clubs +* ``templates/wrapped/{bde_id}/wrapped_view_user.html``: le template pour les wrappeds des + utilisateur·ice·s + +Il est conseillé de suivre la même arborescence pour les fichiers statics (fonts personnalisées, +images, css, etc). De même, il est conseillé de créé un fichier +``templates/wrapped/{bde_id}/wrapped_base.html`` et d'étendre cette template. From 25bfa575edce6faab7375634181f4efd2cd086d8 Mon Sep 17 00:00:00 2001 From: quark Date: Thu, 13 Mar 2025 23:56:10 +0100 Subject: [PATCH 71/84] Another tables and doc --- .../wrapped/1/wrapped_view_club.html | 4 +- apps/wrapped/views.py | 6 +- docs/_static/img/graphs/wrapped.svg | 118 ++++++++++++++++++ docs/apps/index.rst | 3 + docs/apps/wrapped.rst | 108 ++++++++++++++++ 5 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 docs/_static/img/graphs/wrapped.svg create mode 100644 docs/apps/wrapped.rst diff --git a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html index 6d295e9e..35eca18e 100644 --- a/apps/wrapped/templates/wrapped/1/wrapped_view_club.html +++ b/apps/wrapped/templates/wrapped/1/wrapped_view_club.html @@ -23,9 +23,9 @@ SPDX-License-Identifier: GPL-3.0-or-later let d1 = document.getElementById("consumer"); let d2 = document.getElementById("creditor"); if (con) { d1.textContent = {{ big_consumer | safe }}[0] + " " + gettext("with") + " " + {{ big_consumer | safe}}[1] + "€";} - else { d1.textContent = gettext({% trans "Infortunately, you doesn't have consumer this year" %});}; + else { d1.textContent = gettext("{% trans "Infortunately, you doesn't have consumer this year" %}");}; if (cre) { d2.textContent = {{ big_creancier | safe}}[0] + " " + gettext("with") + " " + {{ big_creancier | safe}}[1] + "€";} - else { d2.textContent = gettext({% trans "Congratulations you are a real rat !" %}); }; + else { d2.textContent = gettext("{% trans "Congratulations you are a real rat !" %}"); }; {% endblock %} diff --git a/apps/wrapped/views.py b/apps/wrapped/views.py index c787fd96..1300e1bc 100644 --- a/apps/wrapped/views.py +++ b/apps/wrapped/views.py @@ -20,9 +20,9 @@ class WrappedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Display all Wrapped, and classify by year """ model = Wrapped - tables =[ - lambda data: WrappedTable(data, prefix="public-"), - lambda data: WrappedTable(data, prefix="personnal-"), + tables = [ + lambda data: WrappedTable(data, prefix="public-"), + lambda data: WrappedTable(data, prefix="personnal-"), ] template_name = 'wrapped/wrapped_list.html' extra_context = {'title': _("List of wrapped")} diff --git a/docs/_static/img/graphs/wrapped.svg b/docs/_static/img/graphs/wrapped.svg new file mode 100644 index 00000000..e76497dc --- /dev/null +++ b/docs/_static/img/graphs/wrapped.svg @@ -0,0 +1,118 @@ + + + + + + +model_graph + + + +wrapped_models_Bde + + +     +    Bde     +     +id +     +     +AutoField +     +     +date_end +     +     +DateTimeField +     +     +date_start +     +     +DateTimeField +     +     +name +     +     +CharField +     + + + + +wrapped_models_Wrapped + + +     +    Wrapped     +     +id +     +     +AutoField +     +     +bde +     +     +ForeignKey (id) +     +     +note +     +     +ForeignKey (id) +     +     +data_json +     +     +TextField +     +     +generated +     +     +BooleanField +     +     +public +     +     +BooleanField +     + + + + +wrapped_models_Wrapped->wrapped_models_Bde + + + bde (+) + + + +note_models_notes_Note + + +   +Note +   + + + +wrapped_models_Wrapped->note_models_notes_Note + + + note (+) + + + +\n\n\n + + + diff --git a/docs/apps/index.rst b/docs/apps/index.rst index e16f5196..95315eb8 100644 --- a/docs/apps/index.rst +++ b/docs/apps/index.rst @@ -14,6 +14,7 @@ Applications de la Note Kfet 2020 logs treasury wei + wrapped La Note Kfet 2020 est un projet Django, décomposé en applications. Certaines applications sont développées uniquement pour ce projet, et sont indispensables, @@ -69,4 +70,6 @@ Applications facultatives Interface de gestion pour les trésorièr⋅es, émission de factures, remises de chèque, statistiques... * `WEI `_ : Interface de gestion du WEI. +* `Wrapped `_ : + Récapitulatif personnalisé annuel de statitiques globales et personnelles. diff --git a/docs/apps/wrapped.rst b/docs/apps/wrapped.rst new file mode 100644 index 00000000..199572dc --- /dev/null +++ b/docs/apps/wrapped.rst @@ -0,0 +1,108 @@ +Wrapped +======= + +Cette application montre les statistiques annuelles des utilisateur·ice·s et/ou des clubs. + +Modèles +------- + +Bde +~~~ + +Le modèle ``Bde`` contient des informations relatifs à un BDE : + +* ``name`` : ``CharField``, nom du BDE. +* ``date_start`` : ``DateField``, date de prise de fonction du bureau BDE considéré. +* ``date_end`` : ``DateField``, date de démission du bureau BDE considéré. + +Wrapped +~~~~~~~ + +Contient les informations sur un wrapped : + +* ``generated`` : ``BooleanField``, indique si le wrapped a été généré ou non. +* ``public`` : ``BooleanField``, indique si le wrapped est visible de tous les utilisateur·ice·s ou non. +* ``bde`` : ``ForeignKey(Bde)``, BDE auquel le wrapped correspond. +* ``note`` : ``ForeignKey(Note)``, note à laquelle le wrapped correspond. +* ``data_json`` : ``TextField``, diverses statistique concernant les notes durant le mandat BDE + considéré ou sur la NoteKfet dans sa globalité. + +Graphe des modèles +~~~~~~~~~~~~~~~~~~ + +.. image:: ../_static/img/graphs/wrapped.svg + :width: 960 + :alt: Graphe des modèles de l'application Wrapped + +Fonctionnement +-------------- + +Création d'un BDE +~~~~~~~~~~~~~~~~~ + +Seul un⋅e respo info peut créer un BDE. Pour cela, se rendre dans l'onglet « Admin »., puis « BDE » et +enfin « + Ajouter BDE ». Iel doit renseigner, les dates de début et de fin du bureau BDE ainsi que le +nom de la liste. + +Génération des wrappeds +~~~~~~~~~~~~~~~~~~~~~~~ + +Seul un·e respo info peut générer des wrappeds. Pour une utilisation annuelle classique, iel exécute la +commande : + +``./manage.py generate_wrapped -b "bde_name" -u adh -c active`` + +Pour une utilisation plus technique de cette commande se référer à sa documentation + +``./manage.py help generate_wrapped`` + +Le script prend une dizaine de minutes pour générer tous les wrappeds. + +Créer ses propres wrappeds +-------------------------- + +Cette section est plus technique et s'addresse plutôt à des respos infos en cours de mandat qui voudrai +faire les wrappeds de leur propre BDE. + +Contenu +~~~~~~~ + +Il est fortement conseillé de bien réfléchir à ce que l'on souhaite mettre sur un wrapped, plusieurs +critères sont à prendre compte : + +* compréhension, est-ce que la donnée fait sens auprès des utilisateur·ice·s. +* pertinence, est-ce que la donnée fonctionne pour un grand nombre d'utilisateur. +* faisabilité, est-ce que le temps de calcul est suffisament rapide. +* complexité, est-ce que c'est trop compliqué à coder. + +Script +~~~~~~ + +Le script *generate_wrapped* fonctionne de la manière suivante : + +* ``convert_to_note`` : en fonction des arguments d'entrée, il récupére toutes les notes dont le·s + wrapped·s va/vont être généré·s + ou regénéré·s. +* ``global_data`` : le script génére ensuite des statistiques globales qui concernent pas qu'une seule + note (nombre de soirée, classement, etc). +* ``unique_data`` : le script génére les statitiques uniques à chaque note, et rajoute des données + globales si nécessaire, pour chaque note on souhaite avoir un json avec toutes les données qui + seront dans le wrapped. +* ``make_wrapped`` : enfin, le cas échéant, pour chaque bde, et pour chaque note, le wrapped est crée + ou modifié, et enregistré, s'il est crée il est par défault non public. + +Seules les fonctions ``global_data`` et ``unique_data`` sont à modifier, pour implementer un nouveau +BDE. + +Template +~~~~~~~~ + +Il y a au moins deux templates a écrire pour chaque bde : + +* ``templates/wrapped/{bde_id}/wrapped_view_club.html``: le template pour les wrappeds des clubs +* ``templates/wrapped/{bde_id}/wrapped_view_user.html``: le template pour les wrappeds des + utilisateur·ice·s + +Il est conseillé de suivre la même arborescence pour les fichiers statics (fonts personnalisées, +images, css, etc). De même, il est conseillé de créé un fichier +``templates/wrapped/{bde_id}/wrapped_base.html`` et d'étendre cette template. From 7966d6f3977cedcb4cb8b2e81c234b965d08da0e Mon Sep 17 00:00:00 2001 From: thomasl Date: Mon, 17 Mar 2025 13:15:07 +0100 Subject: [PATCH 72/84] Update file initial.json --- apps/permission/fixtures/initial.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 879bc1d4..dc2ca4c0 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -324,7 +324,7 @@ "mask": 2, "field": "", "permanent": false, - "description": "Créer une transaction de ou vers la note d'un club" + "description": "Créer une transaction de ou vers la note d'un club tant que la source reste au dessus de -20 €" } }, { @@ -3815,7 +3815,7 @@ "mask": 2, "field": "", "permanent": false, - "description": "Créer une transaction vers la note d'un club" + "description": "Créer une transaction vers la note d'un club tant que la source reste au dessus de -20 €" } }, { From 8da62e62fb384bf89a2dc745ef8f076fee68f4a9 Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 18 Mar 2025 15:53:02 +0100 Subject: [PATCH 73/84] Rewrite script and add test --- .../management/commands/generate_wrapped.py | 144 +++++++++--------- apps/wrapped/tests/__init__.py | 0 apps/wrapped/tests/test_wrapped.py | 91 +++++++++++ 3 files changed, 159 insertions(+), 76 deletions(-) create mode 100644 apps/wrapped/tests/__init__.py create mode 100644 apps/wrapped/tests/test_wrapped.py diff --git a/apps/wrapped/management/commands/generate_wrapped.py b/apps/wrapped/management/commands/generate_wrapped.py index 9e5c19d2..8bf08945 100644 --- a/apps/wrapped/management/commands/generate_wrapped.py +++ b/apps/wrapped/management/commands/generate_wrapped.py @@ -38,7 +38,7 @@ class Command(BaseCommand): required=False, help="""User will have their(s) wrapped generated, all = all users - adh = all users who have a valid memberships to BDE during the BDE considered + adh = all users who have a valid cd memberships to BDE during the BDE considered supersuser = all superusers custom user1,user2,... = a list of username, custom_id id1,id2,... = a list of user id""", @@ -70,15 +70,7 @@ class Command(BaseCommand): dest='create', ) - def handle(self, *args, **options): - # useful string for output - red = '\033[31;1m' - yellow = '\033[33;1m' - green = '\033[32;1m' - abort = red + 'ABORT' - warning = yellow + 'WARNING' - success = green + 'SUCCESS' - + def handle(self, *args, **options): # NOQA # Traitement des paramètres verb = options['verbosity'] bde = [] @@ -89,11 +81,11 @@ class Command(BaseCommand): if options['bde_id']: if bde: if verb >= 1: - print(warning) - print(yellow + 'You already defined bde with their name !') + self.stdout.write(self.style.WARNING( + "WARNING\nYou already defined bde with their name !")) if verb >= 0: - print(abort) - return + self.stdout.write(self.style.ERROR("ABORT")) + exit(1) bde_id = options['bde_id'].split(',') bde = [Bde.objects.get(pk=i) for i in bde_id] @@ -113,11 +105,11 @@ class Command(BaseCommand): user = ['custom_id', [User.objects.get(pk=u) for u in user_id]] else: if verb >= 1: - print(warning) - print(yellow + 'You user option is not recognized') + self.sdtout.write(self.style.WARNING( + "WARNING\nYou user option is not recognized")) if verb >= 0: - print(abort) - return + self.stdout.write(self.style.ERROR("ABORT")) + exit(1) club = [] if options['club']: @@ -133,11 +125,11 @@ class Command(BaseCommand): club = ['custom_id', [Club.objects.get(pk=c) for c in club_id]] else: if verb >= 1: - print(warning) - print(yellow + 'You club option is not recognized') + self.stdout.write(self.style.WARNING( + "WARNING\nYou club option is not recognized")) if verb >= 0: - print(abort) - return + self.stdout.write(self.style.ERROR("ABORT")) + exit(1) change = options['change'] create = options['create'] @@ -145,72 +137,75 @@ class Command(BaseCommand): # check if parameters are sufficient for generate wrapped with the desired option if not bde: if verb >= 1: - print(warning) - print(yellow + 'You have not selectionned a BDE !') + self.stdout.write(self.style.WARNING( + "WARNING\nYou have not selectionned a BDE !")) if verb >= 0: - print(abort) - return + self.stdout.write(self.style.ERROR("ABORT")) + exit(1) if not (user or club): if verb >= 1: - print(warning) - print(yellow + 'No club or user selected !') + self.stdout.write(self.style.WARNING( + "WARNING\nNo club or user selected !")) if verb >= 0: - print(abort) - return + self.stdout.write(self.style.ERROR("ABORT")) + exit(1) if verb >= 3: - print('\033[1mOptions:\033[m') + self.stdout.write("Options:") bde_str = '' for b in bde: - bde_str += str(b) - print('BDE: ' + bde_str) + bde_str += str(b) + '\n' + self.stdout.write("BDE: " + bde_str) if user: - print('User: ' + user[0]) + self.stdout.write('User: ' + user[0]) if club: - print('Club: ' + club[0]) - print('change: ' + str(change)) - print('create: ' + str(create)) - print('') + self.stdout.write('Club: ' + club[0]) + self.stdout.write('change: ' + str(change)) + self.stdout.write('create: ' + str(create) + '\n') if not (change or create): if verb >= 1: - print(warning) - print(yellow + 'change and create is set to false, none wrapped will be created') + self.stdout.write(self.style.WARNING( + "WARNING\nchange and create is set to false, none wrapped will be created")) if verb >= 0: - print(abort) - return + self.stdout.write(self.style.ERROR("ABORT")) + exit(1) if verb >= 1 and change: - print(warning) - print(yellow + 'change is set to true, some wrapped may be replaced !') + self.stdout.write(self.style.WARNING( + "WARNING\nchange is set to true, some wrapped may be replaced !")) if verb >= 1 and not create: - print(warning) - print(yellow + 'create is set to false, wrapped will not be created !') + self.stdout.write(self.style.WARNING( + "WARNING\ncreate is set to false, wrapped will not be created !")) if verb >= 3 or change or not create: a = str(input('\033[mContinue ? (y/n) ')).lower() if a in ['n', 'no', 'non', '0']: if verb >= 0: - print(abort) - return + self.stdout.write(self.style.ERROR("ABORT")) + exit(1) note = self.convert_to_note(change, create, bde=bde, user=user, club=club, verb=verb) if verb >= 1: - print("\033[32mUser and/or Club given has successfully convert in their note\033[m") + self.stdout.write(self.style.SUCCESS( + "User and/or Club given has successfully convert in their note")) + global_data = self.global_data(bde, verb=verb) if verb >= 1: - print("\033[32mGlobal data has been successfully generated\033[m") + self.stdout.write(self.style.SUCCESS( + "Global data has been successfully generated")) unique_data = self.unique_data(bde, note, global_data=global_data, verb=verb) if verb >= 1: - print("\033[32mUnique data has been successfully generated\033[m") + self.stdout.write(self.style.SUCCESS( + "Unique data has been successfully generated")) self.make_wrapped(unique_data, note, bde, change, create, verb=verb) if verb >= 1: - print(green + "The wrapped has been generated !") + self.stdout.write(self.style.SUCCESS( + "The wrapped has been generated !")) if verb >= 0: - print(success) + self.stdout.write(self.style.SUCCESS("SUCCESS")) + exit(0) - return - - def convert_to_note(self, change, create, bde=None, user=None, club=None, verb=1): + def convert_to_note(self, change, create, bde=None, user=None, club=None, verb=1): # NOQA notes = [] for b in bde: note_for_bde = Note.objects.filter(pk__lte=-1) @@ -253,17 +248,17 @@ class Command(BaseCommand): note_for_bde = self.filter_note(b, note_for_bde, change, create, verb=verb) notes.append(note_for_bde) if verb >= 2: - print("\033[m{nb} note selectionned for bde {bde}".format(nb=len(note_for_bde), bde=b.name)) + self.stdout.write(f"{len(note_for_bde)} note selectionned for bde {b.name}") return notes - def global_data(self, bde, verb=1): + def global_data(self, bde, verb=1): # NOQA data = {} for b in bde: if b.name == 'Rave Part[list]': if verb >= 2: - print("Begin to make global data") + self.stdout.write("Begin to make global data") if verb >= 3: - print('nb_transaction') + self.stdout.write("nb_transaction") # nb total de transactions data['nb_transaction'] = Transaction.objects.filter( created_at__gte=b.date_start, @@ -271,7 +266,7 @@ class Command(BaseCommand): valid=True).count() if verb >= 3: - print('nb_vieux_con') + self.stdout.write("nb_vieux_con") # nb total de vielleux con·ne·s derrière le bar button_id = [2884, 2585] transactions = Transaction.objects.filter( @@ -286,7 +281,7 @@ class Command(BaseCommand): data['nb_vieux_con'] = q if verb >= 3: - print('nb_soiree') + self.stdout.write("nb_soiree") # nb total de soirée a_type_id = [1, 2, 4, 5, 7, 10] data['nb_soiree'] = Activity.objects.filter( @@ -296,7 +291,7 @@ class Command(BaseCommand): activity_type__pk__in=a_type_id).count() if verb >= 3: - print('pots, nb_entree_pot') + self.stdout.write('pots, nb_entree_pot') # nb d'entrée totale aux pots pot_id = [1, 4, 10] pots = Activity.objects.filter( @@ -310,7 +305,7 @@ class Command(BaseCommand): data['nb_entree_pot'] += Entry.objects.filter(activity=pot).count() if verb >= 3: - print('top3_buttons') + self.stdout.write('top3_buttons') # top 3 des boutons les plus cliqués transactions = Transaction.objects.filter( created_at__gte=b.date_start, @@ -329,7 +324,7 @@ class Command(BaseCommand): data['top3_buttons'] = list(sorted(d.items(), key=lambda item: item[1], reverse=True))[:3] if verb >= 3: - print('class_conso_all') + self.stdout.write('class_conso_all') # le classement des plus gros consommateurs (BDE + club) transactions = Transaction.objects.filter( created_at__gte=b.date_start, @@ -348,7 +343,7 @@ class Command(BaseCommand): data['class_conso_all'] = dict(sorted(d.items(), key=lambda item: item[1], reverse=True)) if verb >= 3: - print('class_conso_bde') + self.stdout.write('class_conso_bde') # le classement des plus gros consommateurs BDE transactions = Transaction.objects.filter( created_at__gte=b.date_start, @@ -368,11 +363,10 @@ class Command(BaseCommand): else: # make your wrapped or reuse previous wrapped - raise NotImplementedError("The BDE: {bde_name} has not personalized wrapped, make it !" - .format(bde_name=b.name)) + raise NotImplementedError(f"The BDE: {b.name} has not personalized wrapped, make it !") return data - def unique_data(self, bde, note, global_data=None, verb=1): + def unique_data(self, bde, note, global_data=None, verb=1): # NOQA data = [] for i in range(len(bde)): data_bde = [] @@ -380,8 +374,7 @@ class Command(BaseCommand): if verb >= 3: total = len(note[i]) current = 0 - print('Make {nb} data for wrapped sponsored by {bde}' - .format(nb=total, bde=bde[i].name)) + self.stdout.write(f"Make {total} data for wrapped sponsored by {bde[i].name}") for n in note[i]: d = {} if 'user' in n.__dir__(): @@ -542,12 +535,11 @@ class Command(BaseCommand): data_bde.append(json.dumps(d)) if verb >= 3: current += 1 - print('\033[2K' + '({c}/{t})'.format(c=current, t=total) + '\033[1A') + self.stdout.write("\033[2K" + f"({current}/{total})" + "\033[1A") else: # make your wrapped or reuse previous wrapped - raise NotImplementedError("The BDE: {bde_name} has not personalized wrapped, make it !" - .format(bde_name=bde[i].name)) + raise NotImplementedError(f"The BDE: {bde[i].name} has not personalized wrapped, make it !") data.append(data_bde) return data @@ -557,7 +549,7 @@ class Command(BaseCommand): total = 0 for n in note: total += len(n) - print('\033[mMake {nb} wrapped'.format(nb=total)) + self.stdout.write(f"Make {total} wrapped") for i in range(len(bde)): for j in range(len(note[i])): if create and not Wrapped.objects.filter(bde=bde[i], note=note[i][j]): @@ -572,7 +564,7 @@ class Command(BaseCommand): w.save() if verb >= 3: current += 1 - print('\033[2K' + '({c}/{t})'.format(c=current, t=total) + '\033[1A') + self.stdout.write("\033[2K" + f"({current}/{total})" + "\033[1A") return def filter_note(self, bde, note, change, create, verb=1): diff --git a/apps/wrapped/tests/__init__.py b/apps/wrapped/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/wrapped/tests/test_wrapped.py b/apps/wrapped/tests/test_wrapped.py new file mode 100644 index 00000000..663bb39b --- /dev/null +++ b/apps/wrapped/tests/test_wrapped.py @@ -0,0 +1,91 @@ +# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from datetime import timedelta + +from api.tests import TestAPI +from django.contrib.auth.models import User +from django.test import TestCase +from django.urls import reverse +from django.utils import timezone + +from ..api.views import WrappedViewSet, BdeViewSet +from ..models import Bde, Wrapped + + +class TestWrapped(TestCase): + """ + Test activities + """ + fixtures = ('initial',) + + def setUp(self): + self.user = User.objects.create_superuser( + username="admintoto", + password="tototototo", + email="toto@example.com" + ) + self.client.force_login(self.user) + + sess = self.client.session + sess["permission_mask"] = 42 + sess.save() + + self.bde = Bde.objects.create( + name="The best BDE", + date_start=timezone.now() - timedelta(days=365), + date_end=timezone.now(), + ) + + self.wrapped = Wrapped.objects.create( + generated=True, + public=False, + bde=self.bde, + note=self.user.note, + data_json="{}", + ) + + def test_wrapped_list(self): + """ + Display the list of all wrapped + """ + response = self.client.get(reverse("wrapped:wrapped_list")) + self.assertEqual(response.status_code, 200) + + def test_wrapped_detail(self): + """ + Display the detail of an wrapped + """ + response = self.client.get(reverse("wrapped:wrapped_detail", args=(self.wrapped.pk,))) + self.assertEqual(response.status_code, 200) + + +class TestWrappedAPI(TestAPI): + def setUp(self) -> None: + super().setUp() + + self.bde = Bde.objects.create( + name="The best BDE", + date_start=timezone.now() - timedelta(days=365), + date_end=timezone.now(), + ) + + self.wrapped = Wrapped.objects.create( + generated=True, + public=False, + bde=self.bde, + note=self.user.note, + data_json="{}", + ) + + def test_bde_api(self): + """ + Load Bde API page and test all filters and permissions + """ + self.check_viewset(BdeViewSet, "/api/wrapped/bde/") + + def test_wrapped_api(self): + """ + Load Wrapped API page and test all filters and permissions + """ + self.check_viewset(WrappedViewSet, "/api/wrapped/wrapped/") From 9bffb32a5e228f7c22a18feaf059140c9a32cab8 Mon Sep 17 00:00:00 2001 From: bleizi Date: Thu, 20 Mar 2025 17:36:38 +0100 Subject: [PATCH 74/84] documentation --- README.md | 2 +- docs/external_services/oauth2.rst | 13 +++++++++++++ docs/install.rst | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 02d589f2..4ba19356 100644 --- a/README.md +++ b/README.md @@ -240,7 +240,7 @@ Pour activer le support d'OpenID Connect, il faut générer une clé privée, pa exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner son emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`). -7. *Enjoy \o/* +8. *Enjoy \o/* ### Installation avec Docker diff --git a/docs/external_services/oauth2.rst b/docs/external_services/oauth2.rst index 76dead8b..252789aa 100644 --- a/docs/external_services/oauth2.rst +++ b/docs/external_services/oauth2.rst @@ -43,6 +43,11 @@ On a ensuite besoin de définir nos propres scopes afin d'avoir des permissions 'SCOPES_BACKEND_CLASS': 'permission.scopes.PermissionScopes', 'OAUTH2_VALIDATOR_CLASS': "permission.scopes.PermissionOAuth2Validator", 'REFRESH_TOKEN_EXPIRE_SECONDS': timedelta(days=14), + 'PKCE_REQUIRED': False, + 'OIDC_ENABLED': True, + 'OIDC_RSA_PRIVATE_KEY': + os.getenv('OIDC_RSA_PRIVATE_KEY', '/var/secrets/oidc.key'), + 'SCOPES': { 'openid': "OpenID Connect scope" }, } Cela a pour effet d'avoir des scopes sous la forme ``PERMISSION_CLUB``, @@ -57,6 +62,14 @@ On ajoute enfin les routes dans ``urls.py`` : path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')) ) +Enfin pour utiliser OIDC, il faut générer une clé privé que l'on va, par défaut, +mettre dans `/var/secrets/oidc.key` : + +.. code:: bash + + cd /var/secrets/ + openssl genrsa -out oidc.key 4096 + L'OAuth2 est désormais prêt à être utilisé. diff --git a/docs/install.rst b/docs/install.rst index 4edf3bb0..2d92be0e 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -227,6 +227,22 @@ En production, ce fichier contient : ) +Génération d'une clé privé pour OIDC +------------------------------------ + +Pour pouvoir proposer le service de connexion Openid Connect (OIDC) par OAuth2, il y a +besoin d'une clé privé. Par défaut, elle est cherché dans le fichier `/var/secrets/oidc.key` +(sinon, il faut modifier l'emplacement dans les fichiers de configurations). + +Pour générer la clé, il faut aller dans le dossier `/var/secrets` (à créer, si nécessaire) puis +utiliser la commande de génération : + +.. code:: bash + + cd /var/secrets + openssl genrsa -out oidc.key 4096 + + Configuration des tâches récurrentes ------------------------------------ From 93aed87265247826590c167b5b57756af58591ad Mon Sep 17 00:00:00 2001 From: Alexis Mercier des Rochettes Date: Mon, 24 Mar 2025 17:34:06 +0100 Subject: [PATCH 75/84] bootstrap: fix minor issues with profile picture cropping * Add required [1] "display: block;" style property to img element * Fix image overflow in modal. As cropper size inherits from img's parent element [2] (including padding according to my research), we need to wrap modal body into another div that has the padding we want. * Remove ability [3] to click away to dismiss the modal as it often interfered with user interaction when cropping. [1] https://github.com/fengyuanchen/cropperjs/tree/v1?tab=readme-ov-file#example [2] https://github.com/fengyuanchen/cropperjs/tree/v1?tab=readme-ov-file#notes [3] https://getbootstrap.com/docs/4.0/components/modal/#options Signed-off-by: Alexis Mercier des Rochettes --- apps/member/templates/member/picture_update.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/member/templates/member/picture_update.html b/apps/member/templates/member/picture_update.html index 60707dcb..dc21cad5 100644 --- a/apps/member/templates/member/picture_update.html +++ b/apps/member/templates/member/picture_update.html @@ -20,12 +20,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
-