1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2024-11-26 18:37:12 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
korenstin
2a288ba474 Add caution_check in the validation form, #96 2024-08-05 11:52:47 +02:00
korenstin
34646b8ddb test wei 2024, linters 2024-08-05 00:38:15 +02:00
7 changed files with 257 additions and 81 deletions

View File

@ -80,6 +80,11 @@ class WEIChooseBusForm(forms.Form):
class WEIMembershipForm(forms.ModelForm): class WEIMembershipForm(forms.ModelForm):
caution_check = forms.BooleanField(
required=False,
label=_("Caution check given"),
)
roles = forms.ModelMultipleChoiceField( roles = forms.ModelMultipleChoiceField(
queryset=WEIRole.objects, queryset=WEIRole.objects,
label=_("WEI Roles"), label=_("WEI Roles"),
@ -148,6 +153,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.
""" """
caution_check = None
roles = None roles = None
def clean(self): def clean(self):

View File

@ -11,60 +11,95 @@ from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInf
from ...models import WEIMembership from ...models import WEIMembership
buses_descr = [ buses_descr = [
["Magi[Kar]p","#ef5568", [
"bus faible en alcool mais fort en connerie avec une partie calme pour les amateurs de sieste et de jeux de société. non discriminant il accepte tout le monde y compris le plus nulle des pokémons (magicarpe !!!!!!). Malgré les accusations mensongères, il n'y a aucun weeb dans le Magi[Kar]p"], "Magi[Kar]p", "#ef5568",
["Va[car]me","#fd7a28", """bus faible en alcool mais fort en connerie avec une partie calme pour les amateurs de sieste et de jeux de société.
"descr"], Non discriminant il accepte tout le monde y compris le plus nulle des pokémons (magicarpe !!!!!!). Malgré les
["[Kar]aïbes","#a5cfdd", accusations mensongères, il n'y a aucun weeb dans le Magi[Kar]p""",
"descr"], ],
["[Kar]di [Bus]","#e46398", [
"descr"], "Va[car]me", "#fd7a28",
["Sparta[bus] 🐺🐒🏉","#ebdac2", "descr",
"descr"], ],
["Zanzo[Bus]","#FFFF", [
"descr"], "[Kar]aïbes", "#a5cfdd",
["Bran[Kar]","#6da1ac", "descr",
"Si vous ne connaissez pas le Bran[Kar], cest comme une grande famille qui fait un apéro, qui se bourre un peu la gueule en discutant des heures autour dune table remplie de bouffe et de super bons cocktails (la plupart des barmen/barwomen du bus sont les barmans de Shakens), sauf quon est un bus du Wei (vous comprendrez bien le nom de notre bus en voyant létat de certain·e·s). Il nous arrive de faire quelques conneries, mais surtout de jouer au Bière-pong en musique !"], ],
["Techno [kar]ade","#8065a3", [
"descr"], "[Kar]di [Bus]", "#e46398",
["[Bus]ka-P","#7c4768", "descr",
"descr"] ],
[
"Sparta[bus] 🐺🐒🏉", "#ebdac2",
"descr",
],
[
"Zanzo[Bus]", "#FFFF",
"descr",
],
[
"Bran[Kar]", "#6da1ac",
"""Si vous ne connaissez pas le Bran[Kar], cest comme une grande famille qui fait un apéro, qui se bourre un peu la
gueule en discutant des heures autour dune table remplie de bouffe et de super bons cocktails (la plupart des
barmen/barwomen du bus sont les barmans de Shakens), sauf quon est un bus du Wei (vous comprendrez bien le nom de notre
bus en voyant létat de certain·e·s). Il nous arrive de faire quelques conneries, mais surtout de jouer au Bière-pong en
musique !""",
],
[
"Techno [kar]ade", "#8065a3",
"descr"
],
[
"[Bus]ka-P", "#7c4768",
"descr",
],
] ]
def get_survey_info(id): def get_survey_info(id):
s = {"recap": { s = {"recap": {
"1": 0, "1": 0,
"2": 0, "2": 0,
"3": 0, "3": 0,
"4": 0, "4": 0,
"5": 0 "5": 0,
}} }}
s_ = {f"bus{id}" : {f"{i}" : 0 for i in range(1,5+1)} for id in range(len(buses_descr))} s_ = {f"bus{id}": {f"{i}": 0 for i in range(1, 5 + 1)} for id in range(len(buses_descr))}
s.update(s_) s.update(s_)
s.update({f"bus{id}" : {f"{i}" : i for i in range(1,5+1)}}) s.update({f"bus{id}": {f"{i}": i for i in range(1, 5 + 1)}})
return {"scores" : s} return {"scores": s}
def print_bus(id): def print_bus(id):
return buses_descr[id][0] + "\n\n" + buses_descr[id][2] return buses_descr[id][0] + "\n\n" + buses_descr[id][2]
def print_all_buses():
l = [print_bus(id) for id in range(len(buses_descr))]
return "\n\n---------\n\n".join(l)
WORDS = {"recap": ["Cher(e) 1A, te voilà arrivé(e) devant un choix fatidique, le choix de ton bus....... \n (Musique effrayante) \n Peite blagounette évidemment, chacun des bus te permettra de passer un excellent WEI ! Mais quitte à avoir le choix, voici la liste de tous les bus ainsi qu'une description détaillée de ces derniers ! Prends ton temps, observe les bien et quand tu te sens prêt(e), appuye sur le bouton 'Noter les bus' pour continuer (pas besoin d'apprendre par coeur les bus, la descirption du bus te sera rappeler avant de le noter !) \n\n\n" + print_all_buses(), { def print_all_buses():
1: "Noter les bus :" liste = [print_bus(id) for id in range(len(buses_descr))]
}]} return "<br><br>---------<br><br>".join(liste)
WORDS = {
"recap":
[
"""Chèr⋅e 1A, te voilà arrivé⋅e devant un choix fatidique, le choix de ton bus.......<br>
(Musique effrayante)<br>
Petite blagounette évidemment, chacun des bus te permettra de passer un excellent WEI !
Mais quitte à avoir le choix, voici la liste de tous les bus ainsi qu'une description détaillée de ces derniers !
Prends ton temps, observe les bien et quand tu te sens prête, appuye sur le bouton 'Noter les bus' pour continuer
(pas besoin d'apprendre par cœur les bus, la description du bus te sera rappeler avant de le noter !) <br><br><br>""" + print_all_buses(),
{
"1": "Noter les bus :",
}
]
}
WORDS.update({ WORDS.update({
f"bus{id}" : [print_bus(id),{i : f"Noter {i}/5" for i in range(1,5+1)}] for id in range(len(buses_descr)) f"bus{id}": [print_bus(id), {i: f"Noter {i}/5" for i in range(1, 5 + 1)}] for id in range(len(buses_descr))
}) })
class WEISurveyForm2024(forms.Form): class WEISurveyForm2024(forms.Form):
""" """
Survey form for the year 2024. Survey form for the year 2024.
@ -300,4 +335,3 @@ class WEISurveyAlgorithm2024(WEISurveyAlgorithm):
if tqdm_obj is not None: if tqdm_obj is not None:
tqdm_obj.n = len(surveys) - len(free_surveys) tqdm_obj.n = len(surveys) - len(free_surveys)
tqdm_obj.refresh() tqdm_obj.refresh()

View File

@ -6,8 +6,6 @@ from datetime import date, timedelta
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase from django.test import TestCase
from django.urls import reverse
from note.models import NoteUser
from ..forms.surveys.wei2023 import WEIBusInformation2023, WEISurvey2023, WORDS, WEISurveyInformation2023 from ..forms.surveys.wei2023 import WEIBusInformation2023, WEISurvey2023, WORDS, WEISurveyInformation2023
from ..models import Bus, WEIClub, WEIRegistration from ..models import Bus, WEIClub, WEIRegistration
@ -127,44 +125,3 @@ class TestWEIAlgorithm(TestCase):
self.assertLessEqual(max_score - score, 25) # Always less than 25 % of tolerance self.assertLessEqual(max_score - score, 25) # Always less than 25 % of tolerance
self.assertLessEqual(penalty / 100, 25) # Tolerance of 5 % self.assertLessEqual(penalty / 100, 25) # Tolerance of 5 %
def test_register_1a(self):
"""
Test register a first year member to the WEI and complete the survey
"""
response = self.client.get(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk)))
self.assertEqual(response.status_code, 200)
user = User.objects.create(username="toto", email="toto@example.com")
NoteUser.objects.create(user=user)
response = self.client.post(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk)), dict(
user=user.id,
soge_credit=True,
birth_date=date(2000, 1, 1),
gender='nonbinary',
clothing_cut='female',
clothing_size='XS',
health_issues='I am a bot',
emergency_contact_name='NoteKfet2020',
emergency_contact_phone='+33123456789',
))
qs = WEIRegistration.objects.filter(user_id=user.id)
self.assertTrue(qs.exists())
registration = qs.get()
self.assertRedirects(response, reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), 302, 200)
for question in WORDS:
# Fill 1A Survey, 20 pages
# be careful if questionnary form change (number of page, type of answer...)
response = self.client.post(reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), {
question: "1"
})
registration.refresh_from_db()
survey = WEISurvey2023(registration)
self.assertRedirects(response, reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), 302,
302 if survey.is_complete() else 200)
self.assertIsNotNone(getattr(survey.information, question), "Survey page " + question + " failed")
survey = WEISurvey2023(registration)
self.assertTrue(survey.is_complete())
survey.select_bus(self.buses[0])
survey.save()
self.assertIsNotNone(survey.information.get_selected_bus())

