From c6e3b54f94799f3ce592ffaa0fb807e25e6ca70e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 13 Sep 2021 20:27:57 +0200 Subject: [PATCH 01/20] Use longtable for better tables for WEI Signed-off-by: Yohann D'ANELLO --- apps/wei/templates/wei/weilist_sample.tex | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/wei/templates/wei/weilist_sample.tex b/apps/wei/templates/wei/weilist_sample.tex index f1b7fbd4..820df64d 100644 --- a/apps/wei/templates/wei/weilist_sample.tex +++ b/apps/wei/templates/wei/weilist_sample.tex @@ -2,6 +2,7 @@ \usepackage{fontspec} \usepackage[margin=1.5cm]{geometry} +\usepackage{longtable} \begin{document} \begin{center} @@ -19,7 +20,7 @@ \begin{center} \footnotesize -\begin{tabular}{ccccccccc} +\begin{longtable}{ccccccccc} \textbf{Nom} & \textbf{Prénom} & \textbf{Date de naissance} & \textbf{Genre} & \textbf{Section} & \textbf{Bus} & \textbf{Équipe} & \textbf{Rôles} \\ {% for membership in memberships %} @@ -27,20 +28,20 @@ & {{ membership.registration.get_gender_display|safe }} & {{ membership.user.profile.section_generated|safe }} & {{ membership.bus.name|safe }} & {% if membership.team %}{{ membership.team.name|safe }}{% else %}--{% endif %} & {{ membership.roles.first|safe }} \\ {% endfor %} -\end{tabular} +\end{longtable} \end{center} \footnotesize Section = Année à l'ENS + code du département \begin{center} -\begin{tabular}{ccccccccc} +\begin{longtable}{ccccccccc} \textbf{Code} & A0 & A1 & A2 & A'2 & A''2 & A3 & B1234 & B1 \\ \textbf{Département} & Informatique & Maths & Physique & Physique appliquée & Chimie & Biologie & SAPHIRE & Mécanique \\ \hline \textbf{Code} & B2 & B3 & B4 & C & D2 & D3 & E & EXT \\ \textbf{Département} & Génie civil & Génie mécanique & EEA & Design & Éco-gestion & Sciences sociales & Anglais & Extérieur -\end{tabular} +\end{longtable} \end{center} \end{document} From e68afc7d0aa46d8754d78b0b11a6e8cfcef0cbca Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 13 Sep 2021 21:06:44 +0200 Subject: [PATCH 02/20] [WEI] Fix redirect link Signed-off-by: Yohann D'ANELLO --- apps/wei/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wei/views.py b/apps/wei/views.py index 0f385d94..09fd9d56 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -1218,4 +1218,4 @@ class WEIAttributeBus1ANextView(LoginRequiredMixin, RedirectView): qs = qs.filter(information_json__contains='selected_bus_pk') # not perfect, but works... if qs.exists(): return reverse_lazy('wei:wei_bus_1A', args=(qs.first().pk, )) - return reverse_lazy('wei_1A_list', args=(wei.pk, )) + return reverse_lazy('wei:wei_1A_list', args=(wei.pk, )) From 4c17e2a92b7a2516e5d4bb842ed1d6150937f403 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 13 Sep 2021 23:27:05 +0200 Subject: [PATCH 03/20] Fix wrong banner message Signed-off-by: Yohann D'ANELLO --- locale/fr/LC_MESSAGES/django.po | 119 ++++++++++++++++---------------- note_kfet/templates/base.html | 4 +- 2 files changed, 61 insertions(+), 62 deletions(-) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 7d71955d..19ae9894 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: 2021-09-12 19:30+0200\n" +"POT-Creation-Date: 2021-09-13 23:26+0200\n" "PO-Revision-Date: 2020-11-16 20:02+0000\n" "Last-Translator: Yohann D'ANELLO \n" "Language-Team: French \n" @@ -56,7 +56,7 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." #: apps/note/models/transactions.py:46 apps/note/models/transactions.py:301 #: apps/permission/models.py:330 #: apps/registration/templates/registration/future_profile_detail.html:16 -#: apps/wei/models.py:66 apps/wei/models.py:123 apps/wei/tables.py:283 +#: 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 msgid "name" @@ -91,7 +91,7 @@ msgstr "types d'activité" #: apps/activity/models.py:68 #: apps/activity/templates/activity/includes/activity_info.html:19 #: apps/note/models/transactions.py:81 apps/permission/models.py:110 -#: apps/permission/models.py:189 apps/wei/models.py:77 apps/wei/models.py:134 +#: apps/permission/models.py:189 apps/wei/models.py:78 apps/wei/models.py:142 msgid "description" msgstr "description" @@ -112,7 +112,7 @@ msgstr "type" #: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:305 #: apps/note/models/notes.py:148 apps/treasury/models.py:285 -#: apps/wei/models.py:165 apps/wei/templates/wei/attribute_bus_1A.html:13 +#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13 #: apps/wei/templates/wei/survey.html:15 msgid "user" msgstr "utilisateur" @@ -511,7 +511,7 @@ msgstr "rôles" msgid "fee" msgstr "cotisation" -#: apps/member/apps.py:14 apps/wei/tables.py:227 apps/wei/tables.py:258 +#: apps/member/apps.py:14 apps/wei/tables.py:226 apps/wei/tables.py:257 msgid "member" msgstr "adhérent" @@ -2508,8 +2508,8 @@ msgstr "Liste des crédits de la Société générale" msgid "Manage credits from the Société générale" msgstr "Gérer les crédits de la Société générale" -#: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50 -#: apps/wei/models.py:61 apps/wei/models.py:172 +#: apps/wei/apps.py:10 apps/wei/models.py:50 apps/wei/models.py:51 +#: apps/wei/models.py:62 apps/wei/models.py:180 #: note_kfet/templates/base.html:103 msgid "WEI" msgstr "WEI" @@ -2520,8 +2520,8 @@ msgstr "" "L'utilisateur sélectionné n'est pas validé. Merci de d'abord valider son " "compte." -#: apps/wei/forms/registration.py:59 apps/wei/models.py:118 -#: apps/wei/models.py:315 +#: apps/wei/forms/registration.py:59 apps/wei/models.py:126 +#: apps/wei/models.py:323 msgid "bus" msgstr "bus" @@ -2547,7 +2547,7 @@ msgstr "" "bus ou électron libre)" #: apps/wei/forms/registration.py:75 apps/wei/forms/registration.py:85 -#: apps/wei/models.py:153 +#: apps/wei/models.py:161 msgid "WEI Roles" msgstr "Rôles au WEI" @@ -2563,123 +2563,123 @@ msgstr "Cette équipe n'appartient pas à ce bus." msgid "Choose a word:" msgstr "Choisissez un mot :" -#: apps/wei/models.py:24 apps/wei/templates/wei/base.html:36 +#: apps/wei/models.py:25 apps/wei/templates/wei/base.html:36 msgid "year" msgstr "année" -#: apps/wei/models.py:28 apps/wei/templates/wei/base.html:30 +#: apps/wei/models.py:29 apps/wei/templates/wei/base.html:30 msgid "date start" msgstr "début" -#: apps/wei/models.py:32 apps/wei/templates/wei/base.html:33 +#: apps/wei/models.py:33 apps/wei/templates/wei/base.html:33 msgid "date end" msgstr "fin" -#: apps/wei/models.py:70 apps/wei/tables.py:306 +#: apps/wei/models.py:71 apps/wei/tables.py:305 msgid "seat count in the bus" msgstr "nombre de sièges dans le bus" -#: apps/wei/models.py:82 +#: apps/wei/models.py:83 msgid "survey information" msgstr "informations sur le questionnaire" -#: apps/wei/models.py:83 +#: apps/wei/models.py:84 msgid "Information about the survey for new members, encoded in JSON" msgstr "" "Informations sur le sondage pour les nouveaux membres, encodées en JSON" -#: apps/wei/models.py:105 +#: apps/wei/models.py:113 msgid "Bus" msgstr "Bus" -#: apps/wei/models.py:106 apps/wei/templates/wei/weiclub_detail.html:51 +#: apps/wei/models.py:114 apps/wei/templates/wei/weiclub_detail.html:51 msgid "Buses" msgstr "Bus" -#: apps/wei/models.py:127 +#: apps/wei/models.py:135 msgid "color" msgstr "couleur" -#: apps/wei/models.py:128 +#: apps/wei/models.py:136 msgid "The color of the T-Shirt, stored with its number equivalent" msgstr "" "La couleur du T-Shirt, stocké sous la forme de son équivalent numérique" -#: apps/wei/models.py:142 +#: apps/wei/models.py:150 msgid "Bus team" msgstr "Équipe de bus" -#: apps/wei/models.py:143 +#: apps/wei/models.py:151 msgid "Bus teams" msgstr "Équipes de bus" -#: apps/wei/models.py:152 +#: apps/wei/models.py:160 msgid "WEI Role" msgstr "Rôle au WEI" -#: apps/wei/models.py:177 +#: apps/wei/models.py:185 msgid "Credit from Société générale" msgstr "Crédit de la Société générale" -#: apps/wei/models.py:182 +#: apps/wei/models.py:190 msgid "Caution check given" msgstr "Chèque de caution donné" -#: apps/wei/models.py:186 apps/wei/templates/wei/weimembership_form.html:64 +#: apps/wei/models.py:194 apps/wei/templates/wei/weimembership_form.html:64 msgid "birth date" msgstr "date de naissance" -#: apps/wei/models.py:192 apps/wei/models.py:202 +#: apps/wei/models.py:200 apps/wei/models.py:210 msgid "Male" msgstr "Homme" -#: apps/wei/models.py:193 apps/wei/models.py:203 +#: apps/wei/models.py:201 apps/wei/models.py:211 msgid "Female" msgstr "Femme" -#: apps/wei/models.py:194 +#: apps/wei/models.py:202 msgid "Non binary" msgstr "Non-binaire" -#: apps/wei/models.py:196 apps/wei/templates/wei/attribute_bus_1A.html:22 +#: apps/wei/models.py:204 apps/wei/templates/wei/attribute_bus_1A.html:22 #: apps/wei/templates/wei/weimembership_form.html:55 msgid "gender" msgstr "genre" -#: apps/wei/models.py:205 apps/wei/templates/wei/weimembership_form.html:58 +#: apps/wei/models.py:213 apps/wei/templates/wei/weimembership_form.html:58 msgid "clothing cut" msgstr "coupe de vêtement" -#: apps/wei/models.py:218 apps/wei/templates/wei/weimembership_form.html:61 +#: apps/wei/models.py:226 apps/wei/templates/wei/weimembership_form.html:61 msgid "clothing size" msgstr "taille de vêtement" -#: apps/wei/models.py:224 apps/wei/templates/wei/attribute_bus_1A.html:28 +#: apps/wei/models.py:232 apps/wei/templates/wei/attribute_bus_1A.html:28 #: apps/wei/templates/wei/weimembership_form.html:67 msgid "health issues" msgstr "problèmes de santé" -#: apps/wei/models.py:229 apps/wei/templates/wei/weimembership_form.html:70 +#: apps/wei/models.py:237 apps/wei/templates/wei/weimembership_form.html:70 msgid "emergency contact name" msgstr "nom du contact en cas d'urgence" -#: apps/wei/models.py:234 apps/wei/templates/wei/weimembership_form.html:73 +#: apps/wei/models.py:242 apps/wei/templates/wei/weimembership_form.html:73 msgid "emergency contact phone" msgstr "téléphone du contact en cas d'urgence" -#: apps/wei/models.py:239 apps/wei/templates/wei/weimembership_form.html:52 +#: apps/wei/models.py:247 apps/wei/templates/wei/weimembership_form.html:52 msgid "first year" msgstr "première année" -#: apps/wei/models.py:240 +#: apps/wei/models.py:248 msgid "Tells if the user is new in the school." msgstr "Indique si l'utilisateur est nouveau dans l'école." -#: apps/wei/models.py:245 +#: apps/wei/models.py:253 msgid "registration information" msgstr "informations sur l'inscription" -#: apps/wei/models.py:246 +#: apps/wei/models.py:254 msgid "" "Information about the registration (buses for old members, survey for the " "new members), encoded in JSON" @@ -2687,27 +2687,27 @@ msgstr "" "Informations sur l'inscription (bus pour les 2A+, questionnaire pour les " "1A), encodées en JSON" -#: apps/wei/models.py:304 +#: apps/wei/models.py:312 msgid "WEI User" msgstr "Participant au WEI" -#: apps/wei/models.py:305 +#: apps/wei/models.py:313 msgid "WEI Users" msgstr "Participants au WEI" -#: apps/wei/models.py:325 +#: apps/wei/models.py:333 msgid "team" msgstr "équipe" -#: apps/wei/models.py:335 +#: apps/wei/models.py:343 msgid "WEI registration" msgstr "Inscription au WEI" -#: apps/wei/models.py:339 +#: apps/wei/models.py:347 msgid "WEI membership" msgstr "Adhésion au WEI" -#: apps/wei/models.py:340 +#: apps/wei/models.py:348 msgid "WEI memberships" msgstr "Adhésions au WEI" @@ -2735,32 +2735,32 @@ msgstr "Année" msgid "preferred bus" msgstr "bus préféré" -#: apps/wei/tables.py:211 apps/wei/templates/wei/bus_detail.html:32 +#: apps/wei/tables.py:210 apps/wei/templates/wei/bus_detail.html:32 #: apps/wei/templates/wei/busteam_detail.html:50 msgid "Teams" msgstr "Équipes" -#: apps/wei/tables.py:220 apps/wei/tables.py:261 +#: apps/wei/tables.py:219 apps/wei/tables.py:260 msgid "Members count" msgstr "Nombre de membres" -#: apps/wei/tables.py:227 apps/wei/tables.py:258 +#: apps/wei/tables.py:226 apps/wei/tables.py:257 msgid "members" msgstr "adhérents" -#: apps/wei/tables.py:288 +#: apps/wei/tables.py:287 msgid "suggested first year" msgstr "1A suggérés" -#: apps/wei/tables.py:294 +#: apps/wei/tables.py:293 msgid "validated first year" msgstr "1A validés" -#: apps/wei/tables.py:300 +#: apps/wei/tables.py:299 msgid "validated staff" msgstr "2A+ validés" -#: apps/wei/tables.py:311 +#: apps/wei/tables.py:310 msgid "free seats" msgstr "sièges libres" @@ -3116,7 +3116,7 @@ msgstr "Valider l'inscription WEI" msgid "Attribute buses to first year members" msgstr "Répartir les 1A dans les bus" -#: apps/wei/views.py:1190 +#: apps/wei/views.py:1191 msgid "Attribute bus" msgstr "Attribuer un bus" @@ -3252,16 +3252,15 @@ msgstr "" 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 " -"registration bonus of 80 € is not credited and the membership is not paid " -"yet. This verification procedure may last a few days. Please make sure that " -"you go to the end of the account creation." +"membership and the WEI are not paid yet. This verification procedure may " +"last a few days. Please make sure that you go to the end of the account " +"creation." msgstr "" "Vous avez déclaré que vous avez ouvert un compte bancaire à la société " "générale. La banque n'a pas encore validé la création du compte auprès du " -"BDE, le bonus d'inscription de 80 € n'a donc pas encore été créditée et " -"l'adhésion n'est pas encore payée. Cette procédure de vérification peut " -"durer quelques jours. Merci de vous assurer de bien aller au bout de vos " -"démarches." +"BDE, l'adhésion et le WEI ne sont donc pas encore payés. Cette procédure de " +"vérification peut durer quelques jours. Merci de vous assurer de bien aller " +"au bout de vos démarches." #: note_kfet/templates/base.html:195 msgid "Contact us" diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index 09ede8c6..f0d47934 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -170,8 +170,8 @@ SPDX-License-Identifier: GPL-3.0-or-later {% if user.sogecredit and not user.sogecredit.valid %}
{% blocktrans trimmed %} - 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 registration bonus of 80 € is not credited and the membership is not paid yet. + 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 membership and the WEI are not paid yet. This verification procedure may last a few days. Please make sure that you go to the end of the account creation. {% endblocktrans %} From 1a4b7c83e887e8c6683da1207c9578253d2d3dce Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 13 Sep 2021 23:37:27 +0200 Subject: [PATCH 04/20] [WEI] Fix critical security issue Signed-off-by: Yohann D'ANELLO --- apps/wei/templates/wei/weiclub_detail.html | 2 +- apps/wei/views.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/wei/templates/wei/weiclub_detail.html b/apps/wei/templates/wei/weiclub_detail.html index 9ffa7374..cd4b5efb 100644 --- a/apps/wei/templates/wei/weiclub_detail.html +++ b/apps/wei/templates/wei/weiclub_detail.html @@ -95,7 +95,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endif %} - {% if can_validate_1a or True %} + {% if can_validate_1a %} {% trans "Attribute buses" %} {% endif %} {% endblock %} diff --git a/apps/wei/views.py b/apps/wei/views.py index 09fd9d56..b60b4a73 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -191,6 +191,10 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): context["not_first_year"] = WEIMembership.objects.filter(user=self.request.user).exists() + qs = WEIMembership.objects.filter(club=club, registration__first_year=True, bus__isnull=True) + context["can_validate_1a"] = PermissionBackend.check_perm( + self.request, "wei.change_weimembership_bus", qs.first()) if qs.exists() else False + return context @@ -1181,7 +1185,10 @@ class WEI1AListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['club'] = self.club - context['bus_repartition_table'] = BusRepartitionTable(Bus.objects.filter(wei=self.club, size__gt=0).all()) + context['bus_repartition_table'] = BusRepartitionTable( + Bus.objects.filter(wei=self.club, size__gt=0) + .filter(PermissionBackend.filter_queryset(self.request, Bus, "view")) + .all()) return context From 0234f19a3383ffc97fe94a3e30c06d0738d3e47e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 14 Sep 2021 13:44:52 +0200 Subject: [PATCH 05/20] [WEI] Automatically indicate a soge credit if already created Signed-off-by: Yohann D'ANELLO --- apps/wei/views.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/wei/views.py b/apps/wei/views.py index b60b4a73..80ff770e 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -7,6 +7,7 @@ import subprocess from datetime import date, timedelta from tempfile import mkdtemp +from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.core.exceptions import PermissionDenied @@ -555,6 +556,12 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView): " participated to a WEI.")) return self.form_invalid(form) + if 'treasury' in settings.INSTALLED_APPS: + from treasury.models import SogeCredit + form.instance.soge_credit = \ + form.instance.soge_credit \ + or SogeCredit.objects.filter(user=form.instance.user, credit_transaction__valid=False).exists() + return super().form_valid(form) def get_success_url(self): @@ -656,6 +663,12 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): form.instance.information = information form.instance.save() + if 'treasury' in settings.INSTALLED_APPS: + from treasury.models import SogeCredit + form.instance.soge_credit = \ + form.instance.soge_credit \ + or SogeCredit.objects.filter(user=form.instance.user, credit_transaction__valid=False).exists() + return super().form_valid(form) def get_success_url(self): From ab2e580e685e42c88edfdda15195949949197fcf Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 15 Sep 2021 12:14:57 +0200 Subject: [PATCH 06/20] Update banner text for more precision Signed-off-by: Yohann D'ANELLO --- apps/registration/forms.py | 3 ++- locale/fr/LC_MESSAGES/django.po | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/registration/forms.py b/apps/registration/forms.py index 17b89c33..21e0da55 100644 --- a/apps/registration/forms.py +++ b/apps/registration/forms.py @@ -46,7 +46,8 @@ class SignUpForm(UserCreationForm): class DeclareSogeAccountOpenedForm(forms.Form): soge_account = forms.BooleanField( - label=_("I declare that I opened a bank account in the Société générale with the BDE partnership."), + label=_("I declare that I opened or I will open soon a bank account in the Société générale with the BDE \ + partnership."), help_text=_("Warning: this engages you to open your bank account. If you finally decides to don't open your " "account, you will have to pay the BDE membership."), required=False, diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 19ae9894..9175e2e7 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -1913,10 +1913,10 @@ msgstr "Cet email est déjà pris." #: apps/registration/forms.py:49 msgid "" -"I declare that I opened a bank account in the Société générale with the BDE " +"I declare that I opened or I will open soon a bank account in the Société générale with the BDE " "partnership." msgstr "" -"Je déclare avoir ouvert un compte à la société générale avec le partenariat " +"Je déclare avoir ouvert ou ouvrir prochainement un compte à la société générale avec le partenariat " "du BDE." #: apps/registration/forms.py:50 From a0b920ac94d01ab593f843bdfecd33bb3a15c249 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 15 Sep 2021 12:40:21 +0200 Subject: [PATCH 07/20] =?UTF-8?q?Don't=20check=20permission=20to=20edit=20?= =?UTF-8?q?credit=20transaction=20test=20while=20deleting=20a=20Sog=C3=A9C?= =?UTF-8?q?redit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yohann D'ANELLO --- apps/treasury/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/treasury/models.py b/apps/treasury/models.py index 7e4e1566..3ba1ee7c 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -432,6 +432,7 @@ class SogeCredit(models.Model): # was opened after the validation of the account. self.credit_transaction.valid = False self.credit_transaction.reason += " (invalide)" + self.credit_transaction._force_save = True self.credit_transaction.save() super().delete(**kwargs) From 76531595ad6400625debddbdd9c6075678fb000d Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 16 Sep 2021 10:58:23 +0200 Subject: [PATCH 08/20] =?UTF-8?q?80=20=E2=82=AC=20for=20people=20that=20op?= =?UTF-8?q?ened=20an=20account=20to=20Soci=C3=A9t=C3=A9=20g=C3=A9n=C3=A9ra?= =?UTF-8?q?le=20and=20don't=20go=20to=20the=20WEI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yohann D'ANELLO --- apps/treasury/models.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/treasury/models.py b/apps/treasury/models.py index 3ba1ee7c..28351531 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -1,6 +1,6 @@ # Copyright (C) 2018-2021 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later - +import datetime from datetime import date from django.conf import settings @@ -305,8 +305,16 @@ class SogeCredit(models.Model): @property def amount(self): - return self.credit_transaction.total if self.valid \ - else sum(transaction.total for transaction in self.transactions.all()) + if self.valid: + return self.credit_transaction.total + amount = sum(transaction.total for transaction in self.transactions.all()) + if 'wei' in settings.INSTALLED_APPS: + from wei.models import WEIMembership + if not WEIMembership.objects.filter(club__weiclub__year=datetime.date.today().year, user=self.user)\ + .exists(): + # 80 € for people that don't go to WEI + amount += 8000 + return amount def update_transactions(self): """ From a3a9dfc8124c54d0ee680fb039adad3bfe1db1c5 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 16 Sep 2021 11:00:10 +0200 Subject: [PATCH 09/20] =?UTF-8?q?[Treasury]=20Don't=20add=20non-existing?= =?UTF-8?q?=20transactions=20to=20sog=C3=A9-credits=20(eg.=20when=20member?= =?UTF-8?q?ship=20is=20free)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yohann D'ANELLO --- apps/treasury/models.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/treasury/models.py b/apps/treasury/models.py index 28351531..427d796d 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -331,13 +331,15 @@ class SogeCredit(models.Model): if bde_qs.exists(): m = bde_qs.get() - if m.transaction not in self.transactions.all(): - self.transactions.add(m.transaction) + if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership + if m.transaction not in self.transactions.all(): + self.transactions.add(m.transaction) if kfet_qs.exists(): m = kfet_qs.get() - if m.transaction not in self.transactions.all(): - self.transactions.add(m.transaction) + if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership + if m.transaction not in self.transactions.all(): + self.transactions.add(m.transaction) if 'wei' in settings.INSTALLED_APPS: from wei.models import WEIClub @@ -345,8 +347,9 @@ class SogeCredit(models.Model): wei_qs = Membership.objects.filter(user=self.user, club=wei, date_start__gte=wei.membership_start) if wei_qs.exists(): m = wei_qs.get() - if m.transaction not in self.transactions.all(): - self.transactions.add(m.transaction) + if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership + if m.transaction not in self.transactions.all(): + self.transactions.add(m.transaction) for tr in self.transactions.all(): tr.valid = False From aa75ce5c7a58c0a733b0764060160642db0163c0 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 16 Sep 2021 15:37:18 +0200 Subject: [PATCH 10/20] [WEI] Don't manage hardcoded people in repartition algorithm Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index a6a241cb..24538045 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -170,7 +170,9 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): We modify it to allow buses to have multiple "weddings". """ surveys = list(self.get_survey_class()(r) for r in self.get_registrations()) # All surveys - surveys = [s for s in surveys if s.is_complete()] + surveys = [s for s in surveys if s.is_complete()] # Don't consider invalid surveys + # Don't manage hardcoded people + surveys = [s for s in surveys if not hasattr(s.information, 'hardcoded') or not s.information.hardcoded] free_surveys = [s for s in surveys if not s.information.valid] # Remaining surveys while free_surveys: # Some students are not affected survey = free_surveys[0] From 79a116d9c624e8cdbdf086fec7f89775095ac62c Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 16 Sep 2021 20:05:20 +0200 Subject: [PATCH 11/20] [WEI] Cache optimization Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/base.py | 31 ++++++++++++++++++++++--------- apps/wei/forms/surveys/wei2021.py | 5 +++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/apps/wei/forms/surveys/base.py b/apps/wei/forms/surveys/base.py index 030f9078..d7fb79b2 100644 --- a/apps/wei/forms/surveys/base.py +++ b/apps/wei/forms/surveys/base.py @@ -50,15 +50,19 @@ class WEIBusInformation: self.bus.information = d self.bus.save() - def free_seats(self, surveys: List["WEISurvey"] = None): - size = self.bus.size - already_occupied = WEIMembership.objects.filter(bus=self.bus).count() + def free_seats(self, surveys: List["WEISurvey"] = None, quotas=None): + if not quotas: + size = self.bus.size + already_occupied = WEIMembership.objects.filter(bus=self.bus).count() + quotas = {self.bus: size - already_occupied} + + quota = quotas[self.bus] valid_surveys = sum(1 for survey in surveys if survey.information.valid and survey.information.get_selected_bus() == self.bus) if surveys else 0 - return size - already_occupied - valid_surveys + return quota - valid_surveys - def has_free_seats(self, surveys=None): - return self.free_seats(surveys) > 0 + def has_free_seats(self, surveys=None, quotas=None): + return self.free_seats(surveys, quotas) > 0 class WEISurveyAlgorithm: @@ -86,14 +90,20 @@ class WEISurveyAlgorithm: """ Queryset of all first year registrations """ - return WEIRegistration.objects.filter(wei__year=cls.get_survey_class().get_year(), first_year=True) + if not hasattr(cls, '_registrations'): + cls._registrations = WEIRegistration.objects.filter(wei__year=cls.get_survey_class().get_year(), + first_year=True).all() + + return cls._registrations @classmethod def get_buses(cls) -> QuerySet: """ Queryset of all buses of the associated wei. """ - return Bus.objects.filter(wei__year=cls.get_survey_class().get_year(), size__gt=0) + if not hasattr(cls, '_buses'): + cls._buses = Bus.objects.filter(wei__year=cls.get_survey_class().get_year(), size__gt=0).all() + return cls._buses @classmethod def get_bus_information(cls, bus): @@ -135,7 +145,10 @@ class WEISurvey: """ The WEI associated to this kind of survey. """ - return WEIClub.objects.get(year=cls.get_year()) + if not hasattr(cls, '_wei'): + cls._wei = WEIClub.objects.get(year=cls.get_year()) + + return cls._wei @classmethod def get_survey_information_class(cls): diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index 24538045..a9205a3f 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -1,6 +1,8 @@ # Copyright (C) 2018-2021 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later + import time +from functools import lru_cache from random import Random from django import forms @@ -135,15 +137,18 @@ class WEISurvey2021(WEISurvey): """ return self.information.step == 20 + @lru_cache() def score(self, bus): if not self.is_complete(): raise ValueError("Survey is not ended, can't calculate score") bus_info = self.get_algorithm_class().get_bus_information(bus) return sum(bus_info.scores[getattr(self.information, 'word' + str(i))] for i in range(1, 21)) / 20 + @lru_cache() def scores_per_bus(self): return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()} + @lru_cache() def ordered_buses(self): values = list(self.scores_per_bus().items()) values.sort(key=lambda item: -item[1]) From e89383e3f49c33c5a5b93d46c33e003f6dcb3f97 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 16 Sep 2021 20:06:34 +0200 Subject: [PATCH 12/20] [WEI] Start repartition by non-male people Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index a9205a3f..8c1e2945 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -7,9 +7,11 @@ from random import Random from django import forms from django.db import transaction +from django.db.models import Q from django.utils.translation import gettext_lazy as _ from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInformation +from ...models import WEIMembership WORDS = [ '13 organisé', '3ième mi temps', 'Années 2000', 'Apéro', 'BBQ', 'BP', 'Beauf', 'Binge drinking', 'Bon enfant', @@ -178,12 +180,33 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): surveys = [s for s in surveys if s.is_complete()] # Don't consider invalid surveys # Don't manage hardcoded people surveys = [s for s in surveys if not hasattr(s.information, 'hardcoded') or not s.information.hardcoded] - free_surveys = [s for s in surveys if not s.information.valid] # Remaining surveys + + # Reset previous algorithm run + for survey in surveys: + survey.free() + survey.save() + + non_men = [s for s in surveys if s.registration.gender != 'male'] + men = [s for s in surveys if s.registration.gender == 'male'] + + quotas = {} + registrations = self.get_registrations() + non_men_total = registrations.filter(~Q(gender='male')).count() + for bus in self.get_buses(): + free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count() + quotas[bus] = 4 + int(non_men_total / registrations.count() * free_seats) + + # Repartition for non men people first + self.make_repartition(non_men, quotas) + self.make_repartition(men) + + def make_repartition(self, surveys, quotas=None): + free_surveys = surveys.copy() # Remaining surveys while free_surveys: # Some students are not affected survey = free_surveys[0] buses = survey.ordered_buses() # Preferences of the student - for bus, _ignored in buses: - if self.get_bus_information(bus).has_free_seats(surveys): + for bus, current_score in buses: + if self.get_bus_information(bus).has_free_seats(surveys, quotas): # Selected bus has free places. Put student in the bus survey.select_bus(bus) survey.save() @@ -191,7 +214,6 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): break else: # Current bus has not enough places. Remove the least preferred student from the bus if existing - current_score = survey.score(bus) least_preferred_survey = None least_score = -1 # Find the least student in the bus that has a lower score than the current student From 1ef25924a0e0ca1624ef0ef48d0e6c59e5b94065 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 16 Sep 2021 20:46:34 +0200 Subject: [PATCH 13/20] [WEI] Display status bar with tqdm Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 30 +++++++++++++++---- apps/wei/management/commands/wei_algorithm.py | 16 ++++++++-- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index 8c1e2945..a046b7ea 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -171,7 +171,7 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): def get_bus_information_class(cls): return WEIBusInformation2021 - def run_algorithm(self): + def run_algorithm(self, display_tqdm=False): """ Gale-Shapley algorithm implementation. We modify it to allow buses to have multiple "weddings". @@ -196,11 +196,26 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count() quotas[bus] = 4 + int(non_men_total / registrations.count() * free_seats) - # Repartition for non men people first - self.make_repartition(non_men, quotas) - self.make_repartition(men) + tqdm_obj = None + if display_tqdm: + from tqdm import tqdm + tqdm_obj = tqdm(total=len(non_men), desc="Non-hommes") - def make_repartition(self, surveys, quotas=None): + # Repartition for non men people first + self.make_repartition(non_men, quotas, tqdm_obj=tqdm_obj) + + if display_tqdm: + tqdm_obj.close() + + from tqdm import tqdm + tqdm_obj = tqdm(total=len(men), desc="Hommes") + + self.make_repartition(men, tqdm_obj=tqdm_obj) + + if display_tqdm: + tqdm_obj.close() + + def make_repartition(self, surveys, quotas=None, tqdm_obj=None): free_surveys = surveys.copy() # Remaining surveys while free_surveys: # Some students are not affected survey = free_surveys[0] @@ -235,6 +250,11 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): free_surveys.append(least_preferred_survey) survey.select_bus(bus) survey.save() + free_surveys.remove(survey) break else: raise ValueError(f"User {survey.registration.user} has no free seat") + + if tqdm_obj is not None: + tqdm_obj.n = len(surveys) - len(free_surveys) + tqdm_obj.refresh() diff --git a/apps/wei/management/commands/wei_algorithm.py b/apps/wei/management/commands/wei_algorithm.py index 238bf13c..93f919f0 100644 --- a/apps/wei/management/commands/wei_algorithm.py +++ b/apps/wei/management/commands/wei_algorithm.py @@ -24,7 +24,14 @@ class Command(BaseCommand): sid = transaction.savepoint() algorithm = CurrentSurvey.get_algorithm_class()() - algorithm.run_algorithm() + + try: + from tqdm import tqdm + display_tqdm = True + except ImportError: + display_tqdm = False + + algorithm.run_algorithm(display_tqdm=display_tqdm) output = options['output'] registrations = algorithm.get_registrations() @@ -34,8 +41,13 @@ class Command(BaseCommand): for bus, members in per_bus.items(): output.write(bus.name + "\n") output.write("=" * len(bus.name) + "\n") + order = -1 for r in members: - output.write(r.user.username + "\n") + survey = CurrentSurvey(r) + for order, (b, _score) in enumerate(survey.ordered_buses()): + if b == bus: + break + output.write(f"{r.user.username} ({order + 1})\n") output.write("\n") if not options['doit']: From 9583cec3ff41d7c728d2107059e9caff0b6b47a2 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 16 Sep 2021 21:10:23 +0200 Subject: [PATCH 14/20] [WEI] Fix quotas Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index a046b7ea..5b7d4530 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -204,13 +204,21 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): # Repartition for non men people first self.make_repartition(non_men, quotas, tqdm_obj=tqdm_obj) + quotas = {} + for bus in self.get_buses(): + free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count() + free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk) + quotas[bus] = free_seats + + print(quotas) + if display_tqdm: tqdm_obj.close() from tqdm import tqdm tqdm_obj = tqdm(total=len(men), desc="Hommes") - self.make_repartition(men, tqdm_obj=tqdm_obj) + self.make_repartition(men, quotas, tqdm_obj=tqdm_obj) if display_tqdm: tqdm_obj.close() From 8638c16b34100ef10165dfb648c00c845b32ec6a Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 16 Sep 2021 22:15:30 +0200 Subject: [PATCH 15/20] [WEI] New score function that takes in account scores given by other buses Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index 5b7d4530..380d8683 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -139,12 +139,25 @@ class WEISurvey2021(WEISurvey): """ return self.information.step == 20 + @classmethod + @lru_cache() + def word_mean(cls, word): + """ + Calculate the mid-score given by all buses. + """ + buses = cls.get_algorithm_class().get_buses() + return sum([cls.get_algorithm_class().get_bus_information(bus).scores[word] for bus in buses]) / buses.count() + @lru_cache() def score(self, bus): if not self.is_complete(): raise ValueError("Survey is not ended, can't calculate score") + bus_info = self.get_algorithm_class().get_bus_information(bus) - return sum(bus_info.scores[getattr(self.information, 'word' + str(i))] for i in range(1, 21)) / 20 + # Score is the given score by the bus subtracted to the mid-score of the buses. + s = sum(bus_info.scores[getattr(self.information, 'word' + str(i))] + - self.word_mean(getattr(self.information, 'word' + str(i))) for i in range(1, 21)) / 20 + return s @lru_cache() def scores_per_bus(self): @@ -210,8 +223,6 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk) quotas[bus] = free_seats - print(quotas) - if display_tqdm: tqdm_obj.close() @@ -265,4 +276,4 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): if tqdm_obj is not None: tqdm_obj.n = len(surveys) - len(free_surveys) - tqdm_obj.refresh() + tqdm_obj.refresh() \ No newline at end of file From 161db0b00bee067bb1215aa5d57320aa8c692d09 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 16 Sep 2021 23:48:03 +0200 Subject: [PATCH 16/20] [WEI] Fix quotas Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index 380d8683..031d9fb5 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -207,6 +207,9 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): non_men_total = registrations.filter(~Q(gender='male')).count() for bus in self.get_buses(): free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count() + # Remove hardcoded people + free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True, + registration__information_json__icontains="hardcoded").count() quotas[bus] = 4 + int(non_men_total / registrations.count() * free_seats) tqdm_obj = None @@ -221,6 +224,9 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): for bus in self.get_buses(): free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count() free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk) + # Remove hardcoded people + free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True, + registration__information_json__icontains="hardcoded").count() quotas[bus] = free_seats if display_tqdm: From 668cfa71a7404b585d0b8eb074695a9d17a77020 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Sun, 26 Sep 2021 23:02:31 +0200 Subject: [PATCH 17/20] fix #98 --- note_kfet/static/js/base.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/note_kfet/static/js/base.js b/note_kfet/static/js/base.js index 1afc858c..e642d15c 100644 --- a/note_kfet/static/js/base.js +++ b/note_kfet/static/js/base.js @@ -96,7 +96,11 @@ function displayStyle (note) { if (!note) { return '' } const balance = note.balance var css = '' - if (balance < -5000) { css += ' text-danger bg-dark' } else if (balance < -1000) { css += ' text-danger' } else if (balance < 0) { css += ' text-warning' } else if (!note.email_confirmed) { css += ' text-white bg-primary' } else if (!note.is_active || (note.membership && note.membership.date_end < new Date().toISOString())) { css += 'text-white bg-info' } + if (balance < -5000) { css += ' text-danger bg-dark' } + else if (balance < -1000) { css += ' text-danger' } + else if (balance < 0) { css += ' text-warning' } + if (!note.email_confirmed) { css += ' bg-primary' } + else if (!note.is_active || (note.membership && note.membership.date_end < new Date().toISOString())) { css += ' bg-info' } return css } From 6b4d18f4b365952bf5b432054380b948fcd3367e Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Sun, 26 Sep 2021 19:26:32 +0200 Subject: [PATCH 18/20] fix #97 --- note_kfet/templates/base.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index f0d47934..3adcef2c 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -193,6 +193,8 @@ SPDX-License-Identifier: GPL-3.0-or-later {% trans "Contact us" %} — + {% trans "Technical Support" %} {% csrf_token %}