From db936bf75a00d75a092999aa87c7f6adaafdf416 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 17:52:52 +0200 Subject: [PATCH 01/11] Avoid anonymous users to access to the WEI registration form Signed-off-by: Yohann D'ANELLO --- apps/wei/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/wei/views.py b/apps/wei/views.py index 348ac751..35a05aaf 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -511,7 +511,8 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView): if today >= wei.date_start or today < wei.membership_start: return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,))) # Don't register twice - if 'myself' in self.request.path and WEIRegistration.objects.filter(wei=wei, user=self.request.user).exists(): + if 'myself' in self.request.path and not self.request.user.is_anonymous \ + and WEIRegistration.objects.filter(wei=wei, user=self.request.user).exists(): obj = WEIRegistration.objects.get(wei=wei, user=self.request.user) return redirect(reverse_lazy('wei:wei_update_registration', args=(obj.pk,))) return super().dispatch(request, *args, **kwargs) @@ -590,7 +591,8 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): if today >= wei.date_start or today < wei.membership_start: return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,))) # Don't register twice - if 'myself' in self.request.path and WEIRegistration.objects.filter(wei=wei, user=self.request.user).exists(): + if 'myself' in self.request.path and not self.request.user.is_anonymous \ + and WEIRegistration.objects.filter(wei=wei, user=self.request.user).exists(): obj = WEIRegistration.objects.get(wei=wei, user=self.request.user) return redirect(reverse_lazy('wei:wei_update_registration', args=(obj.pk,))) return super().dispatch(request, *args, **kwargs) From 49551e88f8e2584c10ce6c48244a039bc745261e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 19:51:57 +0200 Subject: [PATCH 02/11] Fix default promotion year Signed-off-by: Yohann D'ANELLO --- apps/member/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/member/models.py b/apps/member/models.py index 73b7c668..9bb4ecf0 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -74,7 +74,7 @@ class Profile(models.Model): promotion = models.PositiveSmallIntegerField( null=True, - default=datetime.date.today().year, + default=datetime.date.today().year if datetime.date.today().month >= 8 else datetime.date.today().year - 1, verbose_name=_("promotion"), help_text=_("Year of entry to the school (None if not ENS student)"), ) From 3532846c8715b64de0f6618690452d78d9ff4390 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 10 Sep 2021 22:09:47 +0200 Subject: [PATCH 03/11] [WEI] Validate WEI memberships of first year members before the repartition algorithm to debit notes Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/__init__.py | 4 +- apps/wei/forms/registration.py | 14 +++ apps/wei/tables.py | 4 +- apps/wei/views.py | 50 ++++++----- locale/fr/LC_MESSAGES/django.po | 146 ++++++++++++++++---------------- 5 files changed, 121 insertions(+), 97 deletions(-) diff --git a/apps/wei/forms/__init__.py b/apps/wei/forms/__init__.py index ecec33d5..1125ab3c 100644 --- a/apps/wei/forms/__init__.py +++ b/apps/wei/forms/__init__.py @@ -1,10 +1,10 @@ # Copyright (C) 2018-2021 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from .registration import WEIForm, WEIRegistrationForm, WEIMembershipForm, BusForm, BusTeamForm +from .registration import WEIForm, WEIRegistrationForm, WEIMembership1AForm, WEIMembershipForm, BusForm, BusTeamForm from .surveys import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, CurrentSurvey __all__ = [ - 'WEIForm', 'WEIRegistrationForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm', + 'WEIForm', 'WEIRegistrationForm', 'WEIMembesrhip1AForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm', 'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey', ] diff --git a/apps/wei/forms/registration.py b/apps/wei/forms/registration.py index 474d83ee..6bad1dab 100644 --- a/apps/wei/forms/registration.py +++ b/apps/wei/forms/registration.py @@ -144,6 +144,20 @@ class WEIMembershipForm(forms.ModelForm): } +class WEIMembership1AForm(WEIMembershipForm): + """ + Used to confirm registrations of first year members without choosing a bus now. + """ + roles = None + + def clean(self): + return super(forms.ModelForm, self).clean() + + class Meta: + model = WEIMembership + fields = ('credit_type', 'credit_amount', 'last_name', 'first_name', 'bank',) + + class BusForm(forms.ModelForm): class Meta: model = Bus diff --git a/apps/wei/tables.py b/apps/wei/tables.py index 0f862cc9..0f48dcc1 100644 --- a/apps/wei/tables.py +++ b/apps/wei/tables.py @@ -102,9 +102,9 @@ class WEIRegistrationTable(tables.Table): if record.fee > record.user.note.balance and not record.soge_credit: btn_class = 'btn-secondary' tooltip = _("The user does not have enough money.") - elif record.first_year and 'selected_bus_pk' not in record.information: + elif record.first_year: btn_class = 'btn-info' - tooltip = _("The user is in first year, and the repartition algorithm didn't run.") + tooltip = _("The user is in first year. You may validate the credit, the algorithm will run later.") else: btn_class = 'btn-success' tooltip = _("The user has enough money, you can validate the registration.") diff --git a/apps/wei/views.py b/apps/wei/views.py index 35a05aaf..31e03260 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -32,7 +32,8 @@ from permission.views import ProtectQuerysetMixin, ProtectedCreateView from .forms.registration import WEIChooseBusForm from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole -from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembershipForm, CurrentSurvey +from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembership1AForm, \ + WEIMembershipForm, CurrentSurvey from .tables import WEITable, WEIRegistrationTable, BusTable, BusTeamTable, WEIMembershipTable @@ -799,7 +800,6 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): Validate WEI Registration """ model = WEIMembership - form_class = WEIMembershipForm extra_context = {"title": _("Validate WEI registration")} def get_sample_object(self): @@ -855,6 +855,12 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): return context + def get_form_class(self): + registration = WEIRegistration.objects.get(pk=self.kwargs["pk"]) + if registration.first_year and 'sleected_bus_pk' not in registration.information: + return WEIMembership1AForm + return WEIMembershipForm + def get_form(self, form_class=None): form = super().get_form(form_class) registration = WEIRegistration.objects.get(pk=self.kwargs["pk"]) @@ -870,25 +876,27 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): form.fields["bank"].disabled = True form.fields["bank"].initial = "Société générale" - form.fields["bus"].widget.attrs["api_url"] = "/api/wei/bus/?wei=" + str(registration.wei.pk) - if registration.first_year: - # Use the results of the survey to fill initial data - # A first year has no other role than "1A" - del form.fields["roles"] - survey = CurrentSurvey(registration) - if survey.information.valid: - form.fields["bus"].initial = survey.information.get_selected_bus() - else: - # Use the choice of the member to fill initial data - information = registration.information - if "preferred_bus_pk" in information and len(information["preferred_bus_pk"]) == 1: - form["bus"].initial = Bus.objects.get(pk=information["preferred_bus_pk"][0]) - if "preferred_team_pk" in information and len(information["preferred_team_pk"]) == 1: - form["team"].initial = BusTeam.objects.get(pk=information["preferred_team_pk"][0]) - if "preferred_roles_pk" in information: - form["roles"].initial = WEIRole.objects.filter( - Q(pk__in=information["preferred_roles_pk"]) | Q(name="Adhérent WEI") - ).all() + if 'bus' in form.fields: + # For 2A+ and hardcoded 1A + form.fields["bus"].widget.attrs["api_url"] = "/api/wei/bus/?wei=" + str(registration.wei.pk) + if registration.first_year: + # Use the results of the survey to fill initial data + # A first year has no other role than "1A" + del form.fields["roles"] + survey = CurrentSurvey(registration) + if survey.information.valid: + form.fields["bus"].initial = survey.information.get_selected_bus() + else: + # Use the choice of the member to fill initial data + information = registration.information + if "preferred_bus_pk" in information and len(information["preferred_bus_pk"]) == 1: + form["bus"].initial = Bus.objects.get(pk=information["preferred_bus_pk"][0]) + if "preferred_team_pk" in information and len(information["preferred_team_pk"]) == 1: + form["team"].initial = BusTeam.objects.get(pk=information["preferred_team_pk"][0]) + if "preferred_roles_pk" in information: + form["roles"].initial = WEIRole.objects.filter( + Q(pk__in=information["preferred_roles_pk"]) | Q(name="Adhérent WEI") + ).all() return form @transaction.atomic diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 6416a03a..b8d38275 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-08 18:46+0200\n" +"POT-Creation-Date: 2021-09-10 22:08+0200\n" "PO-Revision-Date: 2020-11-16 20:02+0000\n" "Last-Translator: Yohann D'ANELLO \n" "Language-Team: French \n" @@ -111,7 +111,7 @@ msgid "type" 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:286 +#: apps/note/models/notes.py:148 apps/treasury/models.py:285 #: apps/wei/models.py:165 apps/wei/templates/wei/survey.html:15 msgid "user" msgstr "utilisateur" @@ -251,7 +251,7 @@ msgstr "Entré le " msgid "remove" msgstr "supprimer" -#: apps/activity/tables.py:80 apps/note/forms.py:68 apps/treasury/models.py:200 +#: apps/activity/tables.py:80 apps/note/forms.py:68 apps/treasury/models.py:199 msgid "Type" msgstr "Type" @@ -1188,7 +1188,7 @@ msgstr "Modifier le club" msgid "Add new member to the club" msgstr "Ajouter un nouveau membre au club" -#: apps/member/views.py:642 apps/wei/views.py:932 +#: apps/member/views.py:642 apps/wei/views.py:952 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -1494,8 +1494,8 @@ msgstr "" "mode de paiement et un utilisateur ou un club" #: apps/note/models/transactions.py:355 apps/note/models/transactions.py:358 -#: apps/note/models/transactions.py:361 apps/wei/views.py:937 -#: apps/wei/views.py:941 +#: apps/note/models/transactions.py:361 apps/wei/views.py:957 +#: apps/wei/views.py:961 msgid "This field is required." msgstr "Ce champ est requis." @@ -1511,7 +1511,7 @@ msgstr "Transactions de crédit/retrait" msgid "membership transaction" msgstr "transaction d'adhésion" -#: apps/note/models/transactions.py:385 apps/treasury/models.py:293 +#: apps/note/models/transactions.py:385 apps/treasury/models.py:292 msgid "membership transactions" msgstr "transactions d'adhésion" @@ -1625,7 +1625,7 @@ msgid "Amount" msgstr "Montant" #: apps/note/templates/note/transaction_form.html:128 -#: apps/treasury/models.py:55 +#: apps/treasury/models.py:54 msgid "Name" msgstr "Nom" @@ -2102,7 +2102,7 @@ msgstr "Invalider l'inscription" msgid "Treasury" msgstr "Trésorerie" -#: apps/treasury/forms.py:26 apps/treasury/models.py:94 +#: apps/treasury/forms.py:26 apps/treasury/models.py:93 #: apps/treasury/templates/treasury/invoice_form.html:22 msgid "This invoice is locked and can no longer be edited." msgstr "Cette facture est verrouillée et ne peut plus être éditée." @@ -2115,7 +2115,7 @@ msgstr "La remise est déjà fermée." 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:268 +#: apps/treasury/forms.py:125 apps/treasury/models.py:267 #: apps/treasury/tables.py:97 apps/treasury/tables.py:105 #: apps/treasury/templates/treasury/invoice_list.html:16 #: apps/treasury/templates/treasury/remittance_list.html:16 @@ -2127,120 +2127,120 @@ msgstr "Remise" msgid "No attached remittance" msgstr "Pas de remise associée" -#: apps/treasury/models.py:27 +#: apps/treasury/models.py:26 msgid "Invoice identifier" msgstr "Numéro de facture" -#: apps/treasury/models.py:41 +#: apps/treasury/models.py:40 msgid "BDE" msgstr "BDE" -#: apps/treasury/models.py:46 +#: apps/treasury/models.py:45 msgid "Object" msgstr "Objet" -#: apps/treasury/models.py:50 +#: apps/treasury/models.py:49 msgid "Description" msgstr "Description" -#: apps/treasury/models.py:59 +#: apps/treasury/models.py:58 msgid "Address" msgstr "Adresse" -#: apps/treasury/models.py:64 apps/treasury/models.py:194 +#: apps/treasury/models.py:63 apps/treasury/models.py:193 msgid "Date" msgstr "Date" -#: apps/treasury/models.py:68 +#: apps/treasury/models.py:67 msgid "Acquitted" msgstr "Acquittée" -#: apps/treasury/models.py:73 +#: apps/treasury/models.py:72 msgid "Locked" msgstr "Verrouillée" -#: apps/treasury/models.py:74 +#: apps/treasury/models.py:73 msgid "An invoice can't be edited when it is locked." msgstr "Une facture ne peut plus être modifiée si elle est verrouillée." -#: apps/treasury/models.py:80 +#: apps/treasury/models.py:79 msgid "tex source" msgstr "fichier TeX source" -#: apps/treasury/models.py:114 apps/treasury/models.py:130 +#: apps/treasury/models.py:113 apps/treasury/models.py:129 msgid "invoice" msgstr "facture" -#: apps/treasury/models.py:115 +#: apps/treasury/models.py:114 msgid "invoices" msgstr "factures" -#: apps/treasury/models.py:118 +#: apps/treasury/models.py:117 #, python-brace-format msgid "Invoice #{id}" msgstr "Facture n°{id}" -#: apps/treasury/models.py:135 +#: apps/treasury/models.py:134 msgid "Designation" msgstr "Désignation" -#: apps/treasury/models.py:141 +#: apps/treasury/models.py:140 msgid "Quantity" msgstr "Quantité" -#: apps/treasury/models.py:146 +#: apps/treasury/models.py:145 msgid "Unit price" msgstr "Prix unitaire" -#: apps/treasury/models.py:162 +#: apps/treasury/models.py:161 msgid "product" msgstr "produit" -#: apps/treasury/models.py:163 +#: apps/treasury/models.py:162 msgid "products" msgstr "produits" -#: apps/treasury/models.py:183 +#: apps/treasury/models.py:182 msgid "remittance type" msgstr "type de remise" -#: apps/treasury/models.py:184 +#: apps/treasury/models.py:183 msgid "remittance types" msgstr "types de remises" -#: apps/treasury/models.py:205 +#: apps/treasury/models.py:204 msgid "Comment" msgstr "Commentaire" -#: apps/treasury/models.py:210 +#: apps/treasury/models.py:209 msgid "Closed" msgstr "Fermée" -#: apps/treasury/models.py:214 +#: apps/treasury/models.py:213 msgid "remittance" msgstr "remise" -#: apps/treasury/models.py:215 +#: apps/treasury/models.py:214 msgid "remittances" msgstr "remises" -#: apps/treasury/models.py:248 +#: apps/treasury/models.py:247 msgid "Remittance #{:d}: {}" msgstr "Remise n°{:d} : {}" -#: apps/treasury/models.py:272 +#: apps/treasury/models.py:271 msgid "special transaction proxy" msgstr "proxy de transaction spéciale" -#: apps/treasury/models.py:273 +#: apps/treasury/models.py:272 msgid "special transaction proxies" msgstr "proxys de transactions spéciales" -#: apps/treasury/models.py:299 +#: apps/treasury/models.py:298 msgid "credit transaction" msgstr "transaction de crédit" -#: apps/treasury/models.py:418 +#: apps/treasury/models.py:419 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." @@ -2248,16 +2248,16 @@ msgstr "" "Cet utilisateur 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/models.py:438 +#: apps/treasury/models.py:439 #: apps/treasury/templates/treasury/sogecredit_detail.html:10 msgid "Credit from the Société générale" msgstr "Crédit de la Société générale" -#: apps/treasury/models.py:439 +#: apps/treasury/models.py:440 msgid "Credits from the Société générale" msgstr "Crédits de la Société générale" -#: apps/treasury/models.py:442 +#: apps/treasury/models.py:443 #, python-brace-format msgid "Soge credit for {user}" msgstr "Crédit de la société générale pour l'utilisateur {user}" @@ -2710,10 +2710,12 @@ msgid "The user does not have enough money." msgstr "L'utilisateur n'a pas assez d'argent." #: apps/wei/tables.py:107 -msgid "The user is in first year, and the repartition algorithm didn't run." +msgid "" +"The user is in first year. You may validate the credit, the algorithm will " +"run later." msgstr "" -"L'utilisateur est en première année, et l'algorithme de répartition n'a pas " -"tourné." +"L'utilisateur est en première année, vous pouvez valider le crédit, " +"l'algorithme tournera plus tard." #: apps/wei/tables.py:110 msgid "The user has enough money, you can validate the registration." @@ -2752,11 +2754,11 @@ msgstr "Prix du WEI (étudiants)" msgid "WEI list" msgstr "Liste des WEI" -#: apps/wei/templates/wei/base.html:81 apps/wei/views.py:517 +#: apps/wei/templates/wei/base.html:81 apps/wei/views.py:523 msgid "Register 1A" msgstr "Inscrire un 1A" -#: apps/wei/templates/wei/base.html:85 apps/wei/views.py:592 +#: apps/wei/templates/wei/base.html:85 apps/wei/views.py:603 msgid "Register 2A+" msgstr "Inscrire un 2A+" @@ -2785,8 +2787,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:988 -#: apps/wei/views.py:1043 apps/wei/views.py:1053 +#: apps/wei/templates/wei/survey_end.html:11 apps/wei/views.py:1007 +#: apps/wei/views.py:1062 apps/wei/views.py:1072 msgid "Survey WEI" msgstr "Questionnaire WEI" @@ -2827,7 +2829,7 @@ msgstr "Membres du WEI" msgid "Unvalidated registrations" msgstr "Inscriptions non validées" -#: apps/wei/templates/wei/weiclub_list.html:14 apps/wei/views.py:77 +#: apps/wei/templates/wei/weiclub_list.html:14 apps/wei/views.py:78 msgid "Create WEI" msgstr "Créer un WEI" @@ -2968,67 +2970,67 @@ msgstr "Il n'y a pas de pré-inscription en attente avec cette entrée." msgid "View validated memberships..." msgstr "Voir les adhésions validées ..." -#: apps/wei/views.py:56 +#: apps/wei/views.py:57 msgid "Search WEI" msgstr "Chercher un WEI" -#: apps/wei/views.py:107 +#: apps/wei/views.py:108 msgid "WEI Detail" msgstr "Détails du WEI" -#: apps/wei/views.py:202 +#: apps/wei/views.py:203 msgid "View members of the WEI" msgstr "Voir les membres du WEI" -#: apps/wei/views.py:230 +#: apps/wei/views.py:231 msgid "Find WEI Membership" msgstr "Trouver une adhésion au WEI" -#: apps/wei/views.py:240 +#: apps/wei/views.py:241 msgid "View registrations to the WEI" msgstr "Voir les inscriptions au WEI" -#: apps/wei/views.py:264 +#: apps/wei/views.py:265 msgid "Find WEI Registration" msgstr "Trouver une inscription au WEI" -#: apps/wei/views.py:275 +#: apps/wei/views.py:276 msgid "Update the WEI" msgstr "Modifier le WEI" -#: apps/wei/views.py:296 +#: apps/wei/views.py:297 msgid "Create new bus" msgstr "Ajouter un nouveau bus" -#: apps/wei/views.py:334 +#: apps/wei/views.py:335 msgid "Update bus" msgstr "Modifier le bus" -#: apps/wei/views.py:366 +#: apps/wei/views.py:367 msgid "Manage bus" msgstr "Gérer le bus" -#: apps/wei/views.py:393 +#: apps/wei/views.py:394 msgid "Create new team" msgstr "Créer une nouvelle équipe" -#: apps/wei/views.py:433 +#: apps/wei/views.py:434 msgid "Update team" msgstr "Modifier l'équipe" -#: apps/wei/views.py:464 +#: apps/wei/views.py:465 msgid "Manage WEI team" msgstr "Gérer l'équipe WEI" -#: apps/wei/views.py:486 +#: apps/wei/views.py:487 msgid "Register first year student to the WEI" msgstr "Inscrire un 1A au WEI" -#: apps/wei/views.py:539 apps/wei/views.py:627 +#: apps/wei/views.py:545 apps/wei/views.py:638 msgid "This user is already registered to this WEI." msgstr "Cette personne est déjà inscrite au WEI." -#: apps/wei/views.py:544 +#: apps/wei/views.py:550 msgid "" "This user can't be in her/his first year since he/she has already " "participated to a WEI." @@ -3036,27 +3038,27 @@ msgstr "" "Cet utilisateur ne peut pas être en première année puisqu'il a déjà " "participé à un WEI." -#: apps/wei/views.py:561 +#: apps/wei/views.py:567 msgid "Register old student to the WEI" msgstr "Inscrire un 2A+ au WEI" -#: apps/wei/views.py:611 apps/wei/views.py:700 +#: apps/wei/views.py:622 apps/wei/views.py:713 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:657 +#: apps/wei/views.py:668 msgid "Update WEI Registration" msgstr "Modifier l'inscription WEI" -#: apps/wei/views.py:761 +#: apps/wei/views.py:774 msgid "Delete WEI registration" msgstr "Supprimer l'inscription WEI" -#: apps/wei/views.py:772 +#: apps/wei/views.py:785 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:791 +#: apps/wei/views.py:803 msgid "Validate WEI registration" msgstr "Valider l'inscription WEI" From 81e708a7e38fd1c72e638eecc9e98f218d3a6b42 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 11 Sep 2021 14:20:38 +0200 Subject: [PATCH 04/11] [WEI] Fix registration update Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/registration.py | 3 ++- apps/wei/views.py | 28 ++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/apps/wei/forms/registration.py b/apps/wei/forms/registration.py index 6bad1dab..7a7033a7 100644 --- a/apps/wei/forms/registration.py +++ b/apps/wei/forms/registration.py @@ -118,7 +118,8 @@ class WEIMembershipForm(forms.ModelForm): def clean(self): cleaned_data = super().clean() - if cleaned_data["team"] is not None and cleaned_data["team"].bus != cleaned_data["bus"]: + if 'team' in cleaned_data and cleaned_data["team"] is not None \ + and cleaned_data["team"].bus != cleaned_data["bus"]: self.add_error('bus', _("This team doesn't belong to the given bus.")) return cleaned_data diff --git a/apps/wei/views.py b/apps/wei/views.py index 31e03260..7fedcf97 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -680,17 +680,8 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update context["club"] = self.object.wei if self.object.is_validated: - membership_form = WEIMembershipForm(instance=self.object.membership, - data=self.request.POST if self.request.POST else None) - for field_name, field in membership_form.fields.items(): - if not PermissionBackend.check_perm( - self.request, "wei.change_membership_" + field_name, self.object.membership): - field.widget = HiddenInput() - del membership_form.fields["credit_type"] - del membership_form.fields["credit_amount"] - del membership_form.fields["first_name"] - del membership_form.fields["last_name"] - del membership_form.fields["bank"] + membership_form = self.get_membership_form(instance=self.object.membership, + data=self.request.POST) context["membership_form"] = membership_form elif not self.object.first_year and PermissionBackend.check_perm( self.request, "wei.change_weiregistration_information_json", self.object): @@ -722,11 +713,24 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update del form.fields["information_json"] return form + def get_membership_form(self, data=None, instance=None): + membership_form = WEIMembershipForm(data if data else None, instance=instance) + del membership_form.fields["credit_type"] + del membership_form.fields["credit_amount"] + del membership_form.fields["first_name"] + del membership_form.fields["last_name"] + del membership_form.fields["bank"] + for field_name, field in list(membership_form.fields.items()): + if not PermissionBackend.check_perm( + self.request, "wei.change_weimembership_" + field_name, self.object.membership): + del membership_form.fields[field_name] + return membership_form + @transaction.atomic def form_valid(self, form): # If the membership is already validated, then we update the bus and the team (and the roles) if form.instance.is_validated: - membership_form = WEIMembershipForm(self.request.POST, instance=form.instance.membership) + membership_form = self.get_membership_form(self.request.POST, form.instance.membership) if not membership_form.is_valid(): return self.form_invalid(form) membership_form.save() From 61feac13c7bb3f2e309562eb30c231757635dcee Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 11 Sep 2021 19:16:34 +0200 Subject: [PATCH 05/11] [WEI] Add page that display information about the algorithm result Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/registration.py | 3 +- apps/wei/tables.py | 94 +++++++++++++++++ apps/wei/templates/wei/1A_list.html | 20 ++++ apps/wei/templates/wei/weiclub_detail.html | 4 + apps/wei/urls.py | 3 +- apps/wei/views.py | 26 ++++- locale/fr/LC_MESSAGES/django.po | 112 +++++++++++++-------- 7 files changed, 217 insertions(+), 45 deletions(-) create mode 100644 apps/wei/templates/wei/1A_list.html diff --git a/apps/wei/forms/registration.py b/apps/wei/forms/registration.py index 7a7033a7..e934fb50 100644 --- a/apps/wei/forms/registration.py +++ b/apps/wei/forms/registration.py @@ -48,8 +48,7 @@ class WEIRegistrationForm(forms.ModelForm): 'placeholder': 'Nom ...', }, ), - "birth_date": DatePickerInput(options={'defaultDate': '2000-01-01', - 'minDate': '1900-01-01', + "birth_date": DatePickerInput(options={'minDate': '1900-01-01', 'maxDate': '2100-01-01'}), } diff --git a/apps/wei/tables.py b/apps/wei/tables.py index 0f48dcc1..2565bcbc 100644 --- a/apps/wei/tables.py +++ b/apps/wei/tables.py @@ -4,6 +4,7 @@ from datetime import date import django_tables2 as tables +from django.db.models import Q from django.urls import reverse_lazy from django.utils.html import format_html from django.utils.translation import gettext_lazy as _ @@ -169,6 +170,36 @@ class WEIMembershipTable(tables.Table): } +class WEIRegistration1ATable(tables.Table): + user = tables.LinkColumn( + 'wei:wei_update_registration', + args=[A('pk')], + ) + + preferred_bus = tables.Column( + verbose_name=_('preferred bus').capitalize, + accessor='pk', + orderable=False, + ) + + def render_preferred_bus(self, record): + information = record.information + return information['selected_bus_name'] if 'selected_bus_name' in information else "—" + + class Meta: + attrs = { + 'class': 'table table-condensed table-striped table-hover' + } + model = WEIMembership + template_name = 'django_tables2/bootstrap4.html' + fields = ('user', 'user__last_name', 'user__first_name', 'gender', + 'user__profile__department', 'preferred_bus', ) + row_attrs = { + 'class': 'table-row', + 'id': lambda record: "row-" + str(record.pk), + } + + class BusTable(tables.Table): name = tables.LinkColumn( 'wei:manage_bus', @@ -245,3 +276,66 @@ class BusTeamTable(tables.Table): 'id': lambda record: "row-" + str(record.pk), 'data-href': lambda record: reverse_lazy('wei:manage_bus_team', args=(record.pk, )) } + + +class BusRepartitionTable(tables.Table): + name = tables.Column( + verbose_name=_("name").capitalize, + accessor='name', + ) + + suggested_first_year = tables.Column( + verbose_name=_("suggested first year").capitalize, + accessor='pk', + orderable=False, + ) + + validated_first_year = tables.Column( + verbose_name=_("validated first year").capitalize, + accessor='pk', + orderable=False, + ) + + validated_staff = tables.Column( + verbose_name=_("validated staff").capitalize, + accessor='pk', + orderable=False, + ) + + size = tables.Column( + verbose_name=_("seat count in the bus").capitalize, + accessor='size', + ) + + free_seats = tables.Column( + verbose_name=_("free seats").capitalize, + accessor='pk', + orderable=False, + ) + + def render_suggested_first_year(self, record): + registrations = WEIRegistration.objects.filter(Q(membership__isnull=True) | Q(membership__bus__isnull=True), + first_year=True, wei=record.wei) + registrations = [r for r in registrations if 'selected_bus_pk' in r.information] + return sum(1 for r in registrations if r.information['selected_bus_pk'] == record.pk) + + def render_validated_first_year(self, record): + return WEIRegistration.objects.filter(first_year=True, membership__bus=record).count() + + def render_validated_staff(self, record): + return WEIRegistration.objects.filter(first_year=False, membership__bus=record).count() + + def render_free_seats(self, record): + return record.size - self.render_validated_staff(record) - self.render_validated_first_year(record) + + class Meta: + attrs = { + 'class': 'table table-condensed table-striped table-hover' + } + models = Bus + template_name = 'django_tables2/bootstrap4.html' + fields = ('name', ) + row_attrs = { + 'class': 'table-row', + 'id': lambda record: "row-" + str(record.pk), + } diff --git a/apps/wei/templates/wei/1A_list.html b/apps/wei/templates/wei/1A_list.html new file mode 100644 index 00000000..044db80e --- /dev/null +++ b/apps/wei/templates/wei/1A_list.html @@ -0,0 +1,20 @@ +{% extends "wei/base.html" %} + +{% load i18n %} +{% load render_table from django_tables2 %} + +{% block profile_content %} +
+
+