View File

@ -0,0 +1,172 @@
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import random
from datetime import date, timedelta
from django.contrib.auth.models import User
from django.test import TestCase
from django.urls import reverse
from note.models import NoteUser
from ..forms.surveys.wei2024 import WEIBusInformation2024, WEISurvey2024, WORDS, WEISurveyInformation2024
from ..models import Bus, WEIClub, WEIRegistration
class TestWEIAlgorithm(TestCase):
"""
Run some tests to ensure that the WEI algorithm is working well.
"""
fixtures = ('initial',)
def setUp(self):
"""
Create some test data, with one WEI and 10 buses with random score attributions.
"""
self.user = User.objects.create_superuser(
username="weiadmin",
password="admin",
email="admin@example.com",
)
self.user.save()
self.client.force_login(self.user)
sess = self.client.session
sess["permission_mask"] = 42
sess.save()
self.wei = WEIClub.objects.create(
name="WEI 2024",
email="wei2024@example.com",
parent_club_id=2,
membership_fee_paid=12500,
membership_fee_unpaid=5500,
membership_start='2024-01-01',
membership_end='2024-12-31',
date_start=date.today() + timedelta(days=2),
date_end='2024-12-31',
year=2024,
)
self.buses = []
for i in range(10):
bus = Bus.objects.create(wei=self.wei, name=f"Bus {i}", size=10)
self.buses.append(bus)
information = WEIBusInformation2024(bus)
for question in WORDS:
information.scores[question] = {answer: random.randint(1, 5) for answer in WORDS[question][1]}
information.save()
bus.save()
def test_survey_algorithm_small(self):
"""
There are only a few people in each bus, ensure that each person has its best bus
"""
# Add a few users
for i in range(10):
user = User.objects.create(username=f"user{i}")
registration = WEIRegistration.objects.create(
user=user,
wei=self.wei,
first_year=True,
birth_date='2000-01-01',
)
information = WEISurveyInformation2024(registration)
for question in WORDS:
options = list(WORDS[question][1].keys())
setattr(information, question, random.choice(options))
information.step = 20
information.save(registration)
registration.save()
# Run algorithm
WEISurvey2024.get_algorithm_class()().run_algorithm()
# Ensure that everyone has its first choice
for r in WEIRegistration.objects.filter(wei=self.wei).all():
survey = WEISurvey2024(r)
preferred_bus = survey.ordered_buses()[0][0]
chosen_bus = survey.information.get_selected_bus()
self.assertEqual(preferred_bus, chosen_bus)
def test_survey_algorithm_full(self):
"""
Buses are full of first year people, ensure that they are happy
"""
# Add a lot of users
for i in range(95):
user = User.objects.create(username=f"user{i}")
registration = WEIRegistration.objects.create(
user=user,
wei=self.wei,
first_year=True,
birth_date='2000-01-01',
)
information = WEISurveyInformation2024(registration)
for question in WORDS:
options = list(WORDS[question][1].keys())
setattr(information, question, random.choice(options))
information.step = 20
information.save(registration)
registration.save()
# Run algorithm
WEISurvey2024.get_algorithm_class()().run_algorithm()
penalty = 0
# Ensure that everyone seems to be happy
# We attribute a penalty for each user that didn't have its first choice
# The penalty is the square of the distance between the score of the preferred bus
# and the score of the attributed bus
# We consider it acceptable if the mean of this distance is lower than 5 %
for r in WEIRegistration.objects.filter(wei=self.wei).all():
survey = WEISurvey2024(r)
chosen_bus = survey.information.get_selected_bus()
buses = survey.ordered_buses()
score = min(v for bus, v in buses if bus == chosen_bus)
max_score = buses[0][1]
penalty += (max_score - score) ** 2
self.assertLessEqual(max_score - score, 25) # Always less than 25 % of tolerance
self.assertLessEqual(penalty / 100, 25) # Tolerance of 5 %
def test_register_1a(self):
"""
Test register a first year member to the WEI and complete the survey
"""
response = self.client.get(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk)))
self.assertEqual(response.status_code, 200)
user = User.objects.create(username="toto", email="toto@example.com")
NoteUser.objects.create(user=user)
response = self.client.post(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk)), dict(
user=user.id,
soge_credit=True,
birth_date=date(2000, 1, 1),
gender='nonbinary',
clothing_cut='female',
clothing_size='XS',
health_issues='I am a bot',
emergency_contact_name='NoteKfet2020',
emergency_contact_phone='+33123456789',
))
qs = WEIRegistration.objects.filter(user_id=user.id)
self.assertTrue(qs.exists())
registration = qs.get()
self.assertRedirects(response, reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), 302, 200)
for question in WORDS:
# Fill 1A Survey, 20 pages
# be careful if questionnary form change (number of page, type of answer...)
response = self.client.post(reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), {
question: "1"
})
registration.refresh_from_db()
survey = WEISurvey2024(registration)
self.assertRedirects(response, reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), 302,
302 if survey.is_complete() else 200)
self.assertIsNotNone(getattr(survey.information, question), "Survey page " + question + " failed")
survey = WEISurvey2024(registration)
self.assertTrue(survey.is_complete())
survey.select_bus(self.buses[0])
survey.save()
self.assertIsNotNone(survey.information.get_selected_bus())

