1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-09-25 10:58:17 +02:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Ehouarn
034ad9a4ce tests 2025-08-31 22:04:45 +02:00
Ehouarn
897d37f74d New informative questions 2025-08-31 21:45:09 +02:00
Ehouarn
55be3c9836 Answers to survey 2025-08-29 17:13:52 +02:00
Ehouarn
4da87872bd Survey questions 2025-08-20 22:59:37 +02:00
Ehouarn
251bb933da Signals used to ignore _no_signal 2025-08-03 21:19:44 +02:00
Ehouarn
59a502d624 Added column deposit_type to MembershipsTable 2025-08-03 01:02:06 +02:00
Ehouarn
312ab6dac4 Permissions 2025-08-03 00:41:10 +02:00
Ehouarn
cf53b480db Minor fix 2025-08-02 23:42:04 +02:00
Ehouarn
d1aa1edd09 Deposit check logic changed 2025-08-02 23:32:13 +02:00
Ehouarn
d6f9a9c5b0 Better test 2025-08-02 18:35:53 +02:00
15 changed files with 452 additions and 190 deletions

View File

@@ -16,7 +16,7 @@ def save_user_profile(instance, created, raw, **_kwargs):
def update_wei_registration_fee_on_membership_creation(sender, instance, created, **kwargs): def update_wei_registration_fee_on_membership_creation(sender, instance, created, **kwargs):
if created: if not hasattr(instance, "_no_signal") and created:
from wei.models import WEIRegistration from wei.models import WEIRegistration
if instance.club.id == 1 or instance.club.id == 2: if instance.club.id == 1 or instance.club.id == 2:
registrations = WEIRegistration.objects.filter( registrations = WEIRegistration.objects.filter(
@@ -24,14 +24,16 @@ def update_wei_registration_fee_on_membership_creation(sender, instance, created
wei__year=instance.date_start.year, wei__year=instance.date_start.year,
) )
for r in registrations: for r in registrations:
r._force_save = True
r.save() r.save()
def update_wei_registration_fee_on_club_change(sender, instance, **kwargs): def update_wei_registration_fee_on_club_change(sender, instance, **kwargs):
from wei.models import WEIRegistration from wei.models import WEIRegistration
if instance.id == 1 or instance.id == 2: if not hasattr(instance, "_no_signal") and (instance.id == 1 or instance.id == 2):
registrations = WEIRegistration.objects.filter( registrations = WEIRegistration.objects.filter(
wei__year=instance.membership_start.year, wei__year=instance.membership_start.year,
) )
for r in registrations: for r in registrations:
r._force_save = True
r.save() r.save()

View File

@@ -1391,12 +1391,12 @@
"wei", "wei",
"weiregistration" "weiregistration"
], ],
"query": "{\"wei\": [\"club\"], \"wei__membership_end__gte\": [\"today\"]}", "query": "[\"AND\", {\"wei\": [\"club\"], \"wei__membership_end__gte\": [\"today\"]}, {\"deposit_type\": \"note\"}]",
"type": "change", "type": "change",
"mask": 2, "mask": 2,
"field": "caution_check", "field": "deposit_given",
"permanent": false, "permanent": false,
"description": "Dire si un chèque de caution est donné pour une inscription WEI" "description": "Autoriser une transaction de caution WEI"
} }
}, },
{ {
@@ -4366,6 +4366,70 @@
"description": "Modifier l'équipe d'une adhésion WEI à son bus" "description": "Modifier l'équipe d'une adhésion WEI à son bus"
} }
}, },
{
"model": "permission.permission",
"pk": 294,
"fields": {
"model": [
"wei",
"weiregistration"
],
"query": "[\"AND\", {\"wei__year\": [\"today\", \"year\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}, {\"deposit_type\": \"check\"}]",
"type": "change",
"mask": 2,
"field": "deposit_given",
"permanent": false,
"description": "Dire si un chèque de caution a été donné"
}
},
{
"model": "permission.permission",
"pk": 295,
"fields": {
"model": [
"wei",
"weiregistration"
],
"query": "{\"wei__year\": [\"today\", \"year\"]}",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir toutes les inscriptions au WEI courant"
}
},
{
"model": "permission.permission",
"pk": 296,
"fields": {
"model": [
"wei",
"weimembership"
],
"query": "{\"club__weiclub__year\": [\"today\", \"year\"]}",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir toutes les adhésions au WEI courant"
}
},
{
"model": "permission.permission",
"pk": 297,
"fields": {
"model": [
"wei",
"weiregistration"
],
"query": "[\"AND\", {\"user\": [\"user\"], \"wei__membership_start__lte\": [\"today\"], \"wei__membership_end__gte\": [\"today\"]}, [\"OR\", {\"wei\": [\"club\"]}, {\"wei__year\": [\"today\", \"year\"], \"membership\": null}]]",
"type": "change",
"mask": 1,
"field": "deposit_type",
"permanent": false,
"description": "Modifier le type de caution de mon inscription WEI tant qu'elle n'est pas validée"
}
},
{ {
"model": "permission.role", "model": "permission.role",
"pk": 1, "pk": 1,
@@ -4460,7 +4524,8 @@
159, 159,
160, 160,
212, 212,
222 222,
297
] ]
} }
}, },
@@ -4647,7 +4712,10 @@
176, 176,
177, 177,
178, 178,
183 183,
294,
295,
296
] ]
} }
}, },

View File

@@ -77,7 +77,7 @@ class WEIRegistrationViewSet(ReadProtectedModelViewSet):
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter] filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
filterset_fields = ['user', 'user__username', 'user__first_name', 'user__last_name', 'user__email', filterset_fields = ['user', 'user__username', 'user__first_name', 'user__last_name', 'user__email',
'user__note__alias__name', 'user__note__alias__normalized_name', 'wei', 'wei__name', 'user__note__alias__name', 'user__note__alias__normalized_name', 'wei', 'wei__name',
'wei__email', 'wei__year', 'soge_credit', 'deposit_check', 'birth_date', 'gender', 'wei__email', 'wei__year', 'soge_credit', 'deposit_given', 'birth_date', 'gender',
'clothing_cut', 'clothing_size', 'first_year', 'emergency_contact_name', 'clothing_cut', 'clothing_size', 'first_year', 'emergency_contact_name',
'emergency_contact_phone', ] 'emergency_contact_phone', ]
search_fields = ['$user__username', '$user__first_name', '$user__last_name', '$user__email', search_fields = ['$user__username', '$user__first_name', '$user__last_name', '$user__email',

View File

@@ -44,7 +44,7 @@ class WEIRegistrationForm(forms.ModelForm):
fields = [ fields = [
'user', 'soge_credit', 'birth_date', 'gender', 'clothing_size', 'user', 'soge_credit', 'birth_date', 'gender', 'clothing_size',
'health_issues', 'emergency_contact_name', 'emergency_contact_phone', 'health_issues', 'emergency_contact_name', 'emergency_contact_phone',
'first_year', 'information_json', 'deposit_check', 'deposit_type' 'first_year', 'information_json', 'deposit_given', 'deposit_type'
] ]
widgets = { widgets = {
"user": Autocomplete( "user": Autocomplete(
@@ -59,8 +59,8 @@ class WEIRegistrationForm(forms.ModelForm):
'minDate': '1900-01-01', 'minDate': '1900-01-01',
'maxDate': '2100-01-01' 'maxDate': '2100-01-01'
}), }),
"deposit_check": forms.BooleanField( "deposit_given": forms.CheckboxInput(
required=False, attrs={'class': 'form-check-input'},
), ),
"deposit_type": forms.RadioSelect(), "deposit_type": forms.RadioSelect(),
} }
@@ -161,7 +161,7 @@ class WEIMembership1AForm(WEIMembershipForm):
""" """
Used to confirm registrations of first year members without choosing a bus now. Used to confirm registrations of first year members without choosing a bus now.
""" """
deposit_check = None deposit_given = None
roles = None roles = None
def clean(self): def clean(self):

