# Copyright (C) 2018-2023 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.wei2023 import WEIBusInformation2023, WEISurvey2023, WORDS, WEISurveyInformation2023 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 2023", email="wei2023@example.com", parent_club_id=2, membership_fee_paid=12500, membership_fee_unpaid=5500, membership_start='2023-01-01', membership_end='2023-12-31', date_start=date.today() + timedelta(days=2), date_end='2023-12-31', year=2023, ) 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 = WEIBusInformation2023(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 = WEISurveyInformation2023(registration) for question in WORDS: setattr(information, question, random.randint(1, 5)) information.step = 20 information.save(registration) registration.save() # Run algorithm WEISurvey2023.get_algorithm_class()().run_algorithm() # Ensure that everyone has its first choice for r in WEIRegistration.objects.filter(wei=self.wei).all(): survey = WEISurvey2023(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 = WEISurveyInformation2023(registration) for question in WORDS: setattr(information, question, random.randint(1, 5)) information.step = 20 information.save(registration) registration.save() # Run algorithm WEISurvey2023.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 = WEISurvey2023(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 = 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())