mirror of
https://gitlab.crans.org/bde/nk20
synced 2024-11-27 02:43:01 +00:00
Merge branch 'wei' into 'master'
[WEI] Algo de répartition Closes #97 et #98 See merge request bde/nk20!180
This commit is contained in:
commit
11dd8adbb7
@ -46,7 +46,8 @@ class SignUpForm(UserCreationForm):
|
|||||||
|
|
||||||
class DeclareSogeAccountOpenedForm(forms.Form):
|
class DeclareSogeAccountOpenedForm(forms.Form):
|
||||||
soge_account = forms.BooleanField(
|
soge_account = forms.BooleanField(
|
||||||
label=_("I declare that I opened a bank account in the Société générale with the BDE partnership."),
|
label=_("I declare that I opened or I will open soon a bank account in the Société générale with the BDE \
|
||||||
|
partnership."),
|
||||||
help_text=_("Warning: this engages you to open your bank account. If you finally decides to don't open your "
|
help_text=_("Warning: this engages you to open your bank account. If you finally decides to don't open your "
|
||||||
"account, you will have to pay the BDE membership."),
|
"account, you will have to pay the BDE membership."),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import datetime
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -305,8 +305,16 @@ class SogeCredit(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def amount(self):
|
def amount(self):
|
||||||
return self.credit_transaction.total if self.valid \
|
if self.valid:
|
||||||
else sum(transaction.total for transaction in self.transactions.all())
|
return self.credit_transaction.total
|
||||||
|
amount = sum(transaction.total for transaction in self.transactions.all())
|
||||||
|
if 'wei' in settings.INSTALLED_APPS:
|
||||||
|
from wei.models import WEIMembership
|
||||||
|
if not WEIMembership.objects.filter(club__weiclub__year=datetime.date.today().year, user=self.user)\
|
||||||
|
.exists():
|
||||||
|
# 80 € for people that don't go to WEI
|
||||||
|
amount += 8000
|
||||||
|
return amount
|
||||||
|
|
||||||
def update_transactions(self):
|
def update_transactions(self):
|
||||||
"""
|
"""
|
||||||
@ -323,13 +331,15 @@ class SogeCredit(models.Model):
|
|||||||
|
|
||||||
if bde_qs.exists():
|
if bde_qs.exists():
|
||||||
m = bde_qs.get()
|
m = bde_qs.get()
|
||||||
if m.transaction not in self.transactions.all():
|
if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
||||||
self.transactions.add(m.transaction)
|
if m.transaction not in self.transactions.all():
|
||||||
|
self.transactions.add(m.transaction)
|
||||||
|
|
||||||
if kfet_qs.exists():
|
if kfet_qs.exists():
|
||||||
m = kfet_qs.get()
|
m = kfet_qs.get()
|
||||||
if m.transaction not in self.transactions.all():
|
if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
||||||
self.transactions.add(m.transaction)
|
if m.transaction not in self.transactions.all():
|
||||||
|
self.transactions.add(m.transaction)
|
||||||
|
|
||||||
if 'wei' in settings.INSTALLED_APPS:
|
if 'wei' in settings.INSTALLED_APPS:
|
||||||
from wei.models import WEIClub
|
from wei.models import WEIClub
|
||||||
@ -337,8 +347,9 @@ class SogeCredit(models.Model):
|
|||||||
wei_qs = Membership.objects.filter(user=self.user, club=wei, date_start__gte=wei.membership_start)
|
wei_qs = Membership.objects.filter(user=self.user, club=wei, date_start__gte=wei.membership_start)
|
||||||
if wei_qs.exists():
|
if wei_qs.exists():
|
||||||
m = wei_qs.get()
|
m = wei_qs.get()
|
||||||
if m.transaction not in self.transactions.all():
|
if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
||||||
self.transactions.add(m.transaction)
|
if m.transaction not in self.transactions.all():
|
||||||
|
self.transactions.add(m.transaction)
|
||||||
|
|
||||||
for tr in self.transactions.all():
|
for tr in self.transactions.all():
|
||||||
tr.valid = False
|
tr.valid = False
|
||||||
@ -432,6 +443,7 @@ class SogeCredit(models.Model):
|
|||||||
# was opened after the validation of the account.
|
# was opened after the validation of the account.
|
||||||
self.credit_transaction.valid = False
|
self.credit_transaction.valid = False
|
||||||
self.credit_transaction.reason += " (invalide)"
|
self.credit_transaction.reason += " (invalide)"
|
||||||
|
self.credit_transaction._force_save = True
|
||||||
self.credit_transaction.save()
|
self.credit_transaction.save()
|
||||||
super().delete(**kwargs)
|
super().delete(**kwargs)
|
||||||
|
|
||||||
|
@ -50,15 +50,19 @@ class WEIBusInformation:
|
|||||||
self.bus.information = d
|
self.bus.information = d
|
||||||
self.bus.save()
|
self.bus.save()
|
||||||
|
|
||||||
def free_seats(self, surveys: List["WEISurvey"] = None):
|
def free_seats(self, surveys: List["WEISurvey"] = None, quotas=None):
|
||||||
size = self.bus.size
|
if not quotas:
|
||||||
already_occupied = WEIMembership.objects.filter(bus=self.bus).count()
|
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
|
valid_surveys = sum(1 for survey in surveys if survey.information.valid
|
||||||
and survey.information.get_selected_bus() == self.bus) if surveys else 0
|
and survey.information.get_selected_bus() == self.bus) if surveys else 0
|
||||||
return size - already_occupied - valid_surveys
|
return quota - valid_surveys
|
||||||
|
|
||||||
def has_free_seats(self, surveys=None):
|
def has_free_seats(self, surveys=None, quotas=None):
|
||||||
return self.free_seats(surveys) > 0
|
return self.free_seats(surveys, quotas) > 0
|
||||||
|
|
||||||
|
|
||||||
class WEISurveyAlgorithm:
|
class WEISurveyAlgorithm:
|
||||||
@ -86,14 +90,20 @@ class WEISurveyAlgorithm:
|
|||||||
"""
|
"""
|
||||||
Queryset of all first year registrations
|
Queryset of all first year registrations
|
||||||
"""
|
"""
|
||||||
return WEIRegistration.objects.filter(wei__year=cls.get_survey_class().get_year(), first_year=True)
|
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
|
@classmethod
|
||||||
def get_buses(cls) -> QuerySet:
|
def get_buses(cls) -> QuerySet:
|
||||||
"""
|
"""
|
||||||
Queryset of all buses of the associated wei.
|
Queryset of all buses of the associated wei.
|
||||||
"""
|
"""
|
||||||
return Bus.objects.filter(wei__year=cls.get_survey_class().get_year(), size__gt=0)
|
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
|
@classmethod
|
||||||
def get_bus_information(cls, bus):
|
def get_bus_information(cls, bus):
|
||||||
@ -135,7 +145,10 @@ class WEISurvey:
|
|||||||
"""
|
"""
|
||||||
The WEI associated to this kind of survey.
|
The WEI associated to this kind of survey.
|
||||||
"""
|
"""
|
||||||
return WEIClub.objects.get(year=cls.get_year())
|
if not hasattr(cls, '_wei'):
|
||||||
|
cls._wei = WEIClub.objects.get(year=cls.get_year())
|
||||||
|
|
||||||
|
return cls._wei
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_survey_information_class(cls):
|
def get_survey_information_class(cls):
|
||||||
@ -210,3 +223,15 @@ class WEISurvey:
|
|||||||
self.information.selected_bus_pk = None
|
self.information.selected_bus_pk = None
|
||||||
self.information.selected_bus_name = None
|
self.information.selected_bus_name = None
|
||||||
self.information.valid = False
|
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
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
from functools import lru_cache
|
||||||
from random import Random
|
from random import Random
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.db.models import Q
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInformation
|
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInformation
|
||||||
|
from ...models import WEIMembership
|
||||||
|
|
||||||
WORDS = [
|
WORDS = [
|
||||||
'13 organisé', '3ième mi temps', 'Années 2000', 'Apéro', 'BBQ', 'BP', 'Beauf', 'Binge drinking', 'Bon enfant',
|
'13 organisé', '3ième mi temps', 'Années 2000', 'Apéro', 'BBQ', 'BP', 'Beauf', 'Binge drinking', 'Bon enfant',
|
||||||
@ -135,20 +139,41 @@ class WEISurvey2021(WEISurvey):
|
|||||||
"""
|
"""
|
||||||
return self.information.step == 20
|
return self.information.step == 20
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@lru_cache()
|
||||||
|
def word_mean(cls, word):
|
||||||
|
"""
|
||||||
|
Calculate the mid-score given by all buses.
|
||||||
|
"""
|
||||||
|
buses = cls.get_algorithm_class().get_buses()
|
||||||
|
return sum([cls.get_algorithm_class().get_bus_information(bus).scores[word] for bus in buses]) / buses.count()
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
def score(self, bus):
|
def score(self, bus):
|
||||||
if not self.is_complete():
|
if not self.is_complete():
|
||||||
raise ValueError("Survey is not ended, can't calculate score")
|
raise ValueError("Survey is not ended, can't calculate score")
|
||||||
bus_info = self.get_algorithm_class().get_bus_information(bus)
|
|
||||||
return sum(bus_info.scores[getattr(self.information, 'word' + str(i))] for i in range(1, 21)) / 20
|
|
||||||
|
|
||||||
|
bus_info = self.get_algorithm_class().get_bus_information(bus)
|
||||||
|
# Score is the given score by the bus subtracted to the mid-score of the buses.
|
||||||
|
s = sum(bus_info.scores[getattr(self.information, 'word' + str(i))]
|
||||||
|
- self.word_mean(getattr(self.information, 'word' + str(i))) for i in range(1, 21)) / 20
|
||||||
|
return s
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
def scores_per_bus(self):
|
def scores_per_bus(self):
|
||||||
return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()}
|
return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()}
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
def ordered_buses(self):
|
def ordered_buses(self):
|
||||||
values = list(self.scores_per_bus().items())
|
values = list(self.scores_per_bus().items())
|
||||||
values.sort(key=lambda item: -item[1])
|
values.sort(key=lambda item: -item[1])
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_cache(cls):
|
||||||
|
cls.word_mean.cache_clear()
|
||||||
|
return super().clear_cache()
|
||||||
|
|
||||||
|
|
||||||
class WEISurveyAlgorithm2021(WEISurveyAlgorithm):
|
class WEISurveyAlgorithm2021(WEISurveyAlgorithm):
|
||||||
"""
|
"""
|
||||||
@ -164,19 +189,72 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm):
|
|||||||
def get_bus_information_class(cls):
|
def get_bus_information_class(cls):
|
||||||
return WEIBusInformation2021
|
return WEIBusInformation2021
|
||||||
|
|
||||||
def run_algorithm(self):
|
def run_algorithm(self, display_tqdm=False):
|
||||||
"""
|
"""
|
||||||
Gale-Shapley algorithm implementation.
|
Gale-Shapley algorithm implementation.
|
||||||
We modify it to allow buses to have multiple "weddings".
|
We modify it to allow buses to have multiple "weddings".
|
||||||
"""
|
"""
|
||||||
surveys = list(self.get_survey_class()(r) for r in self.get_registrations()) # All surveys
|
surveys = list(self.get_survey_class()(r) for r in self.get_registrations()) # All surveys
|
||||||
surveys = [s for s in surveys if s.is_complete()]
|
surveys = [s for s in surveys if s.is_complete()] # Don't consider invalid surveys
|
||||||
free_surveys = [s for s in surveys if not s.information.valid] # Remaining surveys
|
# Don't manage hardcoded people
|
||||||
|
surveys = [s for s in surveys if not hasattr(s.information, 'hardcoded') or not s.information.hardcoded]
|
||||||
|
|
||||||
|
# Reset previous algorithm run
|
||||||
|
for survey in surveys:
|
||||||
|
survey.free()
|
||||||
|
survey.save()
|
||||||
|
|
||||||
|
non_men = [s for s in surveys if s.registration.gender != 'male']
|
||||||
|
men = [s for s in surveys if s.registration.gender == 'male']
|
||||||
|
|
||||||
|
quotas = {}
|
||||||
|
registrations = self.get_registrations()
|
||||||
|
non_men_total = registrations.filter(~Q(gender='male')).count()
|
||||||
|
for bus in self.get_buses():
|
||||||
|
free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
|
||||||
|
# Remove hardcoded people
|
||||||
|
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
|
||||||
|
registration__information_json__icontains="hardcoded").count()
|
||||||
|
quotas[bus] = 4 + int(non_men_total / registrations.count() * free_seats)
|
||||||
|
|
||||||
|
tqdm_obj = None
|
||||||
|
if display_tqdm:
|
||||||
|
from tqdm import tqdm
|
||||||
|
tqdm_obj = tqdm(total=len(non_men), desc="Non-hommes")
|
||||||
|
|
||||||
|
# Repartition for non men people first
|
||||||
|
self.make_repartition(non_men, quotas, tqdm_obj=tqdm_obj)
|
||||||
|
|
||||||
|
quotas = {}
|
||||||
|
for bus in self.get_buses():
|
||||||
|
free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
|
||||||
|
free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk)
|
||||||
|
# Remove hardcoded people
|
||||||
|
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
|
||||||
|
registration__information_json__icontains="hardcoded").count()
|
||||||
|
quotas[bus] = free_seats
|
||||||
|
|
||||||
|
if display_tqdm:
|
||||||
|
tqdm_obj.close()
|
||||||
|
|
||||||
|
from tqdm import tqdm
|
||||||
|
tqdm_obj = tqdm(total=len(men), desc="Hommes")
|
||||||
|
|
||||||
|
self.make_repartition(men, quotas, tqdm_obj=tqdm_obj)
|
||||||
|
|
||||||
|
if display_tqdm:
|
||||||
|
tqdm_obj.close()
|
||||||
|
|
||||||
|
# Clear cache information after running algorithm
|
||||||
|
WEISurvey2021.clear_cache()
|
||||||
|
|
||||||
|
def make_repartition(self, surveys, quotas=None, tqdm_obj=None):
|
||||||
|
free_surveys = surveys.copy() # Remaining surveys
|
||||||
while free_surveys: # Some students are not affected
|
while free_surveys: # Some students are not affected
|
||||||
survey = free_surveys[0]
|
survey = free_surveys[0]
|
||||||
buses = survey.ordered_buses() # Preferences of the student
|
buses = survey.ordered_buses() # Preferences of the student
|
||||||
for bus, _ignored in buses:
|
for bus, current_score in buses:
|
||||||
if self.get_bus_information(bus).has_free_seats(surveys):
|
if self.get_bus_information(bus).has_free_seats(surveys, quotas):
|
||||||
# Selected bus has free places. Put student in the bus
|
# Selected bus has free places. Put student in the bus
|
||||||
survey.select_bus(bus)
|
survey.select_bus(bus)
|
||||||
survey.save()
|
survey.save()
|
||||||
@ -184,7 +262,6 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm):
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# Current bus has not enough places. Remove the least preferred student from the bus if existing
|
# Current bus has not enough places. Remove the least preferred student from the bus if existing
|
||||||
current_score = survey.score(bus)
|
|
||||||
least_preferred_survey = None
|
least_preferred_survey = None
|
||||||
least_score = -1
|
least_score = -1
|
||||||
# Find the least student in the bus that has a lower score than the current student
|
# Find the least student in the bus that has a lower score than the current student
|
||||||
@ -206,6 +283,11 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm):
|
|||||||
free_surveys.append(least_preferred_survey)
|
free_surveys.append(least_preferred_survey)
|
||||||
survey.select_bus(bus)
|
survey.select_bus(bus)
|
||||||
survey.save()
|
survey.save()
|
||||||
|
free_surveys.remove(survey)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"User {survey.registration.user} has no free seat")
|
raise ValueError(f"User {survey.registration.user} has no free seat")
|
||||||
|
|
||||||
|
if tqdm_obj is not None:
|
||||||
|
tqdm_obj.n = len(surveys) - len(free_surveys)
|
||||||
|
tqdm_obj.refresh()
|
||||||
|
@ -24,7 +24,15 @@ class Command(BaseCommand):
|
|||||||
sid = transaction.savepoint()
|
sid = transaction.savepoint()
|
||||||
|
|
||||||
algorithm = CurrentSurvey.get_algorithm_class()()
|
algorithm = CurrentSurvey.get_algorithm_class()()
|
||||||
algorithm.run_algorithm()
|
|
||||||
|
try:
|
||||||
|
from tqdm import tqdm
|
||||||
|
del tqdm
|
||||||
|
display_tqdm = True
|
||||||
|
except ImportError:
|
||||||
|
display_tqdm = False
|
||||||
|
|
||||||
|
algorithm.run_algorithm(display_tqdm=display_tqdm)
|
||||||
|
|
||||||
output = options['output']
|
output = options['output']
|
||||||
registrations = algorithm.get_registrations()
|
registrations = algorithm.get_registrations()
|
||||||
@ -34,8 +42,13 @@ class Command(BaseCommand):
|
|||||||
for bus, members in per_bus.items():
|
for bus, members in per_bus.items():
|
||||||
output.write(bus.name + "\n")
|
output.write(bus.name + "\n")
|
||||||
output.write("=" * len(bus.name) + "\n")
|
output.write("=" * len(bus.name) + "\n")
|
||||||
|
_order = -1
|
||||||
for r in members:
|
for r in members:
|
||||||
output.write(r.user.username + "\n")
|
survey = CurrentSurvey(r)
|
||||||
|
for _order, (b, _score) in enumerate(survey.ordered_buses()):
|
||||||
|
if b == bus:
|
||||||
|
break
|
||||||
|
output.write(f"{r.user.username} ({_order + 1})\n")
|
||||||
output.write("\n")
|
output.write("\n")
|
||||||
|
|
||||||
if not options['doit']:
|
if not options['doit']:
|
||||||
|
@ -95,7 +95,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if can_validate_1a or True %}
|
{% if can_validate_1a %}
|
||||||
<a href="{% url 'wei:wei_1A_list' pk=object.pk %}" class="btn btn-block btn-info">{% trans "Attribute buses" %}</a>
|
<a href="{% url 'wei:wei_1A_list' pk=object.pk %}" class="btn btn-block btn-info">{% trans "Attribute buses" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
\usepackage{fontspec}
|
\usepackage{fontspec}
|
||||||
\usepackage[margin=1.5cm]{geometry}
|
\usepackage[margin=1.5cm]{geometry}
|
||||||
|
\usepackage{longtable}
|
||||||
|
|
||||||
\begin{document}
|
\begin{document}
|
||||||
\begin{center}
|
\begin{center}
|
||||||
@ -19,7 +20,7 @@
|
|||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\footnotesize
|
\footnotesize
|
||||||
\begin{tabular}{ccccccccc}
|
\begin{longtable}{ccccccccc}
|
||||||
\textbf{Nom} & \textbf{Prénom} & \textbf{Date de naissance} & \textbf{Genre} & \textbf{Section}
|
\textbf{Nom} & \textbf{Prénom} & \textbf{Date de naissance} & \textbf{Genre} & \textbf{Section}
|
||||||
& \textbf{Bus} & \textbf{Équipe} & \textbf{Rôles} \\
|
& \textbf{Bus} & \textbf{Équipe} & \textbf{Rôles} \\
|
||||||
{% for membership in memberships %}
|
{% for membership in memberships %}
|
||||||
@ -27,20 +28,20 @@
|
|||||||
& {{ membership.registration.get_gender_display|safe }} & {{ membership.user.profile.section_generated|safe }} & {{ membership.bus.name|safe }}
|
& {{ membership.registration.get_gender_display|safe }} & {{ membership.user.profile.section_generated|safe }} & {{ membership.bus.name|safe }}
|
||||||
& {% if membership.team %}{{ membership.team.name|safe }}{% else %}--{% endif %} & {{ membership.roles.first|safe }} \\
|
& {% if membership.team %}{{ membership.team.name|safe }}{% else %}--{% endif %} & {{ membership.roles.first|safe }} \\
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
\end{tabular}
|
\end{longtable}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
\footnotesize
|
\footnotesize
|
||||||
Section = Année à l'ENS + code du département
|
Section = Année à l'ENS + code du département
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tabular}{ccccccccc}
|
\begin{longtable}{ccccccccc}
|
||||||
\textbf{Code} & A0 & A1 & A2 & A'2 & A''2 & A3 & B1234 & B1 \\
|
\textbf{Code} & A0 & A1 & A2 & A'2 & A''2 & A3 & B1234 & B1 \\
|
||||||
\textbf{Département} & Informatique & Maths & Physique & Physique appliquée & Chimie & Biologie & SAPHIRE & Mécanique \\
|
\textbf{Département} & Informatique & Maths & Physique & Physique appliquée & Chimie & Biologie & SAPHIRE & Mécanique \\
|
||||||
\hline
|
\hline
|
||||||
\textbf{Code} & B2 & B3 & B4 & C & D2 & D3 & E & EXT \\
|
\textbf{Code} & B2 & B3 & B4 & C & D2 & D3 & E & EXT \\
|
||||||
\textbf{Département} & Génie civil & Génie mécanique & EEA & Design & Éco-gestion & Sciences sociales & Anglais & Extérieur
|
\textbf{Département} & Génie civil & Génie mécanique & EEA & Design & Éco-gestion & Sciences sociales & Anglais & Extérieur
|
||||||
\end{tabular}
|
\end{longtable}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
\end{document}
|
\end{document}
|
||||||
|
@ -7,6 +7,7 @@ import subprocess
|
|||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
@ -191,6 +192,10 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
|||||||
|
|
||||||
context["not_first_year"] = WEIMembership.objects.filter(user=self.request.user).exists()
|
context["not_first_year"] = WEIMembership.objects.filter(user=self.request.user).exists()
|
||||||
|
|
||||||
|
qs = WEIMembership.objects.filter(club=club, registration__first_year=True, bus__isnull=True)
|
||||||
|
context["can_validate_1a"] = PermissionBackend.check_perm(
|
||||||
|
self.request, "wei.change_weimembership_bus", qs.first()) if qs.exists() else False
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -551,6 +556,12 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
" participated to a WEI."))
|
" participated to a WEI."))
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
if 'treasury' in settings.INSTALLED_APPS:
|
||||||
|
from treasury.models import SogeCredit
|
||||||
|
form.instance.soge_credit = \
|
||||||
|
form.instance.soge_credit \
|
||||||
|
or SogeCredit.objects.filter(user=form.instance.user, credit_transaction__valid=False).exists()
|
||||||
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
@ -652,6 +663,12 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
form.instance.information = information
|
form.instance.information = information
|
||||||
form.instance.save()
|
form.instance.save()
|
||||||
|
|
||||||
|
if 'treasury' in settings.INSTALLED_APPS:
|
||||||
|
from treasury.models import SogeCredit
|
||||||
|
form.instance.soge_credit = \
|
||||||
|
form.instance.soge_credit \
|
||||||
|
or SogeCredit.objects.filter(user=form.instance.user, credit_transaction__valid=False).exists()
|
||||||
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
@ -1181,7 +1198,10 @@ class WEI1AListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['club'] = self.club
|
context['club'] = self.club
|
||||||
context['bus_repartition_table'] = BusRepartitionTable(Bus.objects.filter(wei=self.club, size__gt=0).all())
|
context['bus_repartition_table'] = BusRepartitionTable(
|
||||||
|
Bus.objects.filter(wei=self.club, size__gt=0)
|
||||||
|
.filter(PermissionBackend.filter_queryset(self.request, Bus, "view"))
|
||||||
|
.all())
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -1218,4 +1238,4 @@ class WEIAttributeBus1ANextView(LoginRequiredMixin, RedirectView):
|
|||||||
qs = qs.filter(information_json__contains='selected_bus_pk') # not perfect, but works...
|
qs = qs.filter(information_json__contains='selected_bus_pk') # not perfect, but works...
|
||||||
if qs.exists():
|
if qs.exists():
|
||||||
return reverse_lazy('wei:wei_bus_1A', args=(qs.first().pk, ))
|
return reverse_lazy('wei:wei_bus_1A', args=(qs.first().pk, ))
|
||||||
return reverse_lazy('wei_1A_list', args=(wei.pk, ))
|
return reverse_lazy('wei:wei_1A_list', args=(wei.pk, ))
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-09-12 19:30+0200\n"
|
"POT-Creation-Date: 2021-09-13 23:26+0200\n"
|
||||||
"PO-Revision-Date: 2020-11-16 20:02+0000\n"
|
"PO-Revision-Date: 2020-11-16 20:02+0000\n"
|
||||||
"Last-Translator: Yohann D'ANELLO <ynerant@crans.org>\n"
|
"Last-Translator: Yohann D'ANELLO <ynerant@crans.org>\n"
|
||||||
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
||||||
@ -56,7 +56,7 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
|
|||||||
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:301
|
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:301
|
||||||
#: apps/permission/models.py:330
|
#: apps/permission/models.py:330
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:16
|
#: apps/registration/templates/registration/future_profile_detail.html:16
|
||||||
#: apps/wei/models.py:66 apps/wei/models.py:123 apps/wei/tables.py:283
|
#: apps/wei/models.py:67 apps/wei/models.py:131 apps/wei/tables.py:282
|
||||||
#: apps/wei/templates/wei/base.html:26
|
#: apps/wei/templates/wei/base.html:26
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:14
|
#: apps/wei/templates/wei/weimembership_form.html:14
|
||||||
msgid "name"
|
msgid "name"
|
||||||
@ -91,7 +91,7 @@ msgstr "types d'activité"
|
|||||||
#: apps/activity/models.py:68
|
#: apps/activity/models.py:68
|
||||||
#: apps/activity/templates/activity/includes/activity_info.html:19
|
#: apps/activity/templates/activity/includes/activity_info.html:19
|
||||||
#: apps/note/models/transactions.py:81 apps/permission/models.py:110
|
#: apps/note/models/transactions.py:81 apps/permission/models.py:110
|
||||||
#: apps/permission/models.py:189 apps/wei/models.py:77 apps/wei/models.py:134
|
#: apps/permission/models.py:189 apps/wei/models.py:78 apps/wei/models.py:142
|
||||||
msgid "description"
|
msgid "description"
|
||||||
msgstr "description"
|
msgstr "description"
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ msgstr "type"
|
|||||||
|
|
||||||
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:305
|
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:305
|
||||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
||||||
#: apps/wei/models.py:165 apps/wei/templates/wei/attribute_bus_1A.html:13
|
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
||||||
#: apps/wei/templates/wei/survey.html:15
|
#: apps/wei/templates/wei/survey.html:15
|
||||||
msgid "user"
|
msgid "user"
|
||||||
msgstr "utilisateur"
|
msgstr "utilisateur"
|
||||||
@ -511,7 +511,7 @@ msgstr "rôles"
|
|||||||
msgid "fee"
|
msgid "fee"
|
||||||
msgstr "cotisation"
|
msgstr "cotisation"
|
||||||
|
|
||||||
#: apps/member/apps.py:14 apps/wei/tables.py:227 apps/wei/tables.py:258
|
#: apps/member/apps.py:14 apps/wei/tables.py:226 apps/wei/tables.py:257
|
||||||
msgid "member"
|
msgid "member"
|
||||||
msgstr "adhérent"
|
msgstr "adhérent"
|
||||||
|
|
||||||
@ -1913,10 +1913,10 @@ msgstr "Cet email est déjà pris."
|
|||||||
|
|
||||||
#: apps/registration/forms.py:49
|
#: apps/registration/forms.py:49
|
||||||
msgid ""
|
msgid ""
|
||||||
"I declare that I opened a bank account in the Société générale with the BDE "
|
"I declare that I opened or I will open soon a bank account in the Société générale with the BDE "
|
||||||
"partnership."
|
"partnership."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Je déclare avoir ouvert un compte à la société générale avec le partenariat "
|
"Je déclare avoir ouvert ou ouvrir prochainement un compte à la société générale avec le partenariat "
|
||||||
"du BDE."
|
"du BDE."
|
||||||
|
|
||||||
#: apps/registration/forms.py:50
|
#: apps/registration/forms.py:50
|
||||||
@ -2508,8 +2508,8 @@ msgstr "Liste des crédits de la Société générale"
|
|||||||
msgid "Manage credits from the Société générale"
|
msgid "Manage credits from the Société générale"
|
||||||
msgstr "Gérer les crédits de la Société générale"
|
msgstr "Gérer les crédits de la Société générale"
|
||||||
|
|
||||||
#: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50
|
#: apps/wei/apps.py:10 apps/wei/models.py:50 apps/wei/models.py:51
|
||||||
#: apps/wei/models.py:61 apps/wei/models.py:172
|
#: apps/wei/models.py:62 apps/wei/models.py:180
|
||||||
#: note_kfet/templates/base.html:103
|
#: note_kfet/templates/base.html:103
|
||||||
msgid "WEI"
|
msgid "WEI"
|
||||||
msgstr "WEI"
|
msgstr "WEI"
|
||||||
@ -2520,8 +2520,8 @@ msgstr ""
|
|||||||
"L'utilisateur sélectionné n'est pas validé. Merci de d'abord valider son "
|
"L'utilisateur sélectionné n'est pas validé. Merci de d'abord valider son "
|
||||||
"compte."
|
"compte."
|
||||||
|
|
||||||
#: apps/wei/forms/registration.py:59 apps/wei/models.py:118
|
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
|
||||||
#: apps/wei/models.py:315
|
#: apps/wei/models.py:323
|
||||||
msgid "bus"
|
msgid "bus"
|
||||||
msgstr "bus"
|
msgstr "bus"
|
||||||
|
|
||||||
@ -2547,7 +2547,7 @@ msgstr ""
|
|||||||
"bus ou électron libre)"
|
"bus ou électron libre)"
|
||||||
|
|
||||||
#: apps/wei/forms/registration.py:75 apps/wei/forms/registration.py:85
|
#: apps/wei/forms/registration.py:75 apps/wei/forms/registration.py:85
|
||||||
#: apps/wei/models.py:153
|
#: apps/wei/models.py:161
|
||||||
msgid "WEI Roles"
|
msgid "WEI Roles"
|
||||||
msgstr "Rôles au WEI"
|
msgstr "Rôles au WEI"
|
||||||
|
|
||||||
@ -2563,123 +2563,123 @@ msgstr "Cette équipe n'appartient pas à ce bus."
|
|||||||
msgid "Choose a word:"
|
msgid "Choose a word:"
|
||||||
msgstr "Choisissez un mot :"
|
msgstr "Choisissez un mot :"
|
||||||
|
|
||||||
#: apps/wei/models.py:24 apps/wei/templates/wei/base.html:36
|
#: apps/wei/models.py:25 apps/wei/templates/wei/base.html:36
|
||||||
msgid "year"
|
msgid "year"
|
||||||
msgstr "année"
|
msgstr "année"
|
||||||
|
|
||||||
#: apps/wei/models.py:28 apps/wei/templates/wei/base.html:30
|
#: apps/wei/models.py:29 apps/wei/templates/wei/base.html:30
|
||||||
msgid "date start"
|
msgid "date start"
|
||||||
msgstr "début"
|
msgstr "début"
|
||||||
|
|
||||||
#: apps/wei/models.py:32 apps/wei/templates/wei/base.html:33
|
#: apps/wei/models.py:33 apps/wei/templates/wei/base.html:33
|
||||||
msgid "date end"
|
msgid "date end"
|
||||||
msgstr "fin"
|
msgstr "fin"
|
||||||
|
|
||||||
#: apps/wei/models.py:70 apps/wei/tables.py:306
|
#: apps/wei/models.py:71 apps/wei/tables.py:305
|
||||||
msgid "seat count in the bus"
|
msgid "seat count in the bus"
|
||||||
msgstr "nombre de sièges dans le bus"
|
msgstr "nombre de sièges dans le bus"
|
||||||
|
|
||||||
#: apps/wei/models.py:82
|
#: apps/wei/models.py:83
|
||||||
msgid "survey information"
|
msgid "survey information"
|
||||||
msgstr "informations sur le questionnaire"
|
msgstr "informations sur le questionnaire"
|
||||||
|
|
||||||
#: apps/wei/models.py:83
|
#: apps/wei/models.py:84
|
||||||
msgid "Information about the survey for new members, encoded in JSON"
|
msgid "Information about the survey for new members, encoded in JSON"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Informations sur le sondage pour les nouveaux membres, encodées en JSON"
|
"Informations sur le sondage pour les nouveaux membres, encodées en JSON"
|
||||||
|
|
||||||
#: apps/wei/models.py:105
|
#: apps/wei/models.py:113
|
||||||
msgid "Bus"
|
msgid "Bus"
|
||||||
msgstr "Bus"
|
msgstr "Bus"
|
||||||
|
|
||||||
#: apps/wei/models.py:106 apps/wei/templates/wei/weiclub_detail.html:51
|
#: apps/wei/models.py:114 apps/wei/templates/wei/weiclub_detail.html:51
|
||||||
msgid "Buses"
|
msgid "Buses"
|
||||||
msgstr "Bus"
|
msgstr "Bus"
|
||||||
|
|
||||||
#: apps/wei/models.py:127
|
#: apps/wei/models.py:135
|
||||||
msgid "color"
|
msgid "color"
|
||||||
msgstr "couleur"
|
msgstr "couleur"
|
||||||
|
|
||||||
#: apps/wei/models.py:128
|
#: apps/wei/models.py:136
|
||||||
msgid "The color of the T-Shirt, stored with its number equivalent"
|
msgid "The color of the T-Shirt, stored with its number equivalent"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"La couleur du T-Shirt, stocké sous la forme de son équivalent numérique"
|
"La couleur du T-Shirt, stocké sous la forme de son équivalent numérique"
|
||||||
|
|
||||||
#: apps/wei/models.py:142
|
#: apps/wei/models.py:150
|
||||||
msgid "Bus team"
|
msgid "Bus team"
|
||||||
msgstr "Équipe de bus"
|
msgstr "Équipe de bus"
|
||||||
|
|
||||||
#: apps/wei/models.py:143
|
#: apps/wei/models.py:151
|
||||||
msgid "Bus teams"
|
msgid "Bus teams"
|
||||||
msgstr "Équipes de bus"
|
msgstr "Équipes de bus"
|
||||||
|
|
||||||
#: apps/wei/models.py:152
|
#: apps/wei/models.py:160
|
||||||
msgid "WEI Role"
|
msgid "WEI Role"
|
||||||
msgstr "Rôle au WEI"
|
msgstr "Rôle au WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:177
|
#: apps/wei/models.py:185
|
||||||
msgid "Credit from Société générale"
|
msgid "Credit from Société générale"
|
||||||
msgstr "Crédit de la Société générale"
|
msgstr "Crédit de la Société générale"
|
||||||
|
|
||||||
#: apps/wei/models.py:182
|
#: apps/wei/models.py:190
|
||||||
msgid "Caution check given"
|
msgid "Caution check given"
|
||||||
msgstr "Chèque de caution donné"
|
msgstr "Chèque de caution donné"
|
||||||
|
|
||||||
#: apps/wei/models.py:186 apps/wei/templates/wei/weimembership_form.html:64
|
#: apps/wei/models.py:194 apps/wei/templates/wei/weimembership_form.html:64
|
||||||
msgid "birth date"
|
msgid "birth date"
|
||||||
msgstr "date de naissance"
|
msgstr "date de naissance"
|
||||||
|
|
||||||
#: apps/wei/models.py:192 apps/wei/models.py:202
|
#: apps/wei/models.py:200 apps/wei/models.py:210
|
||||||
msgid "Male"
|
msgid "Male"
|
||||||
msgstr "Homme"
|
msgstr "Homme"
|
||||||
|
|
||||||
#: apps/wei/models.py:193 apps/wei/models.py:203
|
#: apps/wei/models.py:201 apps/wei/models.py:211
|
||||||
msgid "Female"
|
msgid "Female"
|
||||||
msgstr "Femme"
|
msgstr "Femme"
|
||||||
|
|
||||||
#: apps/wei/models.py:194
|
#: apps/wei/models.py:202
|
||||||
msgid "Non binary"
|
msgid "Non binary"
|
||||||
msgstr "Non-binaire"
|
msgstr "Non-binaire"
|
||||||
|
|
||||||
#: apps/wei/models.py:196 apps/wei/templates/wei/attribute_bus_1A.html:22
|
#: apps/wei/models.py:204 apps/wei/templates/wei/attribute_bus_1A.html:22
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:55
|
#: apps/wei/templates/wei/weimembership_form.html:55
|
||||||
msgid "gender"
|
msgid "gender"
|
||||||
msgstr "genre"
|
msgstr "genre"
|
||||||
|
|
||||||
#: apps/wei/models.py:205 apps/wei/templates/wei/weimembership_form.html:58
|
#: apps/wei/models.py:213 apps/wei/templates/wei/weimembership_form.html:58
|
||||||
msgid "clothing cut"
|
msgid "clothing cut"
|
||||||
msgstr "coupe de vêtement"
|
msgstr "coupe de vêtement"
|
||||||
|
|
||||||
#: apps/wei/models.py:218 apps/wei/templates/wei/weimembership_form.html:61
|
#: apps/wei/models.py:226 apps/wei/templates/wei/weimembership_form.html:61
|
||||||
msgid "clothing size"
|
msgid "clothing size"
|
||||||
msgstr "taille de vêtement"
|
msgstr "taille de vêtement"
|
||||||
|
|
||||||
#: apps/wei/models.py:224 apps/wei/templates/wei/attribute_bus_1A.html:28
|
#: apps/wei/models.py:232 apps/wei/templates/wei/attribute_bus_1A.html:28
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:67
|
#: apps/wei/templates/wei/weimembership_form.html:67
|
||||||
msgid "health issues"
|
msgid "health issues"
|
||||||
msgstr "problèmes de santé"
|
msgstr "problèmes de santé"
|
||||||
|
|
||||||
#: apps/wei/models.py:229 apps/wei/templates/wei/weimembership_form.html:70
|
#: apps/wei/models.py:237 apps/wei/templates/wei/weimembership_form.html:70
|
||||||
msgid "emergency contact name"
|
msgid "emergency contact name"
|
||||||
msgstr "nom du contact en cas d'urgence"
|
msgstr "nom du contact en cas d'urgence"
|
||||||
|
|
||||||
#: apps/wei/models.py:234 apps/wei/templates/wei/weimembership_form.html:73
|
#: apps/wei/models.py:242 apps/wei/templates/wei/weimembership_form.html:73
|
||||||
msgid "emergency contact phone"
|
msgid "emergency contact phone"
|
||||||
msgstr "téléphone du contact en cas d'urgence"
|
msgstr "téléphone du contact en cas d'urgence"
|
||||||
|
|
||||||
#: apps/wei/models.py:239 apps/wei/templates/wei/weimembership_form.html:52
|
#: apps/wei/models.py:247 apps/wei/templates/wei/weimembership_form.html:52
|
||||||
msgid "first year"
|
msgid "first year"
|
||||||
msgstr "première année"
|
msgstr "première année"
|
||||||
|
|
||||||
#: apps/wei/models.py:240
|
#: apps/wei/models.py:248
|
||||||
msgid "Tells if the user is new in the school."
|
msgid "Tells if the user is new in the school."
|
||||||
msgstr "Indique si l'utilisateur est nouveau dans l'école."
|
msgstr "Indique si l'utilisateur est nouveau dans l'école."
|
||||||
|
|
||||||
#: apps/wei/models.py:245
|
#: apps/wei/models.py:253
|
||||||
msgid "registration information"
|
msgid "registration information"
|
||||||
msgstr "informations sur l'inscription"
|
msgstr "informations sur l'inscription"
|
||||||
|
|
||||||
#: apps/wei/models.py:246
|
#: apps/wei/models.py:254
|
||||||
msgid ""
|
msgid ""
|
||||||
"Information about the registration (buses for old members, survey for the "
|
"Information about the registration (buses for old members, survey for the "
|
||||||
"new members), encoded in JSON"
|
"new members), encoded in JSON"
|
||||||
@ -2687,27 +2687,27 @@ msgstr ""
|
|||||||
"Informations sur l'inscription (bus pour les 2A+, questionnaire pour les "
|
"Informations sur l'inscription (bus pour les 2A+, questionnaire pour les "
|
||||||
"1A), encodées en JSON"
|
"1A), encodées en JSON"
|
||||||
|
|
||||||
#: apps/wei/models.py:304
|
#: apps/wei/models.py:312
|
||||||
msgid "WEI User"
|
msgid "WEI User"
|
||||||
msgstr "Participant au WEI"
|
msgstr "Participant au WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:305
|
#: apps/wei/models.py:313
|
||||||
msgid "WEI Users"
|
msgid "WEI Users"
|
||||||
msgstr "Participants au WEI"
|
msgstr "Participants au WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:325
|
#: apps/wei/models.py:333
|
||||||
msgid "team"
|
msgid "team"
|
||||||
msgstr "équipe"
|
msgstr "équipe"
|
||||||
|
|
||||||
#: apps/wei/models.py:335
|
#: apps/wei/models.py:343
|
||||||
msgid "WEI registration"
|
msgid "WEI registration"
|
||||||
msgstr "Inscription au WEI"
|
msgstr "Inscription au WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:339
|
#: apps/wei/models.py:347
|
||||||
msgid "WEI membership"
|
msgid "WEI membership"
|
||||||
msgstr "Adhésion au WEI"
|
msgstr "Adhésion au WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:340
|
#: apps/wei/models.py:348
|
||||||
msgid "WEI memberships"
|
msgid "WEI memberships"
|
||||||
msgstr "Adhésions au WEI"
|
msgstr "Adhésions au WEI"
|
||||||
|
|
||||||
@ -2735,32 +2735,32 @@ msgstr "Année"
|
|||||||
msgid "preferred bus"
|
msgid "preferred bus"
|
||||||
msgstr "bus préféré"
|
msgstr "bus préféré"
|
||||||
|
|
||||||
#: apps/wei/tables.py:211 apps/wei/templates/wei/bus_detail.html:32
|
#: apps/wei/tables.py:210 apps/wei/templates/wei/bus_detail.html:32
|
||||||
#: apps/wei/templates/wei/busteam_detail.html:50
|
#: apps/wei/templates/wei/busteam_detail.html:50
|
||||||
msgid "Teams"
|
msgid "Teams"
|
||||||
msgstr "Équipes"
|
msgstr "Équipes"
|
||||||
|
|
||||||
#: apps/wei/tables.py:220 apps/wei/tables.py:261
|
#: apps/wei/tables.py:219 apps/wei/tables.py:260
|
||||||
msgid "Members count"
|
msgid "Members count"
|
||||||
msgstr "Nombre de membres"
|
msgstr "Nombre de membres"
|
||||||
|
|
||||||
#: apps/wei/tables.py:227 apps/wei/tables.py:258
|
#: apps/wei/tables.py:226 apps/wei/tables.py:257
|
||||||
msgid "members"
|
msgid "members"
|
||||||
msgstr "adhérents"
|
msgstr "adhérents"
|
||||||
|
|
||||||
#: apps/wei/tables.py:288
|
#: apps/wei/tables.py:287
|
||||||
msgid "suggested first year"
|
msgid "suggested first year"
|
||||||
msgstr "1A suggérés"
|
msgstr "1A suggérés"
|
||||||
|
|
||||||
#: apps/wei/tables.py:294
|
#: apps/wei/tables.py:293
|
||||||
msgid "validated first year"
|
msgid "validated first year"
|
||||||
msgstr "1A validés"
|
msgstr "1A validés"
|
||||||
|
|
||||||
#: apps/wei/tables.py:300
|
#: apps/wei/tables.py:299
|
||||||
msgid "validated staff"
|
msgid "validated staff"
|
||||||
msgstr "2A+ validés"
|
msgstr "2A+ validés"
|
||||||
|
|
||||||
#: apps/wei/tables.py:311
|
#: apps/wei/tables.py:310
|
||||||
msgid "free seats"
|
msgid "free seats"
|
||||||
msgstr "sièges libres"
|
msgstr "sièges libres"
|
||||||
|
|
||||||
@ -3116,7 +3116,7 @@ msgstr "Valider l'inscription WEI"
|
|||||||
msgid "Attribute buses to first year members"
|
msgid "Attribute buses to first year members"
|
||||||
msgstr "Répartir les 1A dans les bus"
|
msgstr "Répartir les 1A dans les bus"
|
||||||
|
|
||||||
#: apps/wei/views.py:1190
|
#: apps/wei/views.py:1191
|
||||||
msgid "Attribute bus"
|
msgid "Attribute bus"
|
||||||
msgstr "Attribuer un bus"
|
msgstr "Attribuer un bus"
|
||||||
|
|
||||||
@ -3252,16 +3252,15 @@ msgstr ""
|
|||||||
msgid ""
|
msgid ""
|
||||||
"You declared that you opened a bank account in the Société générale. The "
|
"You declared that you opened a bank account in the Société générale. The "
|
||||||
"bank did not validate the creation of the account to the BDE, so the "
|
"bank did not validate the creation of the account to the BDE, so the "
|
||||||
"registration bonus of 80 € is not credited and the membership is not paid "
|
"membership and the WEI are not paid yet. This verification procedure may "
|
||||||
"yet. This verification procedure may last a few days. Please make sure that "
|
"last a few days. Please make sure that you go to the end of the account "
|
||||||
"you go to the end of the account creation."
|
"creation."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Vous avez déclaré que vous avez ouvert un compte bancaire à la société "
|
"Vous avez déclaré que vous avez ouvert un compte bancaire à la société "
|
||||||
"générale. La banque n'a pas encore validé la création du compte auprès du "
|
"générale. La banque n'a pas encore validé la création du compte auprès du "
|
||||||
"BDE, le bonus d'inscription de 80 € n'a donc pas encore été créditée et "
|
"BDE, l'adhésion et le WEI ne sont donc pas encore payés. Cette procédure de "
|
||||||
"l'adhésion n'est pas encore payée. Cette procédure de vérification peut "
|
"vérification peut durer quelques jours. Merci de vous assurer de bien aller "
|
||||||
"durer quelques jours. Merci de vous assurer de bien aller au bout de vos "
|
"au bout de vos démarches."
|
||||||
"démarches."
|
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:195
|
#: note_kfet/templates/base.html:195
|
||||||
msgid "Contact us"
|
msgid "Contact us"
|
||||||
|
@ -96,7 +96,11 @@ function displayStyle (note) {
|
|||||||
if (!note) { return '' }
|
if (!note) { return '' }
|
||||||
const balance = note.balance
|
const balance = note.balance
|
||||||
var css = ''
|
var css = ''
|
||||||
if (balance < -5000) { css += ' text-danger bg-dark' } else if (balance < -1000) { css += ' text-danger' } else if (balance < 0) { css += ' text-warning' } else if (!note.email_confirmed) { css += ' text-white bg-primary' } else if (!note.is_active || (note.membership && note.membership.date_end < new Date().toISOString())) { css += 'text-white bg-info' }
|
if (balance < -5000) { css += ' text-danger bg-dark' }
|
||||||
|
else if (balance < -1000) { css += ' text-danger' }
|
||||||
|
else if (balance < 0) { css += ' text-warning' }
|
||||||
|
if (!note.email_confirmed) { css += ' bg-primary' }
|
||||||
|
else if (!note.is_active || (note.membership && note.membership.date_end < new Date().toISOString())) { css += ' bg-info' }
|
||||||
return css
|
return css
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,8 +170,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
{% if user.sogecredit and not user.sogecredit.valid %}
|
{% if user.sogecredit and not user.sogecredit.valid %}
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
{% blocktrans trimmed %}
|
{% blocktrans trimmed %}
|
||||||
You declared that you opened a bank account in the Société générale. The bank did not validate the creation of the account to the BDE,
|
You declared that you opened a bank account in the Société générale. The bank did not validate
|
||||||
so the registration bonus of 80 € is not credited and the membership is not paid yet.
|
the creation of the account to the BDE, so the membership and the WEI are not paid yet.
|
||||||
This verification procedure may last a few days.
|
This verification procedure may last a few days.
|
||||||
Please make sure that you go to the end of the account creation.
|
Please make sure that you go to the end of the account creation.
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
@ -193,6 +193,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<span class="text-muted mr-1">
|
<span class="text-muted mr-1">
|
||||||
<a href="mailto:{{ "CONTACT_EMAIL" | getenv }}"
|
<a href="mailto:{{ "CONTACT_EMAIL" | getenv }}"
|
||||||
class="text-muted">{% trans "Contact us" %}</a> —
|
class="text-muted">{% trans "Contact us" %}</a> —
|
||||||
|
<a href="mailto:{{ "SUPPORT_EMAIL" | getenv }}"
|
||||||
|
class="text-muted">{% trans "Technical Support" %}</a> —
|
||||||
</span>
|
</span>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<select title="language" name="language"
|
<select title="language" name="language"
|
||||||
|
Loading…
Reference in New Issue
Block a user