View File

@@ -10,145 +10,225 @@ from django import forms
from django.db import transaction from django.db import transaction
from django.db.models import Q from django.db.models import Q
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.safestring import mark_safe
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInformation from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInformation
from ...models import WEIMembership, Bus from ...models import WEIMembership, Bus
WORDS = { WORDS = {
'list': [ 'list': [
'13 organisé', '3ième mi temps', 'Années 2000', 'Apéro', 'BBQ', 'BP', 'Beauf', 'Binge drinking', 'Bon enfant', 'Fiesta', 'Graillance', 'Move it move it', 'Calme', 'Nerd et geek', 'Jeux de rôles et danse rock',
'Cartouche', 'Catacombes', 'Chansons paillardes', 'Chansons populaires', 'Chanteur', 'Chartreuse', 'Chill', 'Strass et paillettes', 'Spectaculaire', 'Splendide', 'Flow inégalable', 'Rap', 'Battles légendaires',
'Core', 'DJ', 'Dancefloor', 'Danse', 'David Guetta', 'Disco', 'Eau de vie', 'Électro', 'Escalade', 'Familial', 'Techno', 'Alcool', 'Kiffeur·euse', 'Rugby', 'Médiéval', 'Festif',
'Fanfare', 'Fracassage', 'Féria', 'Hard rock', 'Hoeggarden', 'House', 'Huit-six', 'IPA', 'Inclusif', 'Inferno', 'Stylé', 'Chipie', 'Rétro', 'Vache', 'Farfadet', 'Fanfare',
'Introverti', 'Jager bomb', 'Jazz', 'Jeux d\'alcool', 'Jeux de rôles', 'Jeux vidéo', 'Jul', 'Jus de fruit',
'Karaoké', 'LGBTQI+', 'Lady Gaga', 'Loup garou', 'Morning beer', 'Métal', 'Nuit blanche', 'Ovalie', 'Psychedelic',
'Pétanque', 'Rave', 'Reggae', 'Rhum', 'Ricard', 'Rock', 'Rosé', 'Rétro', 'Séducteur', 'Techno', 'Thérapie taxi',
'Théâtre', 'Trap', 'Turn up', 'Underground', 'Volley', 'Wati B', 'Zinédine Zidane',
], ],
'questions': { 'questions': {
'Question 1': [ "alcool": [
'Description 1', """Sur une échelle allant de 0 (= 0 alcool ou très peu) à 5 (= la fontaine de jouvence alcoolique),
quel niveau de consommation dalcool souhaiterais-tu ?""",
{ {
3: 'Réponse 1 Madagas[car]', 42: 4,
4: 'Réponse 1 Y2[KAR]', 47: 1,
2: 'Réponse 1 Tcherno[bus]', 48: 3,
5: 'Réponse 1 [Kar]tier', 45: 3.5,
1: 'Réponse 1 [Car]cassonne', 44: 4,
6: 'Réponse 1 O[car]ina', 46: 5,
7: 'Réponse 1 Show[bus]', 43: 3,
8: 'Réponse 1 [Car]ioca' 49: 3
} }
], ],
'Question 2': [ "voie_post_bac": [
'Description 2', """Si la DA du bus de ton choix correspondait à une voie post-bac, laquelle serait-elle ?""",
{ {
3: 'Réponse 2 Madagas[car]', 42: "Double licence cuisine/arts du cirque option burger",
4: 'Réponse 2 Y2[KAR]', 47: "BTS Exploration de donjon",
2: 'Réponse 2 Tcherno[bus]', 48: "Ecole des stars en herbe",
5: 'Réponse 2 [Kar]tier', 45: "Déscolarisation précoce",
1: 'Réponse 2 [Car]cassonne', 44: "Rattrapage pour excès de kiff",
6: 'Réponse 2 O[car]ina', 46: "Double cursus STAPS / Licence dhistoire",
7: 'Réponse 2 Show[bus]', 43: "Recherche active dun sugar daddy/dun sugar mommy",
8: 'Réponse 2 [Car]ioca' 49: "Licence de musicologie"
} }
], ],
'Question 3': [ "boite": [
'Description 3', """Tu es seul·e sur une île déserte et devant toi il y a une sombre boîte de taille raisonnable.
Quy a-t-il à lintérieur ?""",
{ {
3: 'Réponse 3 Madagas[car]', 42: "Un burgouzz de valouzz",
4: 'Réponse 3 Y2[KAR]', 47: "Un ocarina (pour me téléporter hors de ce bourbier)",
2: 'Réponse 3 Tcherno[bus]', 48: "Des paillettes, un micro de karaoké et une enceinte bluetooth",
5: 'Réponse 3 [Kar]tier', 45: "Un kebab",
1: 'Réponse 3 [Car]cassonne', 44: "Une 86 et un caisson pour taper du pied",
6: 'Réponse 3 O[car]ina', 46: "Une épée, un ballon et une tireuse",
7: 'Réponse 3 Show[bus]', 43: "Des lunettes de soleil",
8: 'Réponse 3 [Car]ioca' 49: "Mon instrument de musique"
} }
], ],
'Question 4': [ "tardif": [
'Description 4', """Il est 00h, tu as passé la journée à la plage avec tes copains et iels te proposent de prolonger parce
quaprès tout, il ny a plus personne sur la plage à cette heure-ci. Tu nhabites pas loin mais tenchaînes
demain avec une journée similaire avec un autre groupe damis parce que tes trop #busy. Que fais-tu ?""",
{ {
3: 'Réponse 4 Madagas[car]', 42: "On veut se déchaîner toute la nuit !!",
4: 'Réponse 4 Y2[KAR]', 47: "Je prends une glace et chill un moment avant daller dormir",
2: 'Réponse 4 Tcherno[bus]', 48: "Jenfile mes boogie shoes pour enflammer le dancefloor avec elleux et lancer un concours de slay, le perdant finit la bouteille de rhum",
5: 'Réponse 4 [Kar]tier', 45: "La fête continuuuuue",
1: 'Réponse 4 [Car]cassonne', 44: "Soirée sangria plage → boîte → lever de soleil sur la plage",
6: 'Réponse 4 O[car]ina', 46: "Minuit ? Cest lheure du genepi. On commence les alcools forts !!",
7: 'Réponse 4 Show[bus]', 43: "Tenchaînes direct (faut pas les priver de ta présence)",
8: 'Réponse 4 [Car]ioca' 49: "On continue en mode chill (soirée potins)"
} }
], ],
'Question 5': [ "cohesion": [
'Description 5', """Cest la rentrée de Seconde et tu découvres ta classe, tes camarades et ta prof principale!!!
qui vous propose une activité de cohésion. Laquelle est-elle ?""",
{ {
3: 'Réponse 5 Madagas[car]', 42: "Un relais cubi en ventriglisse",
4: 'Réponse 5 Y2[KAR]', 47: "Un jeu de rôle",
2: 'Réponse 5 Tcherno[bus]', 48: "Organiser la soirée de lannée dans le lycée. Le thème : SLAY (Spotlight, Love, Amaze/All-night, Yeah), paillettes, disco",
5: 'Réponse 5 [Kar]tier', 45: "La prof de français propose un slam parce qu'elle pense que c'est du rap littéraire qui fera plaisir aux élèves",
1: 'Réponse 5 [Car]cassonne', 44: "Ptit escape game + apéro",
6: 'Réponse 5 O[car]ina', 46: "Joute avec des boucliers Gilbert",
7: 'Réponse 5 Show[bus]', 43: "Tournage dun clip de confessions nocturnes de Diams",
8: 'Réponse 5 [Car]ioca' 49: "Je sais pas jai raté mon BAFA"
} }
], ],
'Question 6': [ "artiste": [
'Description 6', """Cest lété et la saison des festivals a commencé. Tu regardes la programmation du festival
pas loin de chez toi et tu découvres avec joie la présence dun·e artiste. De qui sagit-il ?""",
{ {
3: 'Réponse 6 Madagas[car]', 42: "Moto-Moto (il chantera son fameux tube “je les aime grosses, je les aime bombées”)",
4: 'Réponse 6 Y2[KAR]', 47: "Hatsune Miku",
2: 'Réponse 6 Tcherno[bus]', 48: "Rihanna",
5: 'Réponse 6 [Kar]tier', 45: "Vald",
1: 'Réponse 6 [Car]cassonne', 44: "Qui connaît vraiment les noms des artistes de tech ?",
6: 'Réponse 6 O[car]ina', 46: "Perceval",
7: 'Réponse 6 Show[bus]', 43: "Fatal bazooka",
8: 'Réponse 6 [Car]ioca' 49: "Måneskin"
} }
], ],
'Question 7': [ "annonce_noel": [
'Description 7', """Cest Noël et tu revois toute ta famille, oncles, tantes, cousin·e·s, grands-parents, la totale.
Dun coup, tu te lèves, tapotes de manière pompeuse sur ton verre avec un de tes couverts.
Quannonces-tu ?""",
{ {
3: 'Réponse 7 Madagas[car]', 42: """« Chère famille. Je sais bien que nous avions dit : pas de politique à table.
4: 'Réponse 7 Y2[KAR]', Je ne peux toutefois me retenir de vous annoncer une grande nouvelle…
2: 'Réponse 7 Tcherno[bus]', jai décidé de quitter la ville pour consacrer ma vie au culte du Roi Julian.
5: 'Réponse 7 [Kar]tier', A moi la jungle luxuriante, là où le soleil chaud caresse les palmiers,
1: 'Réponse 7 [Car]cassonne', où les lémuriens dansent avec frénésie et où chaque repas est une ode au burger sauvage.
6: 'Réponse 7 O[car]ina', Longue vie à Sa Majesté le Roi Julian ! »""",
7: 'Réponse 7 Show[bus]', 47: "« Jai perdu »",
8: 'Réponse 7 [Car]ioca' 48: "« Mes chers parents je pars, jarrête lENS pour devenir DJ slay à Ibiza »",
45: "Jinterromps le repas pour rapper les 6min de bande organisée",
44: "« Digestif ? Pétanque ? Les deux ? »",
46: "« Montjoie St Denis à bas la Macronie »",
43: "« Je suis enceinte » (cest faux jai juste besoin dattention)",
49: """Discours de remerciement :
je lance un powerpoint de 65 slides et sors une feuille A4 blanche (je fais semblant de lire mon discours dessus)"""
} }
], ],
'Question 8': [ "vacances": [
'Description 8', """Les vacances sont là et taimerais bien partir quelque part, mais où ?""",
{ {
3: 'Réponse 8 Madagas[car]', 42: "A Madagascar, à bord dun bus conduit par des pingouins",
4: 'Réponse 8 Y2[KAR]', 47: "Dans ma chambre",
2: 'Réponse 8 Tcherno[bus]', 48: "Rio de Janeiro",
5: 'Réponse 8 [Kar]tier', 45: "N'importe où tant qu'on peut sortir tous les soirs",
1: 'Réponse 8 [Car]cassonne', 44: "Tu suis les plans du club ski ou de piratens",
6: 'Réponse 8 O[car]ina', 46: "Carcassonne",
7: 'Réponse 8 Show[bus]', 43: "Coachella",
8: 'Réponse 8 [Car]ioca' 49: "Dans les montagnes de la république populaire dAuvergne-Rhônes-Alpes pour profiter de la fraîcheur, de la nature et de mes ami·e·s"
} }
], ],
'Question 9': [ "loisir": [
'Description 9', """Tas fini ta journée de cours et tu tapprêtes à profiter dune activité/hobby/loisir de ton choix.
Laquelle est-ce ?""",
{ {
3: 'Réponse 9 Madagas[car]', 42: "Cueillir des noix de coco",
4: 'Réponse 9 Y2[KAR]', 47: "Essayer de travailler puis chill avec des potes autour dun jeu en buvant du thé",
2: 'Réponse 9 Tcherno[bus]', 48: "Repet du nouveau spectacle de mon club, before (potins) puis sortie avec les potes jusquau bout de la night",
5: 'Réponse 9 [Kar]tier', 45: "Zoner avec les copaings jusquà pas dheure",
1: 'Réponse 9 [Car]cassonne', 44: "Go Kfet pour se faire traquenard jusquà 3h du mat",
6: 'Réponse 9 O[car]ina', 46: "Déterminer ce qui est le plus solide entre mon crâne et une ecocup",
7: 'Réponse 9 Show[bus]', 43: "Revoir pour la 6e fois gossip girl au fond de ton lit",
8: 'Réponse 9 [Car]ioca' 49: "Jouer de mon instrument préféré avec les copains/copines pour préparer le prochain concert #solidays"
}
],
"plan": [
"""Tu reçois un message sur la conversation de groupe que tu partages avec tes potes :
vous êtes chaud·e·s pour vous retrouver. Quel plan tattire le plus ?""",
{
42: """Après-midi piscine, puis before arrosé de mojito,
avant daller séclater en pot avec toute la savane et de finir sur un after spécial pina colada""",
47: """(matin) : Ptit jeu de rôle
(repas) : le traditionnel poké-tacos
(juste après le repas) : combat avec des épées en mousse avec les copains!
(16h00) : pause thé
(fin daprès midi) : initiation à la danse rock
(soirée) : découverte dun jeu de société avec des règles obscures
""",
48: "Soirée champagne and chic : spectacle et dîner au moulin rouge puis soirée sur les champs",
45: "Se regrouper pour une soirée, même si il nest encore que 10h",
44: "Ptit poké qui termine en koin koin avec after poker",
46: "Une dégustation de bière, un rugby et toute autre activité joviale",
43: "Un brunch de pour papoter puis friperies",
49: "Soirée raclette !"
} }
] ]
},
'stats': [
{
"question": """Le WEI est structuré par bus, et au sein de chaque bus, par équipes.
Pour toi, être dans une équipe où tout le monde reste sobre (primo-entrants comme encadrants) c'est :""",
"answers": [
(1, "Inenvisageable"),
(2, "À contre cœur"),
(3, "Pourquoi pas"),
(4, "Souhaitable"),
(5, "Nécessaire"),
],
"help_text": "(De toute façon aucun alcool n'est consommé pendant les trajets du bus, ni aller, ni retour.)",
},
{
"question": "Faire partie d'un bus qui n'apporte pas de boisson alcoolisée pour ses membres, pour toi c'est :",
"answers": [
(1, "Inenvisageable"),
(2, "À contre cœur"),
(3, "Pourquoi pas"),
(4, "Souhaitable"),
(5, "Nécessaire"),
],
"help_text": """(Tout les bus apportent de l'alcool cette année, cette question sert à l'organisation pour l'année prochaine.
De plus il y aura de toute façon de l'alcool commun au WEI et aucun alcool n'est consommé pendant les trajets en bus.)""",
},
]
}
IMAGES = {
"vacances": {
49: "/static/wei/img/logo_auvergne_rhone_alpes.jpg",
} }
} }
NB_WORDS = 5 NB_WORDS = 5
class OptionalImageRadioSelect(forms.RadioSelect):
def __init__(self, images=None, *args, **kwargs):
self.images = images or {}
super().__init__(*args, **kwargs)
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
option = super().create_option(name, value, label, selected, index, subindex=subindex, attrs=attrs)
img_url = self.images.get(value)
if img_url:
option['label'] = mark_safe(f'{label} <img src="{img_url}" style="height:32px;vertical-align:middle;">')
else:
option['label'] = label
return option
class WEISurveyForm2025(forms.Form): class WEISurveyForm2025(forms.Form):
""" """
Survey form for the year 2025. Survey form for the year 2025.
@@ -170,7 +250,7 @@ class WEISurveyForm2025(forms.Form):
if information.step == 0: if information.step == 0:
self.fields["words"] = forms.MultipleChoiceField( self.fields["words"] = forms.MultipleChoiceField(
label=_(f"Choose {NB_WORDS} words:"), label=_(f"Select {NB_WORDS} words that describe the WEI experience you want to have."),
choices=[(w, w) for w in WORDS['list']], choices=[(w, w) for w in WORDS['list']],
widget=forms.CheckboxSelectMultiple(), widget=forms.CheckboxSelectMultiple(),
required=True, required=True,
@@ -178,38 +258,34 @@ class WEISurveyForm2025(forms.Form):
if self.is_valid(): if self.is_valid():
return return
buses = WEISurveyAlgorithm2025.get_buses() all_preferred_words = WORDS['list']
informations = {bus: WEIBusInformation2025(bus) for bus in buses}
scores = sum((list(informations[bus].scores.values()) for bus in buses), [])
if scores:
average_score = sum(scores) / len(scores)
else:
average_score = 0
preferred_words = {
bus: [word for word in WORDS['list'] if informations[bus].scores[word] >= average_score]
for bus in buses
}
all_preferred_words = set()
for bus_words in preferred_words.values():
all_preferred_words.update(bus_words)
all_preferred_words = list(all_preferred_words)
rng.shuffle(all_preferred_words) rng.shuffle(all_preferred_words)
self.fields["words"].choices = [(w, w) for w in all_preferred_words] self.fields["words"].choices = [(w, w) for w in all_preferred_words]
else: elif information.step <= len(WORDS['questions']):
questions = list(WORDS['questions'].items()) questions = list(WORDS['questions'].items())
idx = information.step - 1 idx = information.step - 1
if idx < len(questions): if idx < len(questions):
q, (desc, answers) = questions[idx] q, (desc, answers) = questions[idx]
choices = [(k, v) for k, v in answers.items()] if q == 'alcool':
rng.shuffle(choices) choices = [(i / 2, str(i / 2)) for i in range(11)]
else:
choices = [(k, v) for k, v in answers.items()]
rng.shuffle(choices)
self.fields[q] = forms.ChoiceField( self.fields[q] = forms.ChoiceField(
label=desc, label=desc,
choices=choices, choices=choices,
widget=forms.RadioSelect, widget=OptionalImageRadioSelect(images=IMAGES.get(q, {})),
required=True, required=True,
) )
elif information.step == len(WORDS['questions']) + 1:
for i, v in enumerate(WORDS['stats']):
self.fields[f'stat_{i}'] = forms.ChoiceField(
label=v['question'],
choices=v['answers'],
widget=forms.RadioSelect(),
required=False,
help_text=_(v.get('help_text', ''))
)
def clean_words(self): def clean_words(self):
data = self.cleaned_data['words'] data = self.cleaned_data['words']
@@ -226,8 +302,6 @@ class WEIBusInformation2025(WEIBusInformation):
def __init__(self, bus): def __init__(self, bus):
self.scores = {} self.scores = {}
for word in WORDS['list']:
self.scores[word] = 0
super().__init__(bus) super().__init__(bus)
@@ -235,7 +309,9 @@ class BusInformationForm2025(forms.ModelForm):
class Meta: class Meta:
model = Bus model = Bus
fields = ['information_json'] fields = ['information_json']
widgets = {} widgets = {
'information_json': forms.HiddenInput(),
}
def __init__(self, *args, words=None, **kwargs): def __init__(self, *args, words=None, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@@ -257,7 +333,7 @@ class BusInformationForm2025(forms.ModelForm):
label=word, label=word,
choices=choices, choices=choices,
coerce=int, coerce=int,
initial=initial_scores.get(word, 0), initial=initial_scores.get(word, 0) if word in initial_scores else None,
required=True, required=True,
widget=forms.RadioSelect, widget=forms.RadioSelect,
help_text=_("Rate between 0 and 5."), help_text=_("Rate between 0 and 5."),
@@ -285,7 +361,7 @@ class WEISurveyInformation2025(WEISurveyInformation):
step = 0 step = 0
def __init__(self, registration): def __init__(self, registration):
for i in range(1, 5): for i in range(1, NB_WORDS + 1):
setattr(self, "word" + str(i), None) setattr(self, "word" + str(i), None)
for q in WORDS['questions']: for q in WORDS['questions']:
setattr(self, q, None) setattr(self, q, None)
@@ -297,7 +373,7 @@ class WEISurveyInformation2025(WEISurveyInformation):
""" """
self.step = 0 self.step = 0
self.seed = 0 self.seed = 0
for i in range(1, 5): for i in range(1, NB_WORDS + 1):
setattr(self, f"word{i}", None) setattr(self, f"word{i}", None)
for q in WORDS['questions']: for q in WORDS['questions']:
setattr(self, q, None) setattr(self, q, None)
@@ -336,7 +412,7 @@ class WEISurvey2025(WEISurvey):
setattr(self.information, "word" + str(i), word) setattr(self.information, "word" + str(i), word)
self.information.step += 1 self.information.step += 1
self.save() self.save()
else: elif 1 <= self.information.step <= len(WORDS['questions']):
questions = list(WORDS['questions'].keys()) questions = list(WORDS['questions'].keys())
idx = self.information.step - 1 idx = self.information.step - 1
if idx < len(questions): if idx < len(questions):
@@ -344,6 +420,13 @@ class WEISurvey2025(WEISurvey):
setattr(self.information, q, form.cleaned_data[q]) setattr(self.information, q, form.cleaned_data[q])
self.information.step += 1 self.information.step += 1
self.save() self.save()
else:
for i, __ in enumerate(WORDS['stats']):
ans = form.cleaned_data.get(f'stat_{i}')
if ans is not None:
setattr(self.information, f'stat_{i}', ans)
self.information.step += 1
self.save()
@classmethod @classmethod
def get_algorithm_class(cls): def get_algorithm_class(cls):
@@ -353,7 +436,7 @@ class WEISurvey2025(WEISurvey):
""" """
The survey is complete once the bus is chosen. The survey is complete once the bus is chosen.
""" """
return self.information.step > len(WORDS['questions']) return self.information.step > len(WORDS['questions']) + 1
@classmethod @classmethod
@lru_cache() @lru_cache()
@@ -371,8 +454,9 @@ class WEISurvey2025(WEISurvey):
""" """
if not self.is_complete(): if not self.is_complete():
raise ValueError("Survey is not ended, can't calculate score") raise ValueError("Survey is not ended, can't calculate score")
# Score is the given score by the bus subtracted to the mid-score of the buses. s = sum(1 for q in WORDS['questions'] if q != 'alcool' and getattr(self.information, q) == bus.pk)
s = sum(1 for q in WORDS['questions'] if getattr(self.information, q) == bus.pk) if 'alcool' in WORDS['questions'] and bus.pk in WORDS['questions']['alcool'][1] and hasattr(self.information, 'alcool'):
s -= abs(float(self.information.alcool) - float(WORDS['questions']['alcool'][1][bus.pk]))
return s return s
@lru_cache() @lru_cache()
@@ -386,7 +470,7 @@ class WEISurvey2025(WEISurvey):
bus_info = self.get_algorithm_class().get_bus_information(bus) bus_info = self.get_algorithm_class().get_bus_information(bus)
# Score is the given score by the bus subtracted to the mid-score of the buses. # 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))] 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, 1 + NB_WORDS)) - self.word_mean(getattr(self.information, 'word' + str(i))) for i in range(1, 1 + NB_WORDS)) / self.get_algorithm_class().get_buses().count()
return s return s
@lru_cache() @lru_cache()
@@ -396,10 +480,10 @@ class WEISurvey2025(WEISurvey):
@lru_cache() @lru_cache()
def ordered_buses(self): def ordered_buses(self):
""" """
Force the choice of bus to be in the 3 preferred buses according to the words Order the buses by the score_questions of the survey.
""" """
values = list(self.scores_per_bus().items()) values = list(self.scores_per_bus().items())
values.sort(key=lambda item: -item[1][1]) values.sort(key=lambda item: -item[1][0])
return values return values
@classmethod @classmethod
@@ -513,8 +597,8 @@ class WEISurveyAlgorithm2025(WEISurveyAlgorithm):
for survey2 in surveys: for survey2 in surveys:
if not survey2.information.valid or survey2.information.get_selected_bus() != bus: if not survey2.information.valid or survey2.information.get_selected_bus() != bus:
continue continue
score2 = survey2.score_questions(bus) score2 = survey2.score_words(bus)
if current_scores[0] <= score2: # Ignore better students if current_scores[1] <= score2: # Ignore better students
continue continue
if least_preferred_survey is None or score2 < least_score: if least_preferred_survey is None or score2 < least_score:
least_preferred_survey = survey2 least_preferred_survey = survey2

View File

@@ -0,0 +1,22 @@
# Generated by Django 5.2.4 on 2025-08-02 17:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wei', '0017_alter_weiclub_fee_soge_credit'),
]
operations = [
migrations.RemoveField(
model_name='weiregistration',
name='deposit_check',
),
migrations.AddField(
model_name='weiregistration',
name='deposit_given',
field=models.BooleanField(default=False, verbose_name='Deposit given'),
),
]

View File

@@ -202,9 +202,9 @@ class WEIRegistration(models.Model):
verbose_name=_("Credit from Société générale"), verbose_name=_("Credit from Société générale"),
) )
deposit_check = models.BooleanField( deposit_given = models.BooleanField(
default=False, default=False,
verbose_name=_("Deposit check given") verbose_name=_("Deposit given")
) )
deposit_type = models.CharField( deposit_type = models.CharField(

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -84,6 +84,35 @@ class WEIRegistrationTable(tables.Table):
}, },
) )
def render_deposit_type(self, record):
if record.first_year:
return format_html("")
if record.deposit_type == 'check':
# TODO Install Font Awesome 6 to acces more icons (and keep compaibility with current used v4)
return format_html("""
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" width="1.5em" height="1.5em"
fill="currentColor" style="position: relative; left: -0.15em;">
<path d="
M128 128C92.7 128 64 156.7 64 192L64 448C64 483.3 92.7 512 128 512L512 512
C547.3 512 576 483.3 576 448L576 192C576 156.7 547.3 128 512 128L128 128z
M360 352L488 352C501.3 352 512 362.7 512 376C512 389.3 501.3 400 488 400L360 400
C346.7 400 336 389.3 336 376C336 362.7 346.7 352 360 352z
M336 264C336 250.7 346.7 240 360 240L488 240C501.3 240 512 250.7 512 264
C512 277.3 501.3 288 488 288L360 288C346.7 288 336 277.3 336 264z
M212 208C223 208 232 217 232 228L232 232L240 232C251 232 260 241 260 252
C260 263 251 272 240 272L192.5 272C185.6 272 180 277.6 180 284.5
C180 290.6 184.4 295.8 190.4 296.8L232.1 303.8C257.4 308 276 329.9 276 355.6
C276 381.7 257 403.3 232 407.4L232 412.1C232 423.1 223 432.1 212 432.1
C201 432.1 192 423.1 192 412.1L192 408.1L168 408.1C157 408.1 148 399.1 148 388.1
C148 377.1 157 368.1 168 368.1L223.5 368.1C230.4 368.1 236 362.5 236 355.6
C236 349.5 231.6 344.3 225.6 343.3L183.9 336.3C158.5 332 140 310.1 140 284.5
C140 255.7 163.2 232.3 192 232L192 228C192 217 201 208 212 208z
" />
</svg>
""")
if record.deposit_type == 'note':
return format_html("<i class=\"fa fa-exchange\"></i>")
def render_validate(self, record): def render_validate(self, record):
hasperm = PermissionBackend.check_perm( hasperm = PermissionBackend.check_perm(
get_current_request(), "wei.add_weimembership", WEIMembership( get_current_request(), "wei.add_weimembership", WEIMembership(
@@ -125,8 +154,8 @@ class WEIRegistrationTable(tables.Table):
order_by = ('validate', 'user',) order_by = ('validate', 'user',)
model = WEIRegistration model = WEIRegistration
template_name = 'django_tables2/bootstrap4.html' template_name = 'django_tables2/bootstrap4.html'
fields = ('user', 'user__first_name', 'user__last_name', 'first_year', 'deposit_check', fields = ('user', 'user__first_name', 'user__last_name', 'first_year', 'deposit_given',
'edit', 'validate', 'delete',) 'deposit_type', 'edit', 'validate', 'delete',)
row_attrs = { row_attrs = {
'class': 'table-row', 'class': 'table-row',
'id': lambda record: "row-" + str(record.pk), 'id': lambda record: "row-" + str(record.pk),
@@ -158,6 +187,35 @@ class WEIMembershipTable(tables.Table):
def render_year(self, record): def render_year(self, record):
return str(record.user.profile.ens_year) + "A" return str(record.user.profile.ens_year) + "A"
def render_registration__deposit_type(self, record):
if record.registration.first_year:
return format_html("")
if record.registration.deposit_type == 'check':
# TODO Install Font Awesome 6 to acces more icons (and keep compaibility with current used v4)
return format_html("""
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" width="1.5em" height="1.5em"
fill="currentColor" style="position: relative; left: -0.15em;">
<path d="
M128 128C92.7 128 64 156.7 64 192L64 448C64 483.3 92.7 512 128 512L512 512
C547.3 512 576 483.3 576 448L576 192C576 156.7 547.3 128 512 128L128 128z
M360 352L488 352C501.3 352 512 362.7 512 376C512 389.3 501.3 400 488 400L360 400
C346.7 400 336 389.3 336 376C336 362.7 346.7 352 360 352z
M336 264C336 250.7 346.7 240 360 240L488 240C501.3 240 512 250.7 512 264
C512 277.3 501.3 288 488 288L360 288C346.7 288 336 277.3 336 264z
M212 208C223 208 232 217 232 228L232 232L240 232C251 232 260 241 260 252
C260 263 251 272 240 272L192.5 272C185.6 272 180 277.6 180 284.5
C180 290.6 184.4 295.8 190.4 296.8L232.1 303.8C257.4 308 276 329.9 276 355.6
C276 381.7 257 403.3 232 407.4L232 412.1C232 423.1 223 432.1 212 432.1
C201 432.1 192 423.1 192 412.1L192 408.1L168 408.1C157 408.1 148 399.1 148 388.1
C148 377.1 157 368.1 168 368.1L223.5 368.1C230.4 368.1 236 362.5 236 355.6
C236 349.5 231.6 344.3 225.6 343.3L183.9 336.3C158.5 332 140 310.1 140 284.5
C140 255.7 163.2 232.3 192 232L192 228C192 217 201 208 212 208z
" />
</svg>
""")
if record.registration.deposit_type == 'note':
return format_html("<i class=\"fa fa-exchange\"></i>")
class Meta: class Meta:
attrs = { attrs = {
'class': 'table table-condensed table-striped table-hover' 'class': 'table table-condensed table-striped table-hover'
@@ -165,7 +223,7 @@ class WEIMembershipTable(tables.Table):
model = WEIMembership model = WEIMembership
template_name = 'django_tables2/bootstrap4.html' template_name = 'django_tables2/bootstrap4.html'
fields = ('user', 'user__last_name', 'user__first_name', 'registration__gender', 'user__profile__department', fields = ('user', 'user__last_name', 'user__first_name', 'registration__gender', 'user__profile__department',
'year', 'bus', 'team', 'registration__deposit_check', ) 'year', 'bus', 'team', 'registration__deposit_given', 'registration__deposit_type')
row_attrs = { row_attrs = {
'class': 'table-row', 'class': 'table-row',
'id': lambda record: "row-" + str(record.pk), 'id': lambda record: "row-" + str(record.pk),

View File

@@ -22,8 +22,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endif %} {% endif %}
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus' pk=object.pk %}" <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus' pk=object.pk %}"
data-turbolinks="false">{% trans "Edit" %}</a> data-turbolinks="false">{% trans "Edit" %}</a>
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus_info' pk=object.pk %}" <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus_info' pk=object.pk %}"
data-turbolinks="false">{% trans "Edit information" %}</a> data-turbolinks="false">{% trans "Edit information for survey" %}</a>
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:add_team' pk=object.pk %}" <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:add_team' pk=object.pk %}"
data-turbolinks="false">{% trans "Add team" %}</a> data-turbolinks="false">{% trans "Add team" %}</a>
</div> </div>

View File

@@ -31,15 +31,24 @@ SPDX-License-Identifier: GPL-3.0-or-later
<a class="btn btn-success" href="{% url "wei:wei_register_1A_myself" wei_pk=club.pk %}" data-turbolinks="false"> <a class="btn btn-success" href="{% url "wei:wei_register_1A_myself" wei_pk=club.pk %}" data-turbolinks="false">
{% trans "Register to the WEI! 1A" %} {% trans "Register to the WEI! 1A" %}
</a> </a>
{% endif %}
<a class="btn btn-success" href="{% url "wei:wei_register_2A_myself" wei_pk=club.pk %}" data-turbolinks="false">
{% trans "Register to the WEI! 2A+" %}</a>
{% else %} {% else %}
<a class="btn btn-success" href="{% url "wei:wei_register_2A_myself" wei_pk=club.pk %}" data-turbolinks="false">
{% trans "Register to the WEI! 2A+" %}
</a>
{% endif %}
{% else %}
{% if registration.validated %}
<a class="btn btn-warning" href="{% url "wei:wei_update_registration" pk=my_registration.pk %}" <a class="btn btn-warning" href="{% url "wei:wei_update_registration" pk=my_registration.pk %}"
data-turbolinks="false"> data-turbolinks="false">
{% trans "Update my registration" %} {% trans "Update my registration" %}
</a> </a>
{% if not not_first_year %} {% endif %}
{% if my_registration.first_year %}
{% if not survey_complete %}
<a class="btn btn-warning" href="{% url "wei:wei_survey" pk=my_registration.pk %}" data-turbolinks="false">
{% trans "Continue survey" %}
</a>
{% endif %}
<a class="btn btn-warning" href="{% url "wei:wei_survey" pk=my_registration.pk %}?reset=true" data-turbolinks="false"> <a class="btn btn-warning" href="{% url "wei:wei_survey" pk=my_registration.pk %}?reset=true" data-turbolinks="false">
{% trans "Restart survey" %} {% trans "Restart survey" %}
</a> </a>

View File

@@ -96,7 +96,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endif %} {% endif %}
{% else %} {% else %}
<dt class="col-xl-6">{% trans 'Deposit check given'|capfirst %}</dt> <dt class="col-xl-6">{% trans 'Deposit check given'|capfirst %}</dt>
<dd class="col-xl-6">{{ registration.deposit_check|yesno }}</dd> <dd class="col-xl-6">{{ registration.deposit_given|yesno }}</dd>
{% with information=registration.information %} {% with information=registration.information %}
<dt class="col-xl-6">{% trans 'preferred bus'|capfirst %}</dt> <dt class="col-xl-6">{% trans 'preferred bus'|capfirst %}</dt>
@@ -169,9 +169,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endblocktrans %}</p> {% endblocktrans %}</p>
</div> </div>
{% if not registration.deposit_check and not registration.first_year and registration.caution_type == 'check' %} {% if not registration.deposit_given and not registration.first_year and registration.caution_type == 'check' %}
<div class="alert alert-danger"> <div class="alert alert-danger">
{% trans "The user didn't give her/his caution check." %} {% trans "The user didn't give her/his caution." %}
</div> </div>
{% endif %} {% endif %}

View File

@@ -53,9 +53,11 @@ class TestWEIAlgorithm(TestCase):
birth_date='2000-01-01', birth_date='2000-01-01',
) )
information = WEISurveyInformation2025(registration) information = WEISurveyInformation2025(registration)
for j in range(1, 21): for j in range(1, 1 + NB_WORDS):
setattr(information, f'word{j}', random.choice(WORDS['list'])) setattr(information, f'word{j}', random.choice(WORDS['list']))
information.step = 20 for q in WORDS['questions']:
setattr(information, q, random.choice(list(WORDS['questions'][q][1].keys())))
information.step = len(WORDS['questions']) + 2
information.save(registration) information.save(registration)
registration.save() registration.save()
@@ -87,7 +89,7 @@ class TestWEIAlgorithm(TestCase):
setattr(information, f'word{j}', random.choice(WORDS['list'])) setattr(information, f'word{j}', random.choice(WORDS['list']))
for q in WORDS['questions']: for q in WORDS['questions']:
setattr(information, q, random.choice(list(WORDS['questions'][q][1].keys()))) setattr(information, q, random.choice(list(WORDS['questions'][q][1].keys())))
information.step = len(WORDS['questions']) + 1 information.step = len(WORDS['questions']) + 2
information.save(registration) information.save(registration)
registration.save() registration.save()
survey = WEISurvey2025(registration) survey = WEISurvey2025(registration)
@@ -106,10 +108,22 @@ class TestWEIAlgorithm(TestCase):
chosen_bus = survey.information.get_selected_bus() chosen_bus = survey.information.get_selected_bus()
buses = survey.ordered_buses() buses = survey.ordered_buses()
self.assertIn(chosen_bus, [x[0] for x in buses]) self.assertIn(chosen_bus, [x[0] for x in buses])
score = min(v for bus, (v, __) in buses if bus == chosen_bus) score_questions, score_words = next(scores for bus, scores in buses if bus == chosen_bus)
max_score = buses[0][1][0] max_score_questions = max(buses[i][1][0] for i in range(len(buses)))
penalty += (max_score - score) ** 2 max_score_words = max(buses[i][1][1] for i in range(len(buses)))
penalty += (max_score_words - score_words) ** 2
self.assertLessEqual(max_score - score, 1) penalty += (max_score_questions - score_questions) ** 2
self.assertLessEqual(penalty / 100, 25) # Tolerance of 5 % self.assertLessEqual(penalty / 100, 25) # Tolerance of 5 %
# There shouldn't be users who would prefer to switch buses
for r1 in WEIRegistration.objects.filter(wei=self.wei).all():
survey1 = WEISurvey2025(r1)
bus1 = survey1.information.get_selected_bus()
for r2 in WEIRegistration.objects.filter(wei=self.wei, pk__gt=r1.pk):
survey2 = WEISurvey2025(r2)
bus2 = survey2.information.get_selected_bus()
prefer_switch_bus_words = survey1.score_words(bus2) > survey1.score_words(bus1) and survey2.score_words(bus1) > survey2.score_words(bus2)
prefer_switch_bus_questions = survey1.score_questions(bus2) > survey1.score_questions(bus1) and\
survey2.score_questions(bus1) > survey2.score_questions(bus2)
self.assertFalse(prefer_switch_bus_words and prefer_switch_bus_questions)

View File

@@ -101,7 +101,7 @@ class TestWEIRegistration(TestCase):
user_id=self.user.id, user_id=self.user.id,
wei_id=self.wei.id, wei_id=self.wei.id,
soge_credit=True, soge_credit=True,
deposit_check=True, deposit_given=True,
birth_date=date(2000, 1, 1), birth_date=date(2000, 1, 1),
gender="nonbinary", gender="nonbinary",
clothing_cut="male", clothing_cut="male",
@@ -642,7 +642,7 @@ class TestWEIRegistration(TestCase):
last_name="admin", last_name="admin",
first_name="admin", first_name="admin",
bank="Société générale", bank="Société générale",
deposit_check=True, deposit_given=True,
)) ))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertFalse(response.context["form"].is_valid()) self.assertFalse(response.context["form"].is_valid())
@@ -657,7 +657,7 @@ class TestWEIRegistration(TestCase):
last_name="admin", last_name="admin",
first_name="admin", first_name="admin",
bank="Société générale", bank="Société générale",
deposit_check=True, deposit_given=True,
)) ))
self.assertRedirects(response, reverse("wei:wei_registrations", kwargs=dict(pk=self.registration.wei.pk)), 302, 200) self.assertRedirects(response, reverse("wei:wei_registrations", kwargs=dict(pk=self.registration.wei.pk)), 302, 200)
@@ -813,7 +813,7 @@ class TestWeiAPI(TestAPI):
user_id=self.user.id, user_id=self.user.id,
wei_id=self.wei.id, wei_id=self.wei.id,
soge_credit=True, soge_credit=True,
deposit_check=True, deposit_given=True,
birth_date=date(2000, 1, 1), birth_date=date(2000, 1, 1),
gender="nonbinary", gender="nonbinary",
clothing_cut="male", clothing_cut="male",

