From 9523b5f05ff51915534835f8e981bac36baaeef8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 4 Sep 2021 12:37:29 +0200 Subject: [PATCH 01/10] [WEI] Choose one word per bus in the survey Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index 8d5adfad..ddb0fecb 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -47,7 +47,13 @@ class WEISurveyForm2021(forms.Form): words = [] for _ignored in range(information.step + 1): # Generate N times words - words = [rng.choice(WORDS) for _ignored2 in range(10)] + words = None + preferred_words = {bus: [word for word in WORDS if WEIBusInformation2021(bus).scores[word] >= 50] + for bus in WEISurveyAlgorithm2021.get_buses()} + while words is None or len(set(words)) != len(words): + # Ensure that there is no the same word 2 times + words = [rng.choice(words) for _ignored2, words in preferred_words.items()] + rng.shuffle(words) words = [(w, w) for w in words] if self.data: self.fields["word"].choices = [(w, w) for w in WORDS] From 5bf6a5501d22ca2cd3c3d87a935d0cb58d765e63 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sat, 4 Sep 2021 13:03:38 +0200 Subject: [PATCH 02/10] [WEI] Fix test for 1A registration Signed-off-by: Yohann D'ANELLO --- apps/wei/tests/test_wei_registration.py | 59 +++---------------------- 1 file changed, 7 insertions(+), 52 deletions(-) diff --git a/apps/wei/tests/test_wei_registration.py b/apps/wei/tests/test_wei_registration.py index 2a49a5d8..bcd755d8 100644 --- a/apps/wei/tests/test_wei_registration.py +++ b/apps/wei/tests/test_wei_registration.py @@ -84,6 +84,13 @@ class TestWEIRegistration(TestCase): wei=self.wei, description="Test Bus", ) + + # Setup the bus + bus_info = CurrentSurvey.get_algorithm_class().get_bus_information(self.bus) + bus_info.scores["Jus de fruit"] = 70 + bus_info.save() + self.bus.save() + self.team = BusTeam.objects.create( name="Test Team", bus=self.bus, @@ -761,58 +768,6 @@ class TestDefaultWEISurvey(TestCase): self.assertEqual(CurrentSurvey.get_year(), 2021) -class TestWEISurveyAlgorithm(TestCase): - """ - Run the WEI Algorithm. - TODO: Improve this test with some test data once the algorithm will be implemented. - """ - fixtures = ("initial",) - - def setUp(self) -> None: - self.year = timezone.now().year - self.wei = WEIClub.objects.create( - name="Test WEI", - email="gc.wei@example.com", - parent_club_id=2, - membership_fee_paid=12500, - membership_fee_unpaid=5500, - membership_start=date(self.year, 1, 1), - membership_end=date(self.year, 12, 31), - year=self.year, - date_start=date.today() + timedelta(days=2), - date_end=date(self.year, 12, 31), - ) - NoteClub.objects.create(club=self.wei) - self.bus = Bus.objects.create( - name="Test Bus", - wei=self.wei, - description="Test Bus", - ) - self.team = BusTeam.objects.create( - name="Test Team", - bus=self.bus, - color=0xFFFFFF, - description="Test Team", - ) - - self.user = User.objects.create(username="toto") - self.registration = WEIRegistration.objects.create( - user_id=self.user.id, - wei_id=self.wei.id, - soge_credit=True, - caution_check=True, - birth_date=date(2000, 1, 1), - gender="nonbinary", - clothing_cut="male", - clothing_size="XL", - health_issues="I am a bot", - emergency_contact_name="Pikachu", - emergency_contact_phone="+33123456789", - first_year=True, - ) - CurrentSurvey(self.registration).save() - - class TestWeiAPI(TestAPI): def setUp(self) -> None: super().setUp() From a2a749e1ca3fcf941d42be4d815de8abece4cc10 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 17:12:41 +0200 Subject: [PATCH 03/10] [WEI] Fix permission check to register new accounts to users Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 21 +++++++++++---------- apps/wei/views.py | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index ddb0fecb..fd788a74 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -44,16 +44,17 @@ class WEISurveyForm2021(forms.Form): rng = Random(information.seed) - words = [] - for _ignored in range(information.step + 1): - # Generate N times words - words = None - preferred_words = {bus: [word for word in WORDS if WEIBusInformation2021(bus).scores[word] >= 50] - for bus in WEISurveyAlgorithm2021.get_buses()} - while words is None or len(set(words)) != len(words): - # Ensure that there is no the same word 2 times - words = [rng.choice(words) for _ignored2, words in preferred_words.items()] - rng.shuffle(words) + words = None + # Update seed + rng.randint(0, information.step) + + preferred_words = {bus: [word for word in WORDS if bus.size > 0 + and WEIBusInformation2021(bus).scores[word] >= 50] + for bus in WEISurveyAlgorithm2021.get_buses()} + while words is None or len(set(words)) != len(words): + # Ensure that there is no the same word 2 times + words = [rng.choice(words) for _ignored2, words in preferred_words.items()] + rng.shuffle(words) words = [(w, w) for w in words] if self.data: self.fields["word"].choices = [(w, w) for w in WORDS] diff --git a/apps/wei/views.py b/apps/wei/views.py index cf7c3911..231fc220 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -489,7 +489,7 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView): wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"]) return WEIRegistration( wei=wei, - user=self.request.user, + user=User.objects.get(username="note"), first_year=True, birth_date="1970-01-01", gender="No", @@ -557,7 +557,7 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"]) return WEIRegistration( wei=wei, - user=self.request.user, + user=User.objects.get(username="note"), first_year=True, birth_date="1970-01-01", gender="No", From f25eb1d2c5d972bc2ed39f0d078a0944e297c7aa Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 17:30:59 +0200 Subject: [PATCH 04/10] [WEI] Fix some issues Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 1 + apps/wei/views.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index fd788a74..16a9961c 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -40,6 +40,7 @@ class WEISurveyForm2021(forms.Form): if not information.seed: information.seed = int(1000 * time.time()) information.save(registration) + registration._force_save = True registration.save() rng = Random(information.seed) diff --git a/apps/wei/views.py b/apps/wei/views.py index 231fc220..2fb1f4a5 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -487,9 +487,13 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView): def get_sample_object(self): wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"]) + if "myself" in self.request.path: + user = self.request.user + else: + user = User.objects.get(username="note") return WEIRegistration( wei=wei, - user=User.objects.get(username="note"), + user=user, first_year=True, birth_date="1970-01-01", gender="No", @@ -555,9 +559,13 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): def get_sample_object(self): wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"]) + if "myself" in self.request.path: + user = self.request.user + else: + user = User.objects.get(username="note") return WEIRegistration( wei=wei, - user=User.objects.get(username="note"), + user=user, first_year=True, birth_date="1970-01-01", gender="No", From dbc7b3444b86a2a9b801eb6224929789727b5881 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 18:23:55 +0200 Subject: [PATCH 05/10] [WEI] Add script to import bus scores Signed-off-by: Yohann D'ANELLO --- apps/wei/management/commands/import_scores.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 apps/wei/management/commands/import_scores.py diff --git a/apps/wei/management/commands/import_scores.py b/apps/wei/management/commands/import_scores.py new file mode 100644 index 00000000..f8587cfd --- /dev/null +++ b/apps/wei/management/commands/import_scores.py @@ -0,0 +1,50 @@ +# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later +import argparse +import sys + +from django.core.management import BaseCommand +from django.db import transaction + +from ...forms import CurrentSurvey +from ...forms.surveys.wei2021 import WORDS # WARNING: this is specific to 2021 +from ...models import Bus + + +class Command(BaseCommand): + """ + This script is used to load scores for buses from a CSV file. + """ + def add_arguments(self, parser): + parser.add_argument('file', nargs='?', type=argparse.FileType('r'), default=sys.stdin, help='Input CSV file') + + @transaction.atomic + def handle(self, *args, **options): + file = options['file'] + head = file.readline().replace('\n', '') + bus_names = head.split(';') + bus_names = [name for name in bus_names if name] + buses = [] + for name in bus_names: + qs = Bus.objects.filter(name__iexact=name) + if not qs.exists(): + raise ValueError(f"Bus '{name}' does not exist") + buses.append(qs.get()) + + informations = {bus: CurrentSurvey.get_algorithm_class().get_bus_information(bus) for bus in buses} + + for line in file: + elem = line.split(';') + word = elem[0] + if word not in WORDS: + raise ValueError(f"Word {word} is not used") + + for i, bus in enumerate(buses): + info = informations[bus] + info.scores[word] = float(elem[i + 1].replace(',', '.')) + + for bus, info in informations.items(): + info.save() + bus.save() + if options['verbosity'] > 0: + self.stdout.write(f"Bus {bus.name} saved!") From d888d5863a31ea224bfb0b8ce423dc099562f8fb Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 18:39:03 +0200 Subject: [PATCH 06/10] [WEI] For each bus, choose a random word which score is higher than the mid score Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index 16a9961c..2ff8adcc 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -49,9 +49,13 @@ class WEISurveyForm2021(forms.Form): # Update seed rng.randint(0, information.step) - preferred_words = {bus: [word for word in WORDS if bus.size > 0 - and WEIBusInformation2021(bus).scores[word] >= 50] - for bus in WEISurveyAlgorithm2021.get_buses()} + buses = WEISurveyAlgorithm2021.get_buses() + scores = sum((list(WEIBusInformation2021(bus).scores.values()) for bus in buses), []) + average_score = sum(scores) / len(scores) + + preferred_words = {bus: [word for word in WORDS + if WEIBusInformation2021(bus).scores[word] >= average_score] + for bus in buses} while words is None or len(set(words)) != len(words): # Ensure that there is no the same word 2 times words = [rng.choice(words) for _ignored2, words in preferred_words.items()] From 6d2b7054e22a11ee6f1ef4c32426bb2776447d79 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 18:49:34 +0200 Subject: [PATCH 07/10] [WEI] Optimizations in survey load Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 37 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index 2ff8adcc..eaaba872 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -43,28 +43,29 @@ class WEISurveyForm2021(forms.Form): registration._force_save = True registration.save() - rng = Random(information.seed) - - words = None - # Update seed - rng.randint(0, information.step) - - buses = WEISurveyAlgorithm2021.get_buses() - scores = sum((list(WEIBusInformation2021(bus).scores.values()) for bus in buses), []) - average_score = sum(scores) / len(scores) - - preferred_words = {bus: [word for word in WORDS - if WEIBusInformation2021(bus).scores[word] >= average_score] - for bus in buses} - while words is None or len(set(words)) != len(words): - # Ensure that there is no the same word 2 times - words = [rng.choice(words) for _ignored2, words in preferred_words.items()] - rng.shuffle(words) - words = [(w, w) for w in words] if self.data: self.fields["word"].choices = [(w, w) for w in WORDS] if self.is_valid(): return + + rng = Random((information.step + 1) * information.seed) + + words = None + + buses = WEISurveyAlgorithm2021.get_buses() + informations = {bus: WEIBusInformation2021(bus) for bus in buses} + scores = sum((list(informations[bus].scores.values()) for bus in buses), []) + average_score = sum(scores) / len(scores) + + preferred_words = {bus: [word for word in WORDS + if informations[bus].scores[word] >= average_score] + for bus in buses} + while words is None or len(set(words)) != len(words): + print("toto") + # Ensure that there is no the same word 2 times + words = [rng.choice(words) for _ignored2, words in preferred_words.items()] + rng.shuffle(words) + words = [(w, w) for w in words] self.fields["word"].choices = words From ffaa0203109acbe654e1b828f103b4354fbfe3d6 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 18:52:57 +0200 Subject: [PATCH 08/10] Fix WEI registration in dev mode Signed-off-by: Yohann D'ANELLO --- apps/wei/views.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/wei/views.py b/apps/wei/views.py index 2fb1f4a5..a9d0c865 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -490,7 +490,10 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView): if "myself" in self.request.path: user = self.request.user else: - user = User.objects.get(username="note") + # To avoid unique validation issues, we use an account that can't join the WEI. + # In development mode, the note account may not exist, we use a random user (may fail) + user = User.objects.get(username="note") if User.objects.filter(username="note").exists() \ + else User.objects.first() return WEIRegistration( wei=wei, user=user, @@ -562,7 +565,10 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): if "myself" in self.request.path: user = self.request.user else: - user = User.objects.get(username="note") + # To avoid unique validation issues, we use an account that can't join the WEI. + # In development mode, the note account may not exist, we use a random user (may fail) + user = User.objects.get(username="note") if User.objects.filter(username="note").exists() \ + else User.objects.first() return WEIRegistration( wei=wei, user=user, From bb69627ac5438a5cd697908765ba5d9ebcdbedbd Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 18:57:07 +0200 Subject: [PATCH 09/10] Remove debug code Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index eaaba872..b0cfb0cb 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -61,7 +61,6 @@ class WEISurveyForm2021(forms.Form): if informations[bus].scores[word] >= average_score] for bus in buses} while words is None or len(set(words)) != len(words): - print("toto") # Ensure that there is no the same word 2 times words = [rng.choice(words) for _ignored2, words in preferred_words.items()] rng.shuffle(words) From e5051ab018710075049170aa6bd45a1e0100fa21 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 19:32:34 +0200 Subject: [PATCH 10/10] Linting Signed-off-by: Yohann D'ANELLO --- apps/wei/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/wei/views.py b/apps/wei/views.py index a9d0c865..cb8e3646 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -492,8 +492,8 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView): else: # To avoid unique validation issues, we use an account that can't join the WEI. # In development mode, the note account may not exist, we use a random user (may fail) - user = User.objects.get(username="note") if User.objects.filter(username="note").exists() \ - else User.objects.first() + user = User.objects.get(username="note") \ + if User.objects.filter(username="note").exists() else User.objects.first() return WEIRegistration( wei=wei, user=user, @@ -567,8 +567,8 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): else: # To avoid unique validation issues, we use an account that can't join the WEI. # In development mode, the note account may not exist, we use a random user (may fail) - user = User.objects.get(username="note") if User.objects.filter(username="note").exists() \ - else User.objects.first() + user = User.objects.get(username="note") \ + if User.objects.filter(username="note").exists() else User.objects.first() return WEIRegistration( wei=wei, user=user,