View File

@ -767,7 +767,7 @@ class TestDefaultWEISurvey(TestCase):
WEISurvey.update_form(None, None) WEISurvey.update_form(None, None)
self.assertEqual(CurrentSurvey.get_algorithm_class().get_survey_class(), CurrentSurvey) self.assertEqual(CurrentSurvey.get_algorithm_class().get_survey_class(), CurrentSurvey)
self.assertEqual(CurrentSurvey.get_year(), 2023) self.assertEqual(CurrentSurvey.get_year(), 2024)
class TestWeiAPI(TestAPI): class TestWeiAPI(TestAPI):

View File

@ -888,6 +888,9 @@ 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
if "caution_check" in form.fields:
form.fields["caution_check"].initial = registration.caution_check
if registration.soge_credit: if registration.soge_credit:
form.fields["credit_type"].disabled = True form.fields["credit_type"].disabled = True
form.fields["credit_type"].initial = NoteSpecial.objects.get(special_type="Virement bancaire") form.fields["credit_type"].initial = NoteSpecial.objects.get(special_type="Virement bancaire")
@ -929,6 +932,9 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
club = registration.wei club = registration.wei
user = registration.user user = registration.user
if "caution_check" in form.data:
registration.caution_check = form.data["caution_check"] == "on"
registration.save()
membership = form.instance membership = form.instance
membership.user = user membership.user = user
membership.club = club membership.club = club

View File

@ -2726,6 +2726,7 @@ msgid "Credit from Société générale"
msgstr "Crédit de la Société générale" msgstr "Crédit de la Société générale"
#: apps/wei/models.py:188 #: apps/wei/models.py:188
#: apps/wei/forms/registration.py:84
msgid "Caution check given" msgid "Caution check given"
msgstr "Chèque de caution donné" msgstr "Chèque de caution donné"