{% trans "Attribute first year members into buses" %}

+
+ +
+ {% render_table bus_repartition_table %} +
+ {% trans "Start attribution!" %} +
+ {% render_table table %} +
+
+{% endblock %} diff --git a/apps/wei/templates/wei/weiclub_detail.html b/apps/wei/templates/wei/weiclub_detail.html index 40786add..9ffa7374 100644 --- a/apps/wei/templates/wei/weiclub_detail.html +++ b/apps/wei/templates/wei/weiclub_detail.html @@ -94,6 +94,10 @@ SPDX-License-Identifier: GPL-3.0-or-later {% endif %} + + {% if can_validate_1a or True %} + {% trans "Attribute buses" %} + {% endif %} {% endblock %} {% block extrajavascript %} diff --git a/apps/wei/urls.py b/apps/wei/urls.py index a7e8ca99..e75e3452 100644 --- a/apps/wei/urls.py +++ b/apps/wei/urls.py @@ -3,7 +3,7 @@ from django.urls import path -from .views import CurrentWEIDetailView, WEIListView, WEICreateView, WEIDetailView, WEIUpdateView,\ +from .views import CurrentWEIDetailView, WEI1AListView, WEIListView, WEICreateView, WEIDetailView, WEIUpdateView,\ WEIRegistrationsView, WEIMembershipsView, MemberListRenderView,\ BusCreateView, BusManageView, BusUpdateView, BusTeamCreateView, BusTeamManageView, BusTeamUpdateView,\ WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, WEIDeleteRegistrationView,\ @@ -24,6 +24,7 @@ urlpatterns = [ name="wei_memberships_bus_pdf"), path('detail//memberships/pdf///', MemberListRenderView.as_view(), name="wei_memberships_team_pdf"), + path('bus-1A-list//', WEI1AListView.as_view(), name="wei_1A_list"), path('add-bus//', BusCreateView.as_view(), name="add_bus"), path('manage-bus//', BusManageView.as_view(), name="manage_bus"), path('update-bus//', BusUpdateView.as_view(), name="update_bus"), diff --git a/apps/wei/views.py b/apps/wei/views.py index 7fedcf97..baf4dba9 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -13,7 +13,6 @@ from django.core.exceptions import PermissionDenied from django.db import transaction from django.db.models import Q, Count from django.db.models.functions.text import Lower -from django.forms import HiddenInput from django.http import HttpResponse from django.shortcuts import redirect from django.template.loader import render_to_string @@ -34,7 +33,8 @@ from .forms.registration import WEIChooseBusForm from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembership1AForm, \ WEIMembershipForm, CurrentSurvey -from .tables import WEITable, WEIRegistrationTable, BusTable, BusTeamTable, WEIMembershipTable +from .tables import BusRepartitionTable, BusTable, BusTeamTable, WEITable, WEIRegistrationTable, \ + WEIRegistration1ATable, WEIMembershipTable class CurrentWEIDetailView(LoginRequiredMixin, RedirectView): @@ -1160,3 +1160,25 @@ class MemberListRenderView(LoginRequiredMixin, View): shutil.rmtree(tmp_dir) return response + + +class WEI1AListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableView): + model = WEIRegistration + template_name = "wei/1A_list.html" + table_class = WEIRegistration1ATable + extra_context = {"title": _("Attribute buses to first year members")} + + def dispatch(self, request, *args, **kwargs): + self.club = WEIClub.objects.get(pk=self.kwargs["pk"]) + return super().dispatch(request, *args, **kwargs) + + def get_queryset(self, filter_permissions=True, **kwargs): + qs = super().get_queryset(filter_permissions, **kwargs) + qs = qs.filter(first_year=True) + return qs + + 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()) + return context diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index b8d38275..4adb6f95 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-10 22:08+0200\n" +"POT-Creation-Date: 2021-09-11 19:00+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/models.py:66 apps/wei/models.py:123 apps/wei/tables.py:283 #: apps/wei/templates/wei/base.html:26 #: apps/wei/templates/wei/weimembership_form.html:14 msgid "name" @@ -257,14 +257,14 @@ msgstr "Type" #: apps/activity/tables.py:82 apps/member/forms.py:186 #: apps/registration/forms.py:90 apps/treasury/forms.py:131 -#: apps/wei/forms/registration.py:105 +#: apps/wei/forms/registration.py:104 msgid "Last name" msgstr "Nom de famille" #: apps/activity/tables.py:84 apps/member/forms.py:191 #: apps/note/templates/note/transaction_form.html:134 #: apps/registration/forms.py:95 apps/treasury/forms.py:133 -#: apps/wei/forms/registration.py:110 +#: apps/wei/forms/registration.py:109 msgid "First name" msgstr "Prénom" @@ -461,7 +461,7 @@ msgstr "créer" #: apps/logs/models.py:65 apps/note/tables.py:165 apps/note/tables.py:201 #: apps/permission/models.py:127 apps/treasury/tables.py:38 -#: apps/wei/tables.py:73 +#: apps/wei/tables.py:74 msgid "delete" msgstr "supprimer" @@ -508,7 +508,7 @@ msgstr "rôles" msgid "fee" msgstr "cotisation" -#: apps/member/apps.py:14 apps/wei/tables.py:196 apps/wei/tables.py:227 +#: apps/member/apps.py:14 apps/wei/tables.py:227 apps/wei/tables.py:258 msgid "member" msgstr "adhérent" @@ -554,12 +554,12 @@ 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:172 apps/registration/forms.py:77 -#: apps/wei/forms/registration.py:92 +#: apps/wei/forms/registration.py:91 msgid "Credit type" msgstr "Type de rechargement" #: apps/member/forms.py:173 apps/registration/forms.py:78 -#: apps/wei/forms/registration.py:93 +#: apps/wei/forms/registration.py:92 msgid "No credit" msgstr "Pas de rechargement" @@ -568,13 +568,13 @@ msgid "You can credit the note of the user." msgstr "Vous pouvez créditer la note de l'utilisateur avant l'adhésion." #: apps/member/forms.py:179 apps/registration/forms.py:83 -#: apps/wei/forms/registration.py:98 +#: apps/wei/forms/registration.py:97 msgid "Credit amount" msgstr "Montant à créditer" #: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:140 #: apps/registration/forms.py:100 apps/treasury/forms.py:135 -#: apps/wei/forms/registration.py:115 +#: apps/wei/forms/registration.py:114 msgid "Bank" msgstr "Banque" @@ -1188,7 +1188,7 @@ msgstr "Modifier le club" msgid "Add new member to the club" msgstr "Ajouter un nouveau membre au club" -#: apps/member/views.py:642 apps/wei/views.py:952 +#: apps/member/views.py:642 apps/wei/views.py:956 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -1494,8 +1494,8 @@ msgstr "" "mode de paiement et un utilisateur ou un club" #: apps/note/models/transactions.py:355 apps/note/models/transactions.py:358 -#: apps/note/models/transactions.py:361 apps/wei/views.py:957 -#: apps/wei/views.py:961 +#: apps/note/models/transactions.py:361 apps/wei/views.py:961 +#: apps/wei/views.py:965 msgid "This field is required." msgstr "Ce champ est requis." @@ -1530,7 +1530,7 @@ msgstr "Pas de motif spécifié" #: apps/note/tables.py:169 apps/note/tables.py:203 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:74 apps/wei/tables.py:117 +#: 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 @@ -1539,7 +1539,7 @@ msgid "Delete" msgstr "Supprimer" #: apps/note/tables.py:197 apps/note/templates/note/conso_form.html:132 -#: apps/wei/tables.py:48 apps/wei/tables.py:49 +#: apps/wei/tables.py:49 apps/wei/tables.py:50 #: apps/wei/templates/wei/base.html:89 #: apps/wei/templates/wei/bus_detail.html:20 #: apps/wei/templates/wei/busteam_detail.html:20 @@ -2437,7 +2437,7 @@ msgstr "" "demande de crédit." #: apps/treasury/templates/treasury/sogecredit_detail.html:63 -#: apps/wei/tables.py:59 apps/wei/tables.py:101 +#: apps/wei/tables.py:60 apps/wei/tables.py:102 msgid "Validate" msgstr "Valider" @@ -2516,12 +2516,12 @@ msgstr "" "L'utilisateur sélectionné n'est pas validé. Merci de d'abord valider son " "compte." -#: apps/wei/forms/registration.py:60 apps/wei/models.py:118 +#: apps/wei/forms/registration.py:59 apps/wei/models.py:118 #: apps/wei/models.py:315 msgid "bus" msgstr "bus" -#: apps/wei/forms/registration.py:61 +#: apps/wei/forms/registration.py:60 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." @@ -2530,11 +2530,11 @@ msgstr "" "attribuer un bus et une équipe, en particulier si vous êtes un électron " "libre." -#: apps/wei/forms/registration.py:68 +#: apps/wei/forms/registration.py:67 msgid "Team" msgstr "Équipe" -#: apps/wei/forms/registration.py:70 +#: apps/wei/forms/registration.py:69 msgid "" "Leave this field empty if you won't be in a team (staff, bus chief, free " "electron)" @@ -2542,12 +2542,12 @@ 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:76 apps/wei/forms/registration.py:86 +#: apps/wei/forms/registration.py:75 apps/wei/forms/registration.py:85 #: apps/wei/models.py:153 msgid "WEI Roles" msgstr "Rôles au WEI" -#: apps/wei/forms/registration.py:77 +#: apps/wei/forms/registration.py:76 msgid "Select the roles that you are interested in." msgstr "Sélectionnez les rôles qui vous intéressent." @@ -2571,7 +2571,7 @@ msgstr "début" msgid "date end" msgstr "fin" -#: apps/wei/models.py:70 +#: apps/wei/models.py:70 apps/wei/tables.py:306 msgid "seat count in the bus" msgstr "nombre de sièges dans le bus" @@ -2705,11 +2705,11 @@ msgstr "Adhésion au WEI" msgid "WEI memberships" msgstr "Adhésions au WEI" -#: apps/wei/tables.py:104 +#: apps/wei/tables.py:105 msgid "The user does not have enough money." msgstr "L'utilisateur n'a pas assez d'argent." -#: apps/wei/tables.py:107 +#: apps/wei/tables.py:108 msgid "" "The user is in first year. You may validate the credit, the algorithm will " "run later." @@ -2717,27 +2717,55 @@ msgstr "" "L'utilisateur est en première année, vous pouvez valider le crédit, " "l'algorithme tournera plus tard." -#: apps/wei/tables.py:110 +#: apps/wei/tables.py:111 msgid "The user has enough money, you can validate the registration." msgstr "L'utilisateur a assez d'argent, l'inscription est possible." -#: apps/wei/tables.py:142 +#: apps/wei/tables.py:143 msgid "Year" msgstr "Année" -#: apps/wei/tables.py:180 apps/wei/templates/wei/bus_detail.html:32 +#: apps/wei/tables.py:180 apps/wei/templates/wei/weimembership_form.html:102 +msgid "preferred bus" +msgstr "bus préféré" + +#: apps/wei/tables.py:211 apps/wei/templates/wei/bus_detail.html:32 #: apps/wei/templates/wei/busteam_detail.html:50 msgid "Teams" msgstr "Équipes" -#: apps/wei/tables.py:189 apps/wei/tables.py:230 +#: apps/wei/tables.py:220 apps/wei/tables.py:261 msgid "Members count" msgstr "Nombre de membres" -#: apps/wei/tables.py:196 apps/wei/tables.py:227 +#: apps/wei/tables.py:227 apps/wei/tables.py:258 msgid "members" msgstr "adhérents" +#: apps/wei/tables.py:288 +msgid "suggested first year" +msgstr "1A suggérés" + +#: apps/wei/tables.py:294 +msgid "validated first year" +msgstr "1A validés" + +#: apps/wei/tables.py:300 +msgid "validated staff" +msgstr "2A+ validés" + +#: apps/wei/tables.py:311 +msgid "free seats" +msgstr "sièges libres" + +#: apps/wei/templates/wei/1A_list.html:9 +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!" +msgstr "Démarrer l'attribution !" + #: apps/wei/templates/wei/base.html:44 msgid "WEI fee (paid students)" msgstr "Prix du WEI (élèves)" @@ -2787,8 +2815,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:1007 -#: apps/wei/views.py:1062 apps/wei/views.py:1072 +#: apps/wei/templates/wei/survey_end.html:11 apps/wei/views.py:1011 +#: apps/wei/views.py:1066 apps/wei/views.py:1076 msgid "Survey WEI" msgstr "Questionnaire WEI" @@ -2829,6 +2857,10 @@ msgstr "Membres du WEI" msgid "Unvalidated registrations" msgstr "Inscriptions non validées" +#: apps/wei/templates/wei/weiclub_detail.html:99 +msgid "Attribute buses" +msgstr "Répartition dans les bus" + #: apps/wei/templates/wei/weiclub_list.html:14 apps/wei/views.py:78 msgid "Create WEI" msgstr "Créer un WEI" @@ -2865,10 +2897,6 @@ msgstr "L'algorithme n'a pas été exécuté." msgid "caution check given" msgstr "chèque de caution donné" -#: apps/wei/templates/wei/weimembership_form.html:102 -msgid "preferred bus" -msgstr "bus préféré" - #: apps/wei/templates/wei/weimembership_form.html:105 msgid "preferred team" msgstr "équipe préférée" @@ -3042,7 +3070,7 @@ msgstr "" msgid "Register old student to the WEI" msgstr "Inscrire un 2A+ au WEI" -#: apps/wei/views.py:622 apps/wei/views.py:713 +#: apps/wei/views.py:622 apps/wei/views.py:704 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." @@ -3050,18 +3078,22 @@ msgstr "Vous avez déjà ouvert un compte auprès de la société générale." msgid "Update WEI Registration" msgstr "Modifier l'inscription WEI" -#: apps/wei/views.py:774 +#: apps/wei/views.py:778 msgid "Delete WEI registration" msgstr "Supprimer l'inscription WEI" -#: apps/wei/views.py:785 +#: apps/wei/views.py:789 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:803 +#: apps/wei/views.py:807 msgid "Validate WEI registration" msgstr "Valider l'inscription WEI" +#: apps/wei/views.py:1169 +msgid "Attribute buses to first year members" +msgstr "Répartir les 1A dans les bus" + #: note_kfet/settings/base.py:161 msgid "German" msgstr "Allemand" From bb9a0a2593130dfef3fa202c3e09f34d0b320e0b Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 12 Sep 2021 19:49:22 +0200 Subject: [PATCH 06/11] [WEI] UI to attribute buses for 1A Signed-off-by: Yohann D'ANELLO --- apps/wei/tables.py | 2 +- apps/wei/templates/wei/attribute_bus_1A.html | 60 +++++++++++++++++++ .../wei/templates/wei/weimembership_form.html | 2 +- apps/wei/urls.py | 14 ++--- apps/wei/views.py | 17 ++++++ locale/fr/LC_MESSAGES/django.po | 36 +++++++++-- 6 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 apps/wei/templates/wei/attribute_bus_1A.html diff --git a/apps/wei/tables.py b/apps/wei/tables.py index 2565bcbc..d3c4e14f 100644 --- a/apps/wei/tables.py +++ b/apps/wei/tables.py @@ -172,7 +172,7 @@ class WEIMembershipTable(tables.Table): class WEIRegistration1ATable(tables.Table): user = tables.LinkColumn( - 'wei:wei_update_registration', + 'wei:wei_bus_1A', args=[A('pk')], ) diff --git a/apps/wei/templates/wei/attribute_bus_1A.html b/apps/wei/templates/wei/attribute_bus_1A.html new file mode 100644 index 00000000..dbfb6494 --- /dev/null +++ b/apps/wei/templates/wei/attribute_bus_1A.html @@ -0,0 +1,60 @@ +{% extends "wei/base.html" %} + +{% load i18n %} + +{% block profile_content %} +
+
+

