mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-31 15:50:03 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			130 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			130 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
 | |
| # SPDX-License-Identifier: GPL-3.0-or-later
 | |
| 
 | |
| import random
 | |
| 
 | |
| from django.contrib.auth.models import User
 | |
| from django.test import TestCase
 | |
| 
 | |
| from ..forms.surveys.wei2025 import WEIBusInformation2025, WEISurvey2025, WORDS, NB_WORDS, WEISurveyInformation2025
 | |
| 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.wei = WEIClub.objects.create(
 | |
|             name="WEI 2025",
 | |
|             email="wei2025@example.com",
 | |
|             date_start='2025-09-12',
 | |
|             date_end='2025-09-14',
 | |
|             year=2025,
 | |
|             membership_start='2025-06-01'
 | |
|         )
 | |
| 
 | |
|         self.buses = []
 | |
|         for i in range(8):
 | |
|             bus = Bus.objects.create(wei=self.wei, name=f"Bus {i}", size=10)
 | |
|             self.buses.append(bus)
 | |
|             information = WEIBusInformation2025(bus)
 | |
|             for word in WORDS['list']:
 | |
|                 information.scores[word] = random.randint(0, 6)
 | |
|             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 = WEISurveyInformation2025(registration)
 | |
|             for j in range(1, 1 + NB_WORDS):
 | |
|                 setattr(information, f'word{j}', random.choice(WORDS['list']))
 | |
|             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)
 | |
|             registration.save()
 | |
| 
 | |
|         # Run algorithm
 | |
|         WEISurvey2025.get_algorithm_class()().run_algorithm()
 | |
| 
 | |
|         # Ensure that everyone has its first choice
 | |
|         for r in WEIRegistration.objects.filter(wei=self.wei).all():
 | |
|             survey = WEISurvey2025(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(80):
 | |
|             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 = WEISurveyInformation2025(registration)
 | |
|             for j in range(1, 1 + NB_WORDS):
 | |
|                 setattr(information, f'word{j}', random.choice(WORDS['list']))
 | |
|             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)
 | |
|             registration.save()
 | |
|             survey = WEISurvey2025(registration)
 | |
| 
 | |
|         # Run algorithm
 | |
|         WEISurvey2025.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 = WEISurvey2025(r)
 | |
|             chosen_bus = survey.information.get_selected_bus()
 | |
|             buses = survey.ordered_buses()
 | |
|             self.assertIn(chosen_bus, [x[0] for x in buses])
 | |
|             score_questions, score_words = next(scores for bus, scores in buses if bus == chosen_bus)
 | |
|             max_score_questions = max(buses[i][1][0] for i in range(len(buses)))
 | |
|             max_score_words = max(buses[i][1][1] for i in range(len(buses)))
 | |
|             penalty += (max_score_words - score_words) ** 2
 | |
|             penalty += (max_score_questions - score_questions) ** 2
 | |
|         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)
 |