View File

@@ -166,6 +166,7 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, D
my_registration = WEIRegistration.objects.filter(wei=club, user=self.request.user) my_registration = WEIRegistration.objects.filter(wei=club, user=self.request.user)
if my_registration.exists(): if my_registration.exists():
my_registration = my_registration.get() my_registration = my_registration.get()
context["survey_complete"] = CurrentSurvey(my_registration).is_complete()
else: else:
my_registration = None my_registration = None
context["my_registration"] = my_registration context["my_registration"] = my_registration
@@ -213,6 +214,8 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, D
context["not_first_year"] = WEIMembership.objects.filter(user=self.request.user).exists() context["not_first_year"] = WEIMembership.objects.filter(user=self.request.user).exists()
context["registration_validated"] = WEIMembership.objects.filter(registration=my_registration).exists() if my_registration else False
qs = WEIMembership.objects.filter(club=club, registration__first_year=True, bus__isnull=True) qs = WEIMembership.objects.filter(club=club, registration__first_year=True, bus__isnull=True)
context["can_validate_1a"] = PermissionBackend.check_perm( context["can_validate_1a"] = PermissionBackend.check_perm(
self.request, "wei.change_weimembership_bus", qs.first()) if qs.exists() else False self.request, "wei.change_weimembership_bus", qs.first()) if qs.exists() else False
@@ -594,8 +597,8 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView):
# Cacher les champs pendant l'inscription initiale # Cacher les champs pendant l'inscription initiale
if "first_year" in form.fields: if "first_year" in form.fields:
del form.fields["first_year"] del form.fields["first_year"]
if "deposit_check" in form.fields: if "deposit_given" in form.fields:
del form.fields["deposit_check"] del form.fields["deposit_given"]
if "information_json" in form.fields: if "information_json" in form.fields:
del form.fields["information_json"] del form.fields["information_json"]
if "deposit_type" in form.fields: if "deposit_type" in form.fields:
@@ -704,8 +707,8 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
# Cacher les champs pendant l'inscription initiale # Cacher les champs pendant l'inscription initiale
if "first_year" in form.fields: if "first_year" in form.fields:
del form.fields["first_year"] del form.fields["first_year"]
if "deposit_check" in form.fields: if "deposit_given" in form.fields:
del form.fields["deposit_check"] del form.fields["deposit_given"]
if "information_json" in form.fields: if "information_json" in form.fields:
del form.fields["information_json"] del form.fields["information_json"]
@@ -806,9 +809,11 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
# The auto-json-format may cause issues with the default field remove # The auto-json-format may cause issues with the default field remove
if "information_json" in form.fields: if "information_json" in form.fields:
del form.fields["information_json"] del form.fields["information_json"]
# Masquer le champ deposit_check pour tout le monde dans le formulaire de modification # Masquer le champ deposit_given pour tout le monde dans le formulaire de modification
if "deposit_check" in form.fields: if "deposit_given" in form.fields:
del form.fields["deposit_check"] form.fields["deposit_given"].help_text = _("Tick if the deposit check has been given")
if self.object.first_year or self.object.deposit_type == 'note':
del form.fields["deposit_given"]
# S'assurer que le champ deposit_type est obligatoire pour les 2A+ # S'assurer que le champ deposit_type est obligatoire pour les 2A+
if "deposit_type" in form.fields: if "deposit_type" in form.fields:
@@ -1016,17 +1021,18 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
form.fields["last_name"].initial = registration.user.last_name form.fields["last_name"].initial = registration.user.last_name
form.fields["first_name"].initial = registration.user.first_name form.fields["first_name"].initial = registration.user.first_name
# Ajouter le champ deposit_check uniquement pour les non-première année et le rendre obligatoire # Ajouter le champ deposit_given uniquement pour les non-première année et le rendre obligatoire
if not registration.first_year: if not registration.first_year:
if registration.deposit_type == 'check': if registration.deposit_type == 'check':
form.fields["deposit_check"] = forms.BooleanField( form.fields["deposit_given"] = forms.BooleanField(
required=True, required=True,
initial=registration.deposit_check, disabled=True,
initial=registration.deposit_given,
label=_("Deposit check given"), label=_("Deposit check given"),
help_text=_("Please make sure the check is given before validating the registration") help_text=_("Only treasurers can validate this field")
) )
else: else:
form.fields["deposit_check"] = forms.BooleanField( form.fields["deposit_given"] = forms.BooleanField(
required=True, required=True,
initial=False, initial=False,
label=_("Create deposit transaction"), label=_("Create deposit transaction"),
@@ -1067,8 +1073,8 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
club = registration.wei club = registration.wei
user = registration.user user = registration.user
if "deposit_check" in form.data: if "deposit_given" in form.data:
registration.deposit_check = form.data["deposit_check"] == "on" registration.deposit_given = form.data["deposit_given"] == "on"
registration.save() registration.save()
membership = form.instance membership = form.instance
membership.user = user membership.user = user
@@ -1180,7 +1186,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
def form_invalid(self, form): def form_invalid(self, form):
registration = getattr(form.instance, "registration", None) registration = getattr(form.instance, "registration", None)
if registration is not None: if registration is not None:
registration.deposit_check = False registration.deposit_given = False
registration.save() registration.save()
return super().form_invalid(form) return super().form_invalid(form)
@@ -1228,7 +1234,6 @@ class WEIUpdateMembershipView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateVi
return form return form
def get_success_url(self): def get_success_url(self):
print("get_success_url")
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.registration.wei.pk}) return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.registration.wei.pk})