{% trans "Bus attribution" %}

+
+ +
+
+
{% trans 'user'|capfirst %}
+
{{ object.user }}
+ +
{% trans 'last name'|capfirst %}
+
{{ object.user.last_name }}
+ +
{% trans 'first name'|capfirst %}
+
{{ object.user.first_name }}
+ +
{% trans 'gender'|capfirst %}
+
{{ object.get_gender_display }}
+ +
{% trans 'department'|capfirst %}
+
{{ object.user.profile.get_department_display }}
+ +
{% trans 'health issues'|capfirst %}
+
{{ object.health_issues|default:"—" }}
+ +
{% trans 'suggested bus'|capfirst %}
+
{{ survey.information.selected_bus_name }}
+
+ +
+
+ +
+
+
+ {% for key, value in survey.registration.information.items %} +
{{ key }}
+
{{ value }}
+ {% endfor %} +
+
+
+ +
+ + {% for bus, score in survey.ordered_buses %} + + {% endfor %} + + {% trans "Back to main list" %} +
+
+{% endblock %} diff --git a/apps/wei/templates/wei/weimembership_form.html b/apps/wei/templates/wei/weimembership_form.html index 1225175d..7d1059b7 100644 --- a/apps/wei/templates/wei/weimembership_form.html +++ b/apps/wei/templates/wei/weimembership_form.html @@ -53,7 +53,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{{ registration.first_year|yesno }}
{% trans 'gender'|capfirst %}
-
{{ registration.gender }}
+
{{ registration.get_gender_display }}
{% trans 'clothing cut'|capfirst %}
{{ registration.clothing_cut }}
diff --git a/apps/wei/urls.py b/apps/wei/urls.py index e75e3452..ff6e88c5 100644 --- a/apps/wei/urls.py +++ b/apps/wei/urls.py @@ -3,12 +3,11 @@ from django.urls import path -from .views import CurrentWEIDetailView, WEI1AListView, WEIListView, WEICreateView, WEIDetailView, WEIUpdateView,\ - WEIRegistrationsView, WEIMembershipsView, MemberListRenderView,\ - BusCreateView, BusManageView, BusUpdateView, BusTeamCreateView, BusTeamManageView, BusTeamUpdateView,\ - WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, WEIDeleteRegistrationView,\ - WEIValidateRegistrationView, WEISurveyView, WEISurveyEndView, WEIClosedView - +from .views import CurrentWEIDetailView, WEI1AListView, WEIListView, WEICreateView, WEIDetailView, WEIUpdateView, \ + WEIRegistrationsView, WEIMembershipsView, MemberListRenderView, \ + BusCreateView, BusManageView, BusUpdateView, BusTeamCreateView, BusTeamManageView, BusTeamUpdateView, \ + WEIAttributeBus1AView, WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, \ + WEIDeleteRegistrationView, WEIValidateRegistrationView, WEISurveyView, WEISurveyEndView, WEIClosedView app_name = 'wei' urlpatterns = [ @@ -24,7 +23,7 @@ urlpatterns = [ name="wei_memberships_bus_pdf"), path('detail//memberships/pdf///', MemberListRenderView.as_view(), name="wei_memberships_team_pdf"), - path('bus-1A-list//', WEI1AListView.as_view(), name="wei_1A_list"), + path('bus-1A/list//', WEI1AListView.as_view(), name="wei_1A_list"), path('add-bus//', BusCreateView.as_view(), name="add_bus"), path('manage-bus//', BusManageView.as_view(), name="manage_bus"), path('update-bus//', BusUpdateView.as_view(), name="update_bus"), @@ -41,4 +40,5 @@ urlpatterns = [ path('survey//', WEISurveyView.as_view(), name="wei_survey"), path('survey//end/', WEISurveyEndView.as_view(), name="wei_survey_end"), path('detail//closed/', WEIClosedView.as_view(), name="wei_closed"), + path('bus-1A//', WEIAttributeBus1AView.as_view(), name="wei_bus_1A"), ] diff --git a/apps/wei/views.py b/apps/wei/views.py index baf4dba9..2b3a752c 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -1182,3 +1182,20 @@ class WEI1AListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableView): context['club'] = self.club context['bus_repartition_table'] = BusRepartitionTable(Bus.objects.filter(wei=self.club, size__gt=0).all()) return context + + +class WEIAttributeBus1AView(ProtectQuerysetMixin, DetailView): + model = WEIRegistration + template_name = "wei/attribute_bus_1A.html" + extra_context = {"title": _("Attribute bus")} + + def get_queryset(self, filter_permissions=True, **kwargs): + qs = super().get_queryset(filter_permissions, **kwargs) + qs = qs.filter(first_year=True) + return qs + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['club'] = self.object.wei + context['survey'] = CurrentSurvey(self.object) + return context diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 4adb6f95..7d71955d 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-11 19:00+0200\n" +"POT-Creation-Date: 2021-09-12 19:30+0200\n" "PO-Revision-Date: 2020-11-16 20:02+0000\n" "Last-Translator: Yohann D'ANELLO \n" "Language-Team: French \n" @@ -112,7 +112,8 @@ 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/survey.html:15 +#: apps/wei/models.py:165 apps/wei/templates/wei/attribute_bus_1A.html:13 +#: apps/wei/templates/wei/survey.html:15 msgid "user" msgstr "utilisateur" @@ -204,6 +205,7 @@ msgstr "La note est en négatif." #: apps/activity/models.py:240 #: apps/treasury/templates/treasury/sogecredit_detail.html:14 +#: apps/wei/templates/wei/attribute_bus_1A.html:16 msgid "last name" msgstr "nom de famille" @@ -211,6 +213,7 @@ msgstr "nom de famille" #: apps/member/templates/member/includes/profile_info.html:4 #: apps/registration/templates/registration/future_profile_detail.html:16 #: apps/treasury/templates/treasury/sogecredit_detail.html:17 +#: apps/wei/templates/wei/attribute_bus_1A.html:19 #: apps/wei/templates/wei/weimembership_form.html:14 msgid "first name" msgstr "prénom" @@ -620,7 +623,8 @@ msgstr "section" msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" -#: apps/member/models.py:54 apps/wei/templates/wei/weimembership_form.html:32 +#: apps/member/models.py:54 apps/wei/templates/wei/attribute_bus_1A.html:25 +#: apps/wei/templates/wei/weimembership_form.html:32 msgid "department" msgstr "département" @@ -2637,7 +2641,8 @@ msgstr "Femme" msgid "Non binary" msgstr "Non-binaire" -#: apps/wei/models.py:196 apps/wei/templates/wei/weimembership_form.html:55 +#: apps/wei/models.py:196 apps/wei/templates/wei/attribute_bus_1A.html:22 +#: apps/wei/templates/wei/weimembership_form.html:55 msgid "gender" msgstr "genre" @@ -2649,7 +2654,8 @@ msgstr "coupe de vêtement" msgid "clothing size" msgstr "taille de vêtement" -#: apps/wei/models.py:224 apps/wei/templates/wei/weimembership_form.html:67 +#: apps/wei/models.py:224 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é" @@ -2766,6 +2772,22 @@ msgstr "Attribuer les 1A dans les bus" 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:31 +msgid "suggested bus" +msgstr "bus suggéré" + +#: apps/wei/templates/wei/attribute_bus_1A.html:37 +msgid "View raw survey information" +msgstr "Voir les informations brutes du sondage" + +#: apps/wei/templates/wei/attribute_bus_1A.html:57 +msgid "Back to main list" +msgstr "Retour à la liste principale" + #: apps/wei/templates/wei/base.html:44 msgid "WEI fee (paid students)" msgstr "Prix du WEI (élèves)" @@ -3094,6 +3116,10 @@ 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 +msgid "Attribute bus" +msgstr "Attribuer un bus" + #: note_kfet/settings/base.py:161 msgid "German" msgstr "Allemand" From f6d042c99897f85e0ed11158fe5138502e29b510 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 12 Sep 2021 20:10:50 +0200 Subject: [PATCH 07/11] [WEI] Attribute bus to people that paid their registration Signed-off-by: Yohann D'ANELLO --- apps/wei/tables.py | 2 +- apps/wei/views.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/wei/tables.py b/apps/wei/tables.py index d3c4e14f..d9e095c9 100644 --- a/apps/wei/tables.py +++ b/apps/wei/tables.py @@ -193,7 +193,7 @@ class WEIRegistration1ATable(tables.Table): model = WEIMembership template_name = 'django_tables2/bootstrap4.html' fields = ('user', 'user__last_name', 'user__first_name', 'gender', - 'user__profile__department', 'preferred_bus', ) + 'user__profile__department', 'preferred_bus', 'membership__bus', ) row_attrs = { 'class': 'table-row', 'id': lambda record: "row-" + str(record.pk), diff --git a/apps/wei/views.py b/apps/wei/views.py index 2b3a752c..d0601c63 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -1174,7 +1174,8 @@ class WEI1AListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableView): def get_queryset(self, filter_permissions=True, **kwargs): qs = super().get_queryset(filter_permissions, **kwargs) - qs = qs.filter(first_year=True) + qs = qs.filter(first_year=True, membership__isnull=False) + qs = qs.order_by('membership__bus') return qs def get_context_data(self, **kwargs): From 944bb127e285dcfa3c7721bdf36d3cca2a71d4d3 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 12 Sep 2021 22:29:57 +0200 Subject: [PATCH 08/11] [WEI] New UI is working Signed-off-by: Yohann D'ANELLO --- apps/wei/templates/wei/1A_list.html | 2 +- apps/wei/templates/wei/attribute_bus_1A.html | 30 +++++++++++++++++++- apps/wei/urls.py | 3 +- apps/wei/views.py | 16 +++++++++-- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/apps/wei/templates/wei/1A_list.html b/apps/wei/templates/wei/1A_list.html index 044db80e..d9b82937 100644 --- a/apps/wei/templates/wei/1A_list.html +++ b/apps/wei/templates/wei/1A_list.html @@ -12,7 +12,7 @@
{% render_table bus_repartition_table %}
- {% trans "Start attribution!" %} + {% trans "Start attribution!" %}
{% render_table table %}
diff --git a/apps/wei/templates/wei/attribute_bus_1A.html b/apps/wei/templates/wei/attribute_bus_1A.html index dbfb6494..32cc27dd 100644 --- a/apps/wei/templates/wei/attribute_bus_1A.html +++ b/apps/wei/templates/wei/attribute_bus_1A.html @@ -49,7 +49,7 @@
{% for bus, score in survey.ordered_buses %} - {% endfor %} @@ -58,3 +58,31 @@ {% endblock %} + +{% block extrajavascript %} + +{% endblock %} diff --git a/apps/wei/urls.py b/apps/wei/urls.py index ff6e88c5..fb497216 100644 --- a/apps/wei/urls.py +++ b/apps/wei/urls.py @@ -6,7 +6,7 @@ from django.urls import path from .views import CurrentWEIDetailView, WEI1AListView, WEIListView, WEICreateView, WEIDetailView, WEIUpdateView, \ WEIRegistrationsView, WEIMembershipsView, MemberListRenderView, \ BusCreateView, BusManageView, BusUpdateView, BusTeamCreateView, BusTeamManageView, BusTeamUpdateView, \ - WEIAttributeBus1AView, WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, \ + WEIAttributeBus1AView, WEIAttributeBus1ANextView, WEIRegister1AView, WEIRegister2AView, WEIUpdateRegistrationView, \ WEIDeleteRegistrationView, WEIValidateRegistrationView, WEISurveyView, WEISurveyEndView, WEIClosedView app_name = 'wei' @@ -41,4 +41,5 @@ urlpatterns = [ path('survey//end/', WEISurveyEndView.as_view(), name="wei_survey_end"), path('detail//closed/', WEIClosedView.as_view(), name="wei_closed"), path('bus-1A//', WEIAttributeBus1AView.as_view(), name="wei_bus_1A"), + path('bus-1A/next//', WEIAttributeBus1ANextView.as_view(), name="wei_bus_1A_next"), ] diff --git a/apps/wei/views.py b/apps/wei/views.py index d0601c63..0f08e06f 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -13,7 +13,7 @@ from django.core.exceptions import PermissionDenied from django.db import transaction from django.db.models import Q, Count from django.db.models.functions.text import Lower -from django.http import HttpResponse +from django.http import HttpResponse, Http404 from django.shortcuts import redirect from django.template.loader import render_to_string from django.urls import reverse_lazy @@ -1175,7 +1175,7 @@ class WEI1AListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableView): def get_queryset(self, filter_permissions=True, **kwargs): qs = super().get_queryset(filter_permissions, **kwargs) qs = qs.filter(first_year=True, membership__isnull=False) - qs = qs.order_by('membership__bus') + qs = qs.order_by('-membership__bus') return qs def get_context_data(self, **kwargs): @@ -1200,3 +1200,15 @@ class WEIAttributeBus1AView(ProtectQuerysetMixin, DetailView): context['club'] = self.object.wei context['survey'] = CurrentSurvey(self.object) return context + + +class WEIAttributeBus1ANextView(LoginRequiredMixin, RedirectView): + def get_redirect_url(self, *args, **kwargs): + wei = WEIClub.objects.filter(pk=self.kwargs['pk']) + if not wei.exists(): + raise Http404 + wei = wei.get() + qs = WEIRegistration.objects.filter(wei=wei, membership__isnull=False, membership__bus__isnull=True) + if qs.exists(): + return reverse_lazy('wei:wei_bus_1A', args=(qs.first().pk, )) + return reverse_lazy('wei_1A_list', args=(wei.pk, )) From 600ba15faa13e7aa71a97fca24557ee2931002ae Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 13 Sep 2021 19:02:54 +0200 Subject: [PATCH 09/11] [WEI] Display suggested 1A number in a bus in repartition view Signed-off-by: Yohann D'ANELLO --- apps/wei/models.py | 8 ++++++++ apps/wei/templates/wei/attribute_bus_1A.html | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/wei/models.py b/apps/wei/models.py index 7ab56f57..6b7272b4 100644 --- a/apps/wei/models.py +++ b/apps/wei/models.py @@ -7,6 +7,7 @@ from datetime import date from django.conf import settings from django.contrib.auth.models import User from django.db import models +from django.db.models import Q from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField from member.models import Club, Membership @@ -98,6 +99,13 @@ class Bus(models.Model): """ self.information_json = json.dumps(information, indent=2) + @property + def suggested_first_year(self): + registrations = WEIRegistration.objects.filter(Q(membership__isnull=True) | Q(membership__bus__isnull=True), + first_year=True, wei=self.wei) + registrations = [r for r in registrations if 'selected_bus_pk' in r.information] + return sum(1 for r in registrations if r.information['selected_bus_pk'] == self.pk) + def __str__(self): return self.name diff --git a/apps/wei/templates/wei/attribute_bus_1A.html b/apps/wei/templates/wei/attribute_bus_1A.html index 32cc27dd..3305981b 100644 --- a/apps/wei/templates/wei/attribute_bus_1A.html +++ b/apps/wei/templates/wei/attribute_bus_1A.html @@ -50,7 +50,7 @@ {% for bus, score in survey.ordered_buses %} {% endfor %} From ef118c2445b1ab132cedecb4a6fe5cece16402d2 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 13 Sep 2021 19:02:54 +0200 Subject: [PATCH 10/11] [WEI] Avoid errors if the survey is not ended Signed-off-by: Yohann D'ANELLO --- apps/wei/tables.py | 5 ++--- apps/wei/views.py | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/wei/tables.py b/apps/wei/tables.py index d9e095c9..687f8f07 100644 --- a/apps/wei/tables.py +++ b/apps/wei/tables.py @@ -190,13 +190,12 @@ class WEIRegistration1ATable(tables.Table): attrs = { 'class': 'table table-condensed table-striped table-hover' } - model = WEIMembership + model = WEIRegistration template_name = 'django_tables2/bootstrap4.html' fields = ('user', 'user__last_name', 'user__first_name', 'gender', 'user__profile__department', 'preferred_bus', 'membership__bus', ) row_attrs = { - 'class': 'table-row', - 'id': lambda record: "row-" + str(record.pk), + 'class': lambda record: '' if 'selected_bus_pk' in record.information else 'bg-danger', } diff --git a/apps/wei/views.py b/apps/wei/views.py index 0f08e06f..23ab8485 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -1195,6 +1195,12 @@ class WEIAttributeBus1AView(ProtectQuerysetMixin, DetailView): qs = qs.filter(first_year=True) return qs + def dispatch(self, request, *args, **kwargs): + obj = self.get_object() + if 'selected_bus_pk' not in obj.information: + return redirect(reverse_lazy('wei:wei_survey', args=(obj.pk,))) + return super().dispatch(request, *args, **kwargs) + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['club'] = self.object.wei @@ -1209,6 +1215,7 @@ class WEIAttributeBus1ANextView(LoginRequiredMixin, RedirectView): raise Http404 wei = wei.get() qs = WEIRegistration.objects.filter(wei=wei, membership__isnull=False, membership__bus__isnull=True) + 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, )) From 4e3c32eb5e5729d055bceedcda04380a1dd8c093 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 13 Sep 2021 19:28:15 +0200 Subject: [PATCH 11/11] Linting Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/__init__.py | 2 +- apps/wei/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/wei/forms/__init__.py b/apps/wei/forms/__init__.py index 1125ab3c..10765752 100644 --- a/apps/wei/forms/__init__.py +++ b/apps/wei/forms/__init__.py @@ -5,6 +5,6 @@ from .registration import WEIForm, WEIRegistrationForm, WEIMembership1AForm, WEI from .surveys import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, CurrentSurvey __all__ = [ - 'WEIForm', 'WEIRegistrationForm', 'WEIMembesrhip1AForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm', + 'WEIForm', 'WEIRegistrationForm', 'WEIMembership1AForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm', 'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey', ] diff --git a/apps/wei/views.py b/apps/wei/views.py index 23ab8485..0f385d94 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -720,7 +720,7 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update del membership_form.fields["first_name"] del membership_form.fields["last_name"] del membership_form.fields["bank"] - for field_name, field in list(membership_form.fields.items()): + for field_name, _field in list(membership_form.fields.items()): if not PermissionBackend.check_perm( self.request, "wei.change_weimembership_" + field_name, self.object.membership): del membership_form.fields[field_name]