mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-04 01:12:08 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			128 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			128 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# Copyright (C) 2018-2025 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 ..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 %
 |