mirror of
https://gitlab.crans.org/bde/nk20
synced 2024-12-04 22:26:52 +00:00
a6b479db19
- /apps/activity/api/serializers.py - /apps/activity/api/urls.py - /apps/activity/api/views.py - /apps/activity/tests/test_activities.py - /apps/activity/__init__.py - /apps/activity/admin.py - /apps/activity/apps.py - /apps/activity/forms.py - /apps/activity/tables.py - /apps/activity/urls.py - /apps/activity/views.py - /apps/api/__init__.py - /apps/api/apps.py - /apps/api/serializers.py - /apps/api/tests.py - /apps/api/urls.py - /apps/api/views.py - /apps/api/viewsets.py - /apps/logs/signals.py - /apps/logs/apps.py - /apps/logs/__init__.py - /apps/logs/api/serializers.py - /apps/logs/api/urls.py - /apps/logs/api/views.py - /apps/member/api/serializers.py - /apps/member/api/urls.py - /apps/member/api/views.py - /apps/member/templatetags/memberinfo.py - /apps/member/__init__.py - /apps/member/admin.py - /apps/member/apps.py - /apps/member/auth.py - /apps/member/forms.py - /apps/member/hashers.py - /apps/member/signals.py - /apps/member/tables.py - /apps/member/urls.py - /apps/member/views.py - /apps/note/api/serializers.py - /apps/note/api/urls.py - /apps/note/api/views.py - /apps/note/models/__init__.py - /apps/note/static/note/js/consos.js - /apps/note/templates/note/mails/negative_balance.txt - /apps/note/templatetags/getenv.py - /apps/note/templatetags/pretty_money.py - /apps/note/tests/test_transactions.py - /apps/note/__init__.py - /apps/note/admin.py - /apps/note/apps.py - /apps/note/forms.py - /apps/note/signals.py - /apps/note/tables.py - /apps/note/urls.py - /apps/note/views.py - /apps/permission/api/serializers.py - /apps/permission/api/urls.py - /apps/permission/api/views.py - /apps/permission/templatetags/perms.py - /apps/permission/tests/test_oauth2.py - /apps/permission/tests/test_permission_denied.py - /apps/permission/tests/test_permission_queries.py - /apps/permission/tests/test_rights_page.py - /apps/permission/__init__.py - /apps/permission/admin.py - /apps/permission/backends.py - /apps/permission/apps.py - /apps/permission/decorators.py - /apps/permission/permissions.py - /apps/permission/scopes.py - /apps/permission/signals.py - /apps/permission/tables.py - /apps/permission/urls.py - /apps/permission/views.py - /apps/registration/tests/test_registration.py - /apps/registration/__init__.py - /apps/registration/apps.py - /apps/registration/forms.py - /apps/registration/tables.py - /apps/registration/tokens.py - /apps/registration/urls.py - /apps/registration/views.py - /apps/treasury/api/serializers.py - /apps/treasury/api/urls.py - /apps/treasury/api/views.py - /apps/treasury/templatetags/escape_tex.py - /apps/treasury/tests/test_treasury.py - /apps/treasury/__init__.py - /apps/treasury/admin.py - /apps/treasury/apps.py - /apps/treasury/forms.py - /apps/treasury/signals.py - /apps/treasury/tables.py - /apps/treasury/urls.py - /apps/treasury/views.py - /apps/wei/api/serializers.py - /apps/wei/api/urls.py - /apps/wei/api/views.py - /apps/wei/forms/surveys/__init__.py - /apps/wei/forms/surveys/base.py - /apps/wei/forms/surveys/wei2021.py - /apps/wei/forms/surveys/wei2022.py - /apps/wei/forms/surveys/wei2023.py - /apps/wei/forms/__init__.py - /apps/wei/forms/registration.py - /apps/wei/management/commands/export_wei_registrations.py - /apps/wei/management/commands/import_scores.py - /apps/wei/management/commands/wei_algorithm.py - /apps/wei/templates/wei/weilist_sample.tex - /apps/wei/tests/test_wei_algorithm_2021.py - /apps/wei/tests/test_wei_algorithm_2022.py - /apps/wei/tests/test_wei_algorithm_2023.py - /apps/wei/tests/test_wei_registration.py - /apps/wei/__init__.py - /apps/wei/admin.py - /apps/wei/apps.py - /apps/wei/tables.py - /apps/wei/urls.py - /apps/wei/views.py - /note_kfet/settings/__init__.py - /note_kfet/settings/base.py - /note_kfet/settings/development.py - /note_kfet/settings/secrets_example.py - /note_kfet/static/js/base.js - /note_kfet/admin.py - /note_kfet/inputs.py - /note_kfet/middlewares.py - /note_kfet/urls.py - /note_kfet/views.py - /note_kfet/wsgi.py - /entrypoint.sh
238 lines
7.5 KiB
Python
238 lines
7.5 KiB
Python
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from typing import Optional, List
|
|
|
|
from django.db.models import QuerySet
|
|
from django.forms import Form
|
|
|
|
from ...models import WEIClub, WEIRegistration, Bus, WEIMembership
|
|
|
|
|
|
class WEISurveyInformation:
|
|
"""
|
|
Abstract data of the survey.
|
|
"""
|
|
valid = False
|
|
selected_bus_pk = None
|
|
selected_bus_name = None
|
|
|
|
def __init__(self, registration):
|
|
self.__dict__.update(registration.information)
|
|
|
|
def get_selected_bus(self) -> Optional[Bus]:
|
|
"""
|
|
If the algorithm ran, return the prefered bus according to the survey.
|
|
In the other case, return None.
|
|
"""
|
|
return Bus.objects.get(pk=self.selected_bus_pk) if self.valid else None
|
|
|
|
def save(self, registration) -> None:
|
|
"""
|
|
Store the data of the survey into the database, with the information of the registration.
|
|
"""
|
|
registration.information = self.__dict__
|
|
|
|
|
|
class WEIBusInformation:
|
|
"""
|
|
Abstract data of the bus.
|
|
"""
|
|
|
|
def __init__(self, bus: Bus):
|
|
self.__dict__.update(bus.information)
|
|
self.bus = bus
|
|
self.save()
|
|
|
|
def save(self):
|
|
d = self.__dict__.copy()
|
|
d.pop("bus")
|
|
self.bus.information = d
|
|
self.bus.save()
|
|
|
|
def free_seats(self, surveys: List["WEISurvey"] = None, quotas=None):
|
|
if not quotas:
|
|
size = self.bus.size
|
|
already_occupied = WEIMembership.objects.filter(bus=self.bus).count()
|
|
quotas = {self.bus: size - already_occupied}
|
|
|
|
quota = quotas[self.bus]
|
|
valid_surveys = sum(1 for survey in surveys if survey.information.valid
|
|
and survey.information.get_selected_bus() == self.bus) if surveys else 0
|
|
return quota - valid_surveys
|
|
|
|
def has_free_seats(self, surveys=None, quotas=None):
|
|
return self.free_seats(surveys, quotas) > 0
|
|
|
|
|
|
class WEISurveyAlgorithm:
|
|
"""
|
|
Abstract algorithm that attributes a bus to each new member.
|
|
"""
|
|
|
|
@classmethod
|
|
def get_survey_class(cls):
|
|
"""
|
|
The class of the survey associated with this algorithm.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@classmethod
|
|
def get_bus_information_class(cls):
|
|
"""
|
|
The class of the information associated to a bus extending WEIBusInformation.
|
|
Default: WEIBusInformation (contains nothing)
|
|
"""
|
|
return WEIBusInformation
|
|
|
|
@classmethod
|
|
def get_registrations(cls) -> QuerySet:
|
|
"""
|
|
Queryset of all first year registrations
|
|
"""
|
|
if not hasattr(cls, '_registrations'):
|
|
cls._registrations = WEIRegistration.objects.filter(wei__year=cls.get_survey_class().get_year(),
|
|
first_year=True).all()
|
|
|
|
return cls._registrations
|
|
|
|
@classmethod
|
|
def get_buses(cls) -> QuerySet:
|
|
"""
|
|
Queryset of all buses of the associated wei.
|
|
"""
|
|
if not hasattr(cls, '_buses'):
|
|
cls._buses = Bus.objects.filter(wei__year=cls.get_survey_class().get_year(), size__gt=0).all()
|
|
return cls._buses
|
|
|
|
@classmethod
|
|
def get_bus_information(cls, bus):
|
|
"""
|
|
Return the WEIBusInformation object containing the data stored in a given bus.
|
|
"""
|
|
return cls.get_bus_information_class()(bus)
|
|
|
|
def run_algorithm(self) -> None:
|
|
"""
|
|
Once this method implemented, run the algorithm that attributes a bus to each first year member.
|
|
This method can be run in command line through ``python manage.py wei_algorithm``
|
|
See ``wei.management.commmands.wei_algorithm``
|
|
This method must call Survey.select_bus for each survey.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
|
|
class WEISurvey:
|
|
"""
|
|
Survey associated to a first year WEI registration.
|
|
The data is stored into WEISurveyInformation, this class acts as a manager.
|
|
This is an abstract class: this has to be extended each year to implement custom methods.
|
|
"""
|
|
|
|
def __init__(self, registration: WEIRegistration):
|
|
self.registration = registration
|
|
self.information = self.get_survey_information_class()(registration)
|
|
|
|
@classmethod
|
|
def get_year(cls) -> int:
|
|
"""
|
|
Get year of the wei concerned by the type of the survey.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@classmethod
|
|
def get_wei(cls) -> WEIClub:
|
|
"""
|
|
The WEI associated to this kind of survey.
|
|
"""
|
|
if not hasattr(cls, '_wei'):
|
|
cls._wei = WEIClub.objects.get(year=cls.get_year())
|
|
|
|
return cls._wei
|
|
|
|
@classmethod
|
|
def get_survey_information_class(cls):
|
|
"""
|
|
The class of the data (extending WEISurveyInformation).
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def get_form_class(self) -> Form:
|
|
"""
|
|
The form class of the survey.
|
|
This is proper to the status of the survey: the form class can evolve according to the progress of the survey.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def update_form(self, form) -> None:
|
|
"""
|
|
Once the form is instanciated, the information can be updated with the information of the registration
|
|
and the information of the survey.
|
|
This method is called once the form is created.
|
|
"""
|
|
pass
|
|
|
|
def form_valid(self, form) -> None:
|
|
"""
|
|
Called when the information of the form are validated.
|
|
This method should update the information of the survey.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def is_complete(self) -> bool:
|
|
"""
|
|
Return True if the survey is complete.
|
|
If the survey is complete, then the button "Next" will display some text for the end of the survey.
|
|
If not, the survey is reloaded and continues.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def save(self) -> None:
|
|
"""
|
|
Store the information of the survey into the database.
|
|
"""
|
|
self.information.save(self.registration)
|
|
# The information is forced-saved.
|
|
# We don't want that anyone can update manually the information, so since most users don't have the
|
|
# right to save the information of a registration, we force save.
|
|
self.registration._force_save = True
|
|
self.registration.save()
|
|
|
|
@classmethod
|
|
def get_algorithm_class(cls):
|
|
"""
|
|
Algorithm class associated to the survey.
|
|
The algorithm, extending WEISurveyAlgorithm, should associate a bus to each first year member.
|
|
The association is not permanent: that's only a suggestion.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def select_bus(self, bus) -> None:
|
|
"""
|
|
Set the suggestion into the data of the membership.
|
|
:param bus: The bus suggested.
|
|
"""
|
|
self.information.selected_bus_pk = bus.pk
|
|
self.information.selected_bus_name = bus.name
|
|
self.information.valid = True
|
|
|
|
def free(self) -> None:
|
|
"""
|
|
Unselect the select bus.
|
|
"""
|
|
self.information.selected_bus_pk = None
|
|
self.information.selected_bus_name = None
|
|
self.information.valid = False
|
|
|
|
@classmethod
|
|
def clear_cache(cls):
|
|
"""
|
|
Clear stored information.
|
|
"""
|
|
if hasattr(cls, '_wei'):
|
|
del cls._wei
|
|
if hasattr(cls.get_algorithm_class(), '_registrations'):
|
|
del cls.get_algorithm_class()._registrations
|
|
if hasattr(cls.get_algorithm_class(), '_buses'):
|
|
del cls.get_algorithm_class()._buses
|