1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-09-25 19:08:18 +02:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Ehouarn
0934b8fa34 Patch 2025-08-30 16:15:55 +02:00
Ehouarn
7633c9ab4b Better phone input (no invalid number) 2025-08-29 18:36:18 +02:00
ehouarn
92f6d11cb5 Merge branch 'translations' into 'main'
Some WEI translations

See merge request bde/nk20!342
2025-08-21 00:03:43 +02:00
Ehouarn
1fdb30d7d2 Some WEI translations 2025-08-20 23:37:34 +02:00
ehouarn
6975ed6df6 Merge branch 'wei' into 'main'
Survey questions

See merge request bde/nk20!341
2025-08-20 23:30:34 +02:00
Ehouarn
4da87872bd Survey questions 2025-08-20 22:59:37 +02:00
ehouarn
68e5f280b4 Merge branch 'wei' into 'main'
Signals used to ignore _no_signal

See merge request bde/nk20!340
2025-08-03 21:36:41 +02:00
Ehouarn
251bb933da Signals used to ignore _no_signal 2025-08-03 21:19:44 +02:00
ehouarn
4fbbfd2365 Merge branch 'translations' into 'main'
French translations for WEI

See merge request bde/nk20!339
2025-08-03 13:04:14 +02:00
Ehouarn
0ac719b1f6 French translations for WEI 2025-08-03 12:47:22 +02:00
13 changed files with 497 additions and 296 deletions

View File

@@ -10,6 +10,7 @@ from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import User
from django.db import transaction
from django.forms import CheckboxSelectMultiple
from phonenumber_field.formfields import PhoneNumberField
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from note.models import NoteSpecial, Alias
@@ -45,6 +46,11 @@ class ProfileForm(forms.ModelForm):
A form for the extras field provided by the :model:`member.Profile` model.
"""
# Remove widget=forms.HiddenInput() if you want to use report frequency.
phone_number = PhoneNumberField(
widget=forms.TextInput(attrs={"type": "tel", "class": "form-control"}),
required=False
)
report_frequency = forms.IntegerField(required=False, initial=0, label=_("Report frequency"))
last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date"))
@@ -72,7 +78,12 @@ class ProfileForm(forms.ModelForm):
if not self.instance.section or (("department" in self.changed_data
or "promotion" in self.changed_data) and "section" not in self.changed_data):
self.instance.section = self.instance.section_generated
return super().save(commit)
instance = super().save(commit=False)
if instance.phone_number:
instance.phone_number = instance.phone_number.as_e164
if commit:
instance.save()
return instance
class Meta:
model = Profile

View File

@@ -16,7 +16,7 @@ def save_user_profile(instance, created, raw, **_kwargs):
def update_wei_registration_fee_on_membership_creation(sender, instance, created, **kwargs):
if created:
if not hasattr(instance, "_no_signal") and created:
from wei.models import WEIRegistration
if instance.club.id == 1 or instance.club.id == 2:
registrations = WEIRegistration.objects.filter(
@@ -24,14 +24,16 @@ def update_wei_registration_fee_on_membership_creation(sender, instance, created
wei__year=instance.date_start.year,
)
for r in registrations:
r._force_save = True
r.save()
def update_wei_registration_fee_on_club_change(sender, instance, **kwargs):
from wei.models import WEIRegistration
if instance.id == 1 or instance.id == 2:
if not hasattr(instance, "_no_signal") and (instance.id == 1 or instance.id == 2):
registrations = WEIRegistration.objects.filter(
wei__year=instance.membership_start.year,
)
for r in registrations:
r._force_save = True
r.save()

View File

@@ -10,7 +10,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{{ title }}
</h3>
<div class="card-body">
<form method="post">
<form method="post" id="profile-form">
{% csrf_token %}
{{ form | crispy }}
{{ profile_form | crispy }}
@@ -21,3 +21,45 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div>
</div>
{% endblock %}
{% block extrajavascript %}
<!-- intl-tel-input CSS/JS -->
<script>
(() => {
const input = document.querySelector("input[name='phone_number']");
const form = document.querySelector("#profile-form");
if (!input || !form) {
console.error("Input phone_number ou form introuvable.");
}
const iti = window.intlTelInput(input, {
initialCountry: "auto",
nationalMode: false,
autoPlaceholder: "off",
geoIpLookup: callback => {
fetch("https://ipapi.co/json")
.then(res => res.json())
.then(data => callback(data.country_code))
.catch(() => callback("fr"));
},
loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/js/utils.js"),
});
form.addEventListener("submit", function(e){
if (!input.value.trim()) {
return;
}
const number = iti.getNumber(intlTelInput.utils.numberFormat.E164);
if (number) {
input.value = number;
form.submit();
} else {
e.preventDefault();
input.focus();
}
});
})();
</script>
{% endblock %}

View File

@@ -10,145 +10,183 @@ from django import forms
from django.db import transaction
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from django.utils.safestring import mark_safe
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInformation
from ...models import WEIMembership, Bus
WORDS = {
'list': [
'13 organisé', '3ième mi temps', 'Années 2000', 'Apéro', 'BBQ', 'BP', 'Beauf', 'Binge drinking', 'Bon enfant',
'Cartouche', 'Catacombes', 'Chansons paillardes', 'Chansons populaires', 'Chanteur', 'Chartreuse', 'Chill',
'Core', 'DJ', 'Dancefloor', 'Danse', 'David Guetta', 'Disco', 'Eau de vie', 'Électro', 'Escalade', 'Familial',
'Fanfare', 'Fracassage', 'Féria', 'Hard rock', 'Hoeggarden', 'House', 'Huit-six', 'IPA', 'Inclusif', 'Inferno',
'Introverti', 'Jager bomb', 'Jazz', 'Jeux d\'alcool', 'Jeux de rôles', 'Jeux vidéo', 'Jul', 'Jus de fruit',
'Karaoké', 'LGBTQI+', 'Lady Gaga', 'Loup garou', 'Morning beer', 'Métal', 'Nuit blanche', 'Ovalie', 'Psychedelic',
'Pétanque', 'Rave', 'Reggae', 'Rhum', 'Ricard', 'Rock', 'Rosé', 'Rétro', 'Séducteur', 'Techno', 'Thérapie taxi',
'Théâtre', 'Trap', 'Turn up', 'Underground', 'Volley', 'Wati B', 'Zinédine Zidane',
'Fiesta', 'Graillance', 'Move it move it', 'Calme', 'Nert et geek', 'Jeux de rôles et danse rock',
'Strass et paillettes', 'Spectaculaire', 'Splendide', 'Flow inégalable', 'Rap', 'Battles légendaires',
'Techno', 'Alcool', 'Kiffeur·euse', 'Rugby', 'Médiéval', 'Festif',
'Stylé', 'Chipie', 'Rétro', 'Vache', 'Farfadet', 'Fanfare',
],
'questions': {
'Question 1': [
'Description 1',
"alcool": [
"""Sur une échelle allant de 0 (= 0 alcool ou très peu) à 5 (= la fontaine de jouvence alcoolique),
quel niveau de consommation dalcool souhaiterais-tu ?""",
{
3: 'Réponse 1 Madagas[car]',
4: 'Réponse 1 Y2[KAR]',
2: 'Réponse 1 Tcherno[bus]',
5: 'Réponse 1 [Kar]tier',
1: 'Réponse 1 [Car]cassonne',
6: 'Réponse 1 O[car]ina',
7: 'Réponse 1 Show[bus]',
8: 'Réponse 1 [Car]ioca'
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
],
'Question 2': [
'Description 2',
"voie_post_bac": [
"""Si la DA du bus de ton choix correspondait à une voie post-bac, laquelle serait-elle ?""",
{
3: 'Réponse 2 Madagas[car]',
4: 'Réponse 2 Y2[KAR]',
2: 'Réponse 2 Tcherno[bus]',
5: 'Réponse 2 [Kar]tier',
1: 'Réponse 2 [Car]cassonne',
6: 'Réponse 2 O[car]ina',
7: 'Réponse 2 Show[bus]',
8: 'Réponse 2 [Car]ioca'
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
],
'Question 3': [
'Description 3',
"boite": [
"""Tu es seul·e sur une île déserte et devant toi il y a une sombre boîte de taille raisonnable.
Quy a-t-il à lintérieur ?""",
{
3: 'Réponse 3 Madagas[car]',
4: 'Réponse 3 Y2[KAR]',
2: 'Réponse 3 Tcherno[bus]',
5: 'Réponse 3 [Kar]tier',
1: 'Réponse 3 [Car]cassonne',
6: 'Réponse 3 O[car]ina',
7: 'Réponse 3 Show[bus]',
8: 'Réponse 3 [Car]ioca'
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
],
'Question 4': [
'Description 4',
"tardif": [
"""Il est 00h, tu as passé la journée à la plage avec tes copains et iels te proposent de prolonger parce
quaprès tout, il ny a plus personne sur la plage à cette heure-ci. Tu nhabites pas loin mais tenchaînes
demain avec une journée similaire avec un autre groupe damis parce que tes trop #busy. Que fais-tu ?""",
{
3: 'Réponse 4 Madagas[car]',
4: 'Réponse 4 Y2[KAR]',
2: 'Réponse 4 Tcherno[bus]',
5: 'Réponse 4 [Kar]tier',
1: 'Réponse 4 [Car]cassonne',
6: 'Réponse 4 O[car]ina',
7: 'Réponse 4 Show[bus]',
8: 'Réponse 4 [Car]ioca'
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
],
'Question 5': [
'Description 5',
"cohesion": [
"""Cest la rentrée de Seconde et tu découvres ta classe, tes camarades et ta prof principale!!!
qui vous propose une activité de cohésion. Laquelle est-elle ?""",
{
3: 'Réponse 5 Madagas[car]',
4: 'Réponse 5 Y2[KAR]',
2: 'Réponse 5 Tcherno[bus]',
5: 'Réponse 5 [Kar]tier',
1: 'Réponse 5 [Car]cassonne',
6: 'Réponse 5 O[car]ina',
7: 'Réponse 5 Show[bus]',
8: 'Réponse 5 [Car]ioca'
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
],
'Question 6': [
'Description 6',
"artiste": [
"""Cest lété et la saison des festivals a commencé. Tu regardes la programmation du festival
pas loin de chez toi et tu découvres avec joie la présence dun·e artiste. De qui sagit-il ?""",
{
3: 'Réponse 6 Madagas[car]',
4: 'Réponse 6 Y2[KAR]',
2: 'Réponse 6 Tcherno[bus]',
5: 'Réponse 6 [Kar]tier',
1: 'Réponse 6 [Car]cassonne',
6: 'Réponse 6 O[car]ina',
7: 'Réponse 6 Show[bus]',
8: 'Réponse 6 [Car]ioca'
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
],
'Question 7': [
'Description 7',
"annonce_noel": [
"""Cest Noël et tu revois toute ta famille, oncles, tantes, cousin·e·s, grands-parents, la totale.
Dun coup, tu te lèves, tapotes de manière pompeuse sur ton verre avec un de tes couverts.
Quannonces-tu ?""",
{
3: 'Réponse 7 Madagas[car]',
4: 'Réponse 7 Y2[KAR]',
2: 'Réponse 7 Tcherno[bus]',
5: 'Réponse 7 [Kar]tier',
1: 'Réponse 7 [Car]cassonne',
6: 'Réponse 7 O[car]ina',
7: 'Réponse 7 Show[bus]',
8: 'Réponse 7 [Car]ioca'
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
],
'Question 8': [
'Description 8',
"vacances": [
"""Les vacances sont là et taimerais bien partir quelque part, mais où ?""",
{
3: 'Réponse 8 Madagas[car]',
4: 'Réponse 8 Y2[KAR]',
2: 'Réponse 8 Tcherno[bus]',
5: 'Réponse 8 [Kar]tier',
1: 'Réponse 8 [Car]cassonne',
6: 'Réponse 8 O[car]ina',
7: 'Réponse 8 Show[bus]',
8: 'Réponse 8 [Car]ioca'
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
],
'Question 9': [
'Description 9',
"loisir": [
"""Tas fini ta journée de cours et tu tapprêtes à profiter dune activité/hobby/loisir de ton choix.
Laquelle est-ce ?""",
{
3: 'Réponse 9 Madagas[car]',
4: 'Réponse 9 Y2[KAR]',
2: 'Réponse 9 Tcherno[bus]',
5: 'Réponse 9 [Kar]tier',
1: 'Réponse 9 [Car]cassonne',
6: 'Réponse 9 O[car]ina',
7: 'Réponse 9 Show[bus]',
8: 'Réponse 9 [Car]ioca'
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
],
"plan": [
"""Tu reçois un message sur la conversation de groupe que tu partages avec tes potes :
vous êtes chaud·e·s pour vous retrouver. Quel plan tattire le plus ?""",
{
42: "",
47: "",
48: "",
45: "",
44: "",
46: "",
43: "",
49: ""
}
]
}
}
IMAGES = {
}
NB_WORDS = 5
class OptionalImageRadioSelect(forms.RadioSelect):
def __init__(self, images=None, *args, **kwargs):
self.images = images or {}
super().__init__(*args, **kwargs)
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
option = super().create_option(name, value, label, selected, index, subindex=subindex, attrs=attrs)
img_url = self.images.get(value)
if img_url:
option['label'] = mark_safe(f'{label} <img src="{img_url}" style="height:32px;vertical-align:middle;">')
else:
option['label'] = label
return option
class WEISurveyForm2025(forms.Form):
"""
Survey form for the year 2025.
@@ -170,7 +208,7 @@ class WEISurveyForm2025(forms.Form):
if information.step == 0:
self.fields["words"] = forms.MultipleChoiceField(
label=_(f"Choose {NB_WORDS} words:"),
label=_(f"Select {NB_WORDS} words that describe the WEI experience you want to have."),
choices=[(w, w) for w in WORDS['list']],
widget=forms.CheckboxSelectMultiple(),
required=True,
@@ -178,23 +216,7 @@ class WEISurveyForm2025(forms.Form):
if self.is_valid():
return
buses = WEISurveyAlgorithm2025.get_buses()
informations = {bus: WEIBusInformation2025(bus) for bus in buses}
scores = sum((list(informations[bus].scores.values()) for bus in buses), [])
if scores:
average_score = sum(scores) / len(scores)
else:
average_score = 0
preferred_words = {
bus: [word for word in WORDS['list'] if informations[bus].scores[word] >= average_score]
for bus in buses
}
all_preferred_words = set()
for bus_words in preferred_words.values():
all_preferred_words.update(bus_words)
all_preferred_words = list(all_preferred_words)
all_preferred_words = WORDS['list']
rng.shuffle(all_preferred_words)
self.fields["words"].choices = [(w, w) for w in all_preferred_words]
else:
@@ -202,12 +224,15 @@ class WEISurveyForm2025(forms.Form):
idx = information.step - 1
if idx < len(questions):
q, (desc, answers) = questions[idx]
choices = [(k, v) for k, v in answers.items()]
rng.shuffle(choices)
if q == 'alcool':
choices = [(i / 2, str(i / 2)) for i in range(11)]
else:
choices = [(k, v) for k, v in answers.items()]
rng.shuffle(choices)
self.fields[q] = forms.ChoiceField(
label=desc,
choices=choices,
widget=forms.RadioSelect,
widget=OptionalImageRadioSelect(images=IMAGES.get(q, {})),
required=True,
)
@@ -226,8 +251,6 @@ class WEIBusInformation2025(WEIBusInformation):
def __init__(self, bus):
self.scores = {}
for word in WORDS['list']:
self.scores[word] = 0
super().__init__(bus)
@@ -235,7 +258,9 @@ class BusInformationForm2025(forms.ModelForm):
class Meta:
model = Bus
fields = ['information_json']
widgets = {}
widgets = {
'information_json': forms.HiddenInput(),
}
def __init__(self, *args, words=None, **kwargs):
super().__init__(*args, **kwargs)
@@ -257,7 +282,7 @@ class BusInformationForm2025(forms.ModelForm):
label=word,
choices=choices,
coerce=int,
initial=initial_scores.get(word, 0),
initial=initial_scores.get(word, 0) if word in initial_scores else None,
required=True,
widget=forms.RadioSelect,
help_text=_("Rate between 0 and 5."),
@@ -285,7 +310,7 @@ class WEISurveyInformation2025(WEISurveyInformation):
step = 0
def __init__(self, registration):
for i in range(1, 5):
for i in range(1, NB_WORDS + 1):
setattr(self, "word" + str(i), None)
for q in WORDS['questions']:
setattr(self, q, None)
@@ -297,7 +322,7 @@ class WEISurveyInformation2025(WEISurveyInformation):
"""
self.step = 0
self.seed = 0
for i in range(1, 5):
for i in range(1, NB_WORDS + 1):
setattr(self, f"word{i}", None)
for q in WORDS['questions']:
setattr(self, q, None)
@@ -371,8 +396,9 @@ class WEISurvey2025(WEISurvey):
"""
if not self.is_complete():
raise ValueError("Survey is not ended, can't calculate score")
# Score is the given score by the bus subtracted to the mid-score of the buses.
s = sum(1 for q in WORDS['questions'] if getattr(self.information, q) == bus.pk)
s = sum(1 for q in WORDS['questions'] if q != 'alcool' and getattr(self.information, q) == bus.pk)
if 'alcool' in WORDS['questions'] and bus.pk in WORDS['questions']['alcool'][1] and hasattr(self.information, 'alcool'):
s -= abs(float(self.information.alcool) - float(WORDS['questions']['alcool'][1][bus.pk]))
return s
@lru_cache()
@@ -396,7 +422,7 @@ class WEISurvey2025(WEISurvey):
@lru_cache()
def ordered_buses(self):
"""
Force the choice of bus to be in the 3 preferred buses according to the words
Order the buses by the score_questions of the survey.
"""
values = list(self.scores_per_bus().items())
values.sort(key=lambda item: -item[1][0])
@@ -513,7 +539,7 @@ class WEISurveyAlgorithm2025(WEISurveyAlgorithm):
for survey2 in surveys:
if not survey2.information.valid or survey2.information.get_selected_bus() != bus:
continue
score2 = survey2.score_questions(bus)
score2 = survey2.score_words(bus)
if current_scores[1] <= score2: # Ignore better students
continue
if least_preferred_survey is None or score2 < least_score:

View File

@@ -22,8 +22,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endif %}
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus' pk=object.pk %}"
data-turbolinks="false">{% trans "Edit" %}</a>
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus_info' pk=object.pk %}"
data-turbolinks="false">{% trans "Edit information" %}</a>
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus_info' pk=object.pk %}"
data-turbolinks="false">{% trans "Edit information for survey" %}</a>
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:add_team' pk=object.pk %}"
data-turbolinks="false">{% trans "Add team" %}</a>
</div>

View File

@@ -31,15 +31,22 @@ SPDX-License-Identifier: GPL-3.0-or-later
<a class="btn btn-success" href="{% url "wei:wei_register_1A_myself" wei_pk=club.pk %}" data-turbolinks="false">
{% trans "Register to the WEI! 1A" %}
</a>
{% endif %}
{% else %}
<a class="btn btn-success" href="{% url "wei:wei_register_2A_myself" wei_pk=club.pk %}" data-turbolinks="false">
{% trans "Register to the WEI! 2A+" %}</a>
{% trans "Register to the WEI! 2A+" %}
</a>
{% endif %}
{% else %}
<a class="btn btn-warning" href="{% url "wei:wei_update_registration" pk=my_registration.pk %}"
data-turbolinks="false">
{% trans "Update my registration" %}
</a>
{% if not not_first_year %}
{% if not survey_complete %}
<a class="btn btn-warning" href="{% url "wei:wei_survey" pk=my_registration.pk %}" data-turbolinks="false">
{% trans "Continue survey" %}
</a>
{% endif %}
<a class="btn btn-warning" href="{% url "wei:wei_survey" pk=my_registration.pk %}?reset=true" data-turbolinks="false">
{% trans "Restart survey" %}
</a>

View File

@@ -11,7 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{{ title }}
</h3>
<div class="card-body">
<form method="post">
<form id="registration-form" method="post">
{% csrf_token %}
{{ form|crispy }}
{{ membership_form|crispy }}
@@ -22,6 +22,46 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endblock %}
{% block extrajavascript %}
<!-- intl-tel-input CSS/JS -->
<script>
(() => {
const input = document.querySelector("input[name='emergency_contact_phone']");
const form = document.querySelector("#registration-form");
if (!input || !form) {
console.error("Input phone_number ou form introuvable.");
}
const iti = window.intlTelInput(input, {
initialCountry: "auto",
nationalMode: false,
autoPlaceholder: "off",
geoIpLookup: callback => {
fetch("https://ipapi.co/json")
.then(res => res.json())
.then(data => callback(data.country_code))
.catch(() => callback("fr"));
},
loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/js/utils.js"),
});
form.addEventListener("submit", function(e){
if (!input.value.trim()) {
return;
}
const number = iti.getNumber(intlTelInput.utils.numberFormat.E164);
if (number) {
input.value = number;
form.submit();
} else {
e.preventDefault();
input.focus();
}
});
})();
</script>
{% if not object.membership %}
<script>
$(document).ready(function () {

View File

@@ -105,8 +105,6 @@ class TestWEIAlgorithm(TestCase):
survey = WEISurvey2025(r)
chosen_bus = survey.information.get_selected_bus()
buses = survey.ordered_buses()
'''print(buses)
print(chosen_bus)'''
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)))

View File

@@ -166,6 +166,7 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, D
my_registration = WEIRegistration.objects.filter(wei=club, user=self.request.user)
if my_registration.exists():
my_registration = my_registration.get()
context["survey_complete"] = CurrentSurvey(my_registration).is_complete()
else:
my_registration = None
context["my_registration"] = my_registration
@@ -1231,7 +1232,6 @@ class WEIUpdateMembershipView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateVi
return form
def get_success_url(self):
print("get_success_url")
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.registration.wei.pk})

View File

@@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-07-15 18:17+0200\n"
"POT-Creation-Date: 2025-08-20 23:34+0200\n"
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
"Last-Translator: bleizi <bleizi@crans.org>\n"
"Last-Translator: ehouarn <ehouarn@crans.org>\n"
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
@@ -19,10 +19,8 @@ msgstr ""
"X-Generator: Poedit 3.0\n"
#: apps/activity/api/serializers.py:77
#, fuzzy
#| msgid "This friendship already exists"
msgid "This opener already exists"
msgstr "Cette amitié existe déjà"
msgstr "Cette personne est déjà ouvreur⋅se"
#: apps/activity/apps.py:10 apps/activity/models.py:129
#: apps/activity/models.py:169 apps/activity/models.py:329
@@ -66,7 +64,7 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:299
#: apps/permission/models.py:329
#: apps/registration/templates/registration/future_profile_detail.html:16
#: apps/wei/models.py:77 apps/wei/models.py:150 apps/wei/tables.py:282
#: apps/wei/models.py:77 apps/wei/models.py:150 apps/wei/tables.py:342
#: apps/wei/templates/wei/base.html:26
#: apps/wei/templates/wei/weimembership_form.html:14 apps/wrapped/models.py:16
msgid "name"
@@ -291,14 +289,14 @@ msgstr "Type"
#: apps/activity/tables.py:86 apps/member/forms.py:199
#: apps/registration/forms.py:91 apps/treasury/forms.py:131
#: apps/wei/forms/registration.py:129
#: apps/wei/forms/registration.py:117
msgid "Last name"
msgstr "Nom de famille"
#: apps/activity/tables.py:88 apps/member/forms.py:204
#: apps/note/templates/note/transaction_form.html:138
#: apps/registration/forms.py:96 apps/treasury/forms.py:133
#: apps/wei/forms/registration.py:134
#: apps/wei/forms/registration.py:122
msgid "First name"
msgstr "Prénom"
@@ -315,7 +313,7 @@ msgstr "Solde du compte"
#: apps/note/tables.py:281 apps/treasury/tables.py:39
#: apps/treasury/templates/treasury/invoice_confirm_delete.html:30
#: apps/treasury/templates/treasury/sogecredit_detail.html:65
#: apps/wei/tables.py:75 apps/wei/tables.py:118
#: apps/wei/tables.py:74 apps/wei/tables.py:75 apps/wei/tables.py:148
#: apps/wei/templates/wei/weiregistration_confirm_delete.html:31
#: note_kfet/templates/oauth2_provider/application_confirm_delete.html:18
#: note_kfet/templates/oauth2_provider/application_detail.html:39
@@ -406,6 +404,7 @@ msgstr "Entrée effectuée !"
#: apps/wei/templates/wei/bus_form.html:17
#: apps/wei/templates/wei/busteam_form.html:18
#: apps/wei/templates/wei/weiclub_form.html:17
#: apps/wei/templates/wei/weimembership_update.html:17
#: apps/wei/templates/wei/weiregistration_form.html:18
msgid "Submit"
msgstr "Envoyer"
@@ -462,7 +461,6 @@ msgstr "modifier"
#: apps/activity/templates/activity/includes/activity_info.html:74
#: apps/logs/models.py:65 apps/note/tables.py:230 apps/note/tables.py:279
#: apps/permission/models.py:126 apps/treasury/tables.py:38
#: apps/wei/tables.py:74
msgid "delete"
msgstr "supprimer"
@@ -537,7 +535,7 @@ msgstr "Pâtes METRO 5kg"
#: apps/food/forms.py:53 apps/food/forms.py:81
msgid "Specific order given to GCKs"
msgstr ""
msgstr "Instruction donnée aux GCKs"
#: apps/food/forms.py:77
msgid "Lasagna"
@@ -598,7 +596,7 @@ msgid "order"
msgstr "consigne"
#: apps/food/models.py:107 apps/food/views.py:35
#: note_kfet/templates/base.html:72
#: note_kfet/templates/base.html:73
msgid "Food"
msgstr "Bouffe"
@@ -687,45 +685,45 @@ msgstr "Retour à la liste de nourriture"
msgid "View food"
msgstr "Voir l'aliment"
#: apps/food/templates/food/food_list.html:37
#: apps/food/templates/food/food_list.html:38
#: note_kfet/templates/base_search.html:15
msgid "Search by attribute such as name..."
msgstr "Chercher par un attribut tel que le nom..."
#: apps/food/templates/food/food_list.html:49
#: apps/food/templates/food/food_list.html:50
#: note_kfet/templates/base_search.html:23
msgid "There is no results."
msgstr "Il n'y a pas de résultat."
#: apps/food/templates/food/food_list.html:58
#: apps/food/templates/food/food_list.html:59
msgid "Meal served"
msgstr "Plat servis"
#: apps/food/templates/food/food_list.html:63
#: apps/food/templates/food/food_list.html:64
msgid "New meal"
msgstr "Nouveau plat"
#: apps/food/templates/food/food_list.html:72
#: apps/food/templates/food/food_list.html:73
msgid "There is no meal served."
msgstr "Il n'y a pas de plat servi."
#: apps/food/templates/food/food_list.html:79
#: apps/food/templates/food/food_list.html:80
msgid "Free food"
msgstr "Open"
#: apps/food/templates/food/food_list.html:86
#: apps/food/templates/food/food_list.html:87
msgid "There is no free food."
msgstr "Il n'y a pas de bouffe en open"
#: apps/food/templates/food/food_list.html:94
#: apps/food/templates/food/food_list.html:95
msgid "Food of your clubs"
msgstr "Bouffe de tes clubs"
#: apps/food/templates/food/food_list.html:100
#: apps/food/templates/food/food_list.html:101
msgid "Food of club"
msgstr "Bouffe du club"
#: apps/food/templates/food/food_list.html:107
#: apps/food/templates/food/food_list.html:108
msgid "Yours club has not food yet."
msgstr "Ton club n'a pas de bouffe pour l'instant"
@@ -807,41 +805,41 @@ msgstr "Ajouter un nouveau QR-code"
msgid "Add an aliment"
msgstr "Ajouter un nouvel aliment"
#: apps/food/views.py:228
#: apps/food/views.py:237
msgid "Add a meal"
msgstr "Ajouter un plat"
#: apps/food/views.py:259
#: apps/food/views.py:277
msgid "Manage ingredients of:"
msgstr "Gestion des ingrédienrs de :"
#: apps/food/views.py:273 apps/food/views.py:281
#: apps/food/views.py:291 apps/food/views.py:299
#, python-brace-format
msgid "Fully used in {meal}"
msgstr "Aliment entièrement utilisé dans : {meal}"
#: apps/food/views.py:320
#: apps/food/views.py:346
msgid "Add the ingredient:"
msgstr "Ajouter l'ingrédient"
#: apps/food/views.py:346
#: apps/food/views.py:372
#, python-brace-format
msgid "Food fully used in : {meal.name}"
msgstr "Aliment entièrement utilisé dans : {meal.name}"
#: apps/food/views.py:365
#: apps/food/views.py:391
msgid "Update an aliment"
msgstr "Modifier un aliment"
#: apps/food/views.py:413
#: apps/food/views.py:439
msgid "Details of:"
msgstr "Détails de :"
#: apps/food/views.py:423 apps/treasury/tables.py:149
#: apps/food/views.py:449 apps/treasury/tables.py:149
msgid "Yes"
msgstr "Oui"
#: apps/food/views.py:425 apps/member/models.py:99 apps/treasury/tables.py:149
#: apps/food/views.py:451 apps/member/models.py:99 apps/treasury/tables.py:149
msgid "No"
msgstr "Non"
@@ -912,11 +910,11 @@ msgstr "cotisation pour adhérer (normalien·ne étudiant·e)"
msgid "roles"
msgstr "rôles"
#: apps/member/admin.py:66 apps/member/models.py:351
#: apps/member/admin.py:66 apps/member/models.py:351 apps/wei/models.py:290
msgid "fee"
msgstr "cotisation"
#: apps/member/apps.py:14 apps/wei/tables.py:226 apps/wei/tables.py:257
#: apps/member/apps.py:14 apps/wei/tables.py:286 apps/wei/tables.py:317
msgid "member"
msgstr "adhérent·e"
@@ -977,12 +975,12 @@ msgid "Check this case if the Société Générale paid the inscription."
msgstr "Cochez cette case si la Société Générale a payé l'inscription."
#: apps/member/forms.py:185 apps/registration/forms.py:78
#: apps/wei/forms/registration.py:116
#: apps/wei/forms/registration.py:104
msgid "Credit type"
msgstr "Type de rechargement"
#: apps/member/forms.py:186 apps/registration/forms.py:79
#: apps/wei/forms/registration.py:117
#: apps/wei/forms/registration.py:105
msgid "No credit"
msgstr "Pas de rechargement"
@@ -991,13 +989,13 @@ msgid "You can credit the note of the user."
msgstr "Vous pouvez créditer la note de l'utilisateur⋅rice avant l'adhésion."
#: apps/member/forms.py:192 apps/registration/forms.py:84
#: apps/wei/forms/registration.py:122
#: apps/wei/forms/registration.py:110
msgid "Credit amount"
msgstr "Montant à créditer"
#: apps/member/forms.py:209 apps/note/templates/note/transaction_form.html:144
#: apps/registration/forms.py:101 apps/treasury/forms.py:135
#: apps/wei/forms/registration.py:139
#: apps/wei/forms/registration.py:127
msgid "Bank"
msgstr "Banque"
@@ -1422,7 +1420,7 @@ msgstr "Membres du club"
#: apps/member/templates/member/club_detail.html:40
#: apps/member/templates/member/profile_detail.html:32
#: apps/wei/templates/wei/weiclub_detail.html:75
#: apps/wei/templates/wei/weiclub_detail.html:105
msgid "Transaction history"
msgstr "Historique des transactions"
@@ -1976,8 +1974,8 @@ msgstr ""
"mode de paiement et un⋅e utilisateur⋅rice ou un club"
#: apps/note/models/transactions.py:357 apps/note/models/transactions.py:360
#: apps/note/models/transactions.py:363 apps/wei/views.py:1103
#: apps/wei/views.py:1107
#: apps/note/models/transactions.py:363 apps/wei/views.py:1135
#: apps/wei/views.py:1139
msgid "This field is required."
msgstr "Ce champ est requis."
@@ -2079,8 +2077,6 @@ msgstr "Historique des transactions récentes"
#: apps/note/templates/note/mails/weekly_report.txt:32
#: apps/registration/templates/registration/mails/email_validation_email.html:40
#: apps/registration/templates/registration/mails/email_validation_email.txt:16
#: apps/scripts/templates/scripts/food_report.html:48
#: apps/scripts/templates/scripts/food_report.txt:14
msgid "Mail generated by the Note Kfet on the"
msgstr "Mail généré par la Note Kfet le"
@@ -2484,7 +2480,7 @@ msgstr ""
#: apps/registration/templates/registration/future_profile_detail.html:73
#: apps/wei/templates/wei/weimembership_form.html:127
#: apps/wei/templates/wei/weimembership_form.html:192
#: apps/wei/templates/wei/weimembership_form.html:194
msgid "Validate registration"
msgstr "Valider l'inscription"
@@ -2761,7 +2757,7 @@ msgstr "Crédits de la Société générale"
msgid "Soge credit for {user}"
msgstr "Crédit de la société générale pour l'utilisateur·rice {user}"
#: apps/treasury/models.py:446
#: apps/treasury/models.py:444
msgid ""
"This user doesn't have enough money to pay the memberships with its note. "
"Please ask her/him to credit the note before invalidating this credit."
@@ -2943,7 +2939,7 @@ msgstr ""
"supprimer la demande de crédit."
#: apps/treasury/templates/treasury/sogecredit_detail.html:63
#: apps/wei/tables.py:60 apps/wei/tables.py:102
#: apps/wei/tables.py:60 apps/wei/tables.py:131
msgid "Validate"
msgstr "Valider"
@@ -3012,22 +3008,21 @@ msgstr "Gérer les crédits de la Société générale"
#: apps/wei/apps.py:10 apps/wei/models.py:47 apps/wei/models.py:48
#: apps/wei/models.py:72 apps/wei/models.py:197
#: note_kfet/templates/base.html:108
#: note_kfet/templates/base.html:109
msgid "WEI"
msgstr "WEI"
#: apps/wei/forms/registration.py:37
#: apps/wei/forms/registration.py:38
msgid "The selected user is not validated. Please validate its account first"
msgstr ""
"L'utilisateur·rice sélectionné·e n'est pas validé·e. Merci de d'abord "
"valider son compte"
#: apps/wei/forms/registration.py:84 apps/wei/models.py:145
#: apps/wei/models.py:354
msgid "bus"
msgstr "bus"
#: apps/wei/forms/registration.py:72 apps/wei/models.py:107
msgid "Bus"
msgstr "Bus"
#: apps/wei/forms/registration.py:85
#: apps/wei/forms/registration.py:73
msgid ""
"This choice is not definitive. The WEI organizers are free to attribute for "
"you a bus and a team, in particular if you are a free eletron."
@@ -3036,11 +3031,11 @@ msgstr ""
"vous attribuer un bus et une équipe, en particulier si vous êtes un·e "
"électron libre."
#: apps/wei/forms/registration.py:92
#: apps/wei/forms/registration.py:80
msgid "Team"
msgstr "Équipe"
#: apps/wei/forms/registration.py:94
#: apps/wei/forms/registration.py:82
msgid ""
"Leave this field empty if you won't be in a team (staff, bus chief, free "
"electron)"
@@ -3048,25 +3043,35 @@ msgstr ""
"Laissez ce champ vide si vous ne serez pas dans une équipe (staff, chef de "
"bus ou électron libre)"
#: apps/wei/forms/registration.py:100 apps/wei/forms/registration.py:110
#: apps/wei/forms/registration.py:88 apps/wei/forms/registration.py:98
#: apps/wei/models.py:179
msgid "WEI Roles"
msgstr "Rôles au WEI"
#: apps/wei/forms/registration.py:101
#: apps/wei/forms/registration.py:89
msgid "Select the roles that you are interested in."
msgstr "Sélectionnez les rôles qui vous intéressent."
#: apps/wei/forms/registration.py:160
#: apps/wei/forms/registration.py:148
msgid "This team doesn't belong to the given bus."
msgstr "Cette équipe n'appartient pas à ce bus."
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
#: apps/wei/forms/surveys/wei2025.py:36
msgid "Choose a word:"
msgstr "Choisissez un mot :"
#: apps/wei/forms/surveys/wei2025.py:123
#: apps/wei/forms/surveys/wei2025.py:211
#, python-brace-format
msgid ""
"Select {NB_WORDS} words that describe the WEI experience you want to have."
msgstr ""
#: apps/wei/forms/surveys/wei2025.py:242
#, python-brace-format
msgid "Please choose exactly {NB_WORDS} words"
msgstr ""
#: apps/wei/forms/surveys/wei2025.py:288
msgid "Rate between 0 and 5."
msgstr "Note entre 0 et 5."
@@ -3084,7 +3089,7 @@ msgstr "début"
msgid "date end"
msgstr "fin"
#: apps/wei/models.py:37
#: apps/wei/models.py:37 apps/wei/templates/wei/base.html:53
msgid "deposit amount"
msgstr "montant de la caution"
@@ -3092,7 +3097,7 @@ msgstr "montant de la caution"
msgid "membership fee (soge credit)"
msgstr "Cotisation pour adhérer (crédit sogé)"
#: apps/wei/models.py:81 apps/wei/tables.py:305
#: apps/wei/models.py:81 apps/wei/tables.py:365
msgid "seat count in the bus"
msgstr "nombre de sièges dans le bus"
@@ -3105,14 +3110,14 @@ msgid "Information about the survey for new members, encoded in JSON"
msgstr ""
"Informations sur le sondage pour les nouveaux membres, encodées en JSON"
#: apps/wei/models.py:107
msgid "Bus"
msgstr "Bus"
#: apps/wei/models.py:108 apps/wei/templates/wei/weiclub_detail.html:51
#: apps/wei/models.py:108 apps/wei/templates/wei/weiclub_detail.html:63
msgid "Buses"
msgstr "Bus"
#: apps/wei/models.py:145 apps/wei/models.py:375
msgid "bus"
msgstr "bus"
#: apps/wei/models.py:154
msgid "color"
msgstr "couleur"
@@ -3138,10 +3143,9 @@ msgstr "Rôle au WEI"
msgid "Credit from Société générale"
msgstr "Crédit de la Société générale"
#: apps/wei/models.py:207 apps/wei/templates/wei/weimembership_form.html:98
#: apps/wei/views.py:997
msgid "Deposit check given"
msgstr "Chèque de caution donné"
#: apps/wei/models.py:207
msgid "Deposit given"
msgstr "Caution donnée"
#: apps/wei/models.py:213
msgid "Check"
@@ -3226,35 +3230,35 @@ msgstr ""
"Informations sur l'inscription (bus pour les 2A+, questionnaire pour les "
"1A), encodées en JSON"
#: apps/wei/models.py:290
#: apps/wei/models.py:296
msgid "WEI User"
msgstr "Participant·e au WEI"
#: apps/wei/models.py:291
#: apps/wei/models.py:297
msgid "WEI Users"
msgstr "Participant·e·s au WEI"
#: apps/wei/models.py:364
#: apps/wei/models.py:385
msgid "team"
msgstr "équipe"
#: apps/wei/models.py:374
#: apps/wei/models.py:395
msgid "WEI registration"
msgstr "Inscription au WEI"
#: apps/wei/models.py:378
#: apps/wei/models.py:399
msgid "WEI membership"
msgstr "Adhésion au WEI"
#: apps/wei/models.py:379
#: apps/wei/models.py:400
msgid "WEI memberships"
msgstr "Adhésions au WEI"
#: apps/wei/tables.py:105
#: apps/wei/tables.py:135
msgid "The user does not have enough money."
msgstr "L'utilisateur⋅rice n'a pas assez d'argent."
#: apps/wei/tables.py:108
#: apps/wei/tables.py:138
msgid ""
"The user is in first year. You may validate the credit, the algorithm will "
"run later."
@@ -3262,44 +3266,44 @@ msgstr ""
"L'utilisateur·rice est en première année, vous pouvez valider le crédit, "
"l'algorithme tournera plus tard."
#: apps/wei/tables.py:111
#: apps/wei/tables.py:141
msgid "The user has enough money, you can validate the registration."
msgstr "L'utilisateur⋅rice a assez d'argent, l'inscription est possible."
#: apps/wei/tables.py:143
#: apps/wei/tables.py:174
msgid "Year"
msgstr "Année"
#: apps/wei/tables.py:180 apps/wei/templates/wei/weimembership_form.html:102
#: apps/wei/tables.py:240 apps/wei/templates/wei/weimembership_form.html:102
msgid "preferred bus"
msgstr "bus préféré"
#: apps/wei/tables.py:210 apps/wei/templates/wei/bus_detail.html:38
#: apps/wei/tables.py:270 apps/wei/templates/wei/bus_detail.html:38
#: apps/wei/templates/wei/busteam_detail.html:52
msgid "Teams"
msgstr "Équipes"
#: apps/wei/tables.py:219 apps/wei/tables.py:260
#: apps/wei/tables.py:279 apps/wei/tables.py:320
msgid "Members count"
msgstr "Nombre de membres"
#: apps/wei/tables.py:226 apps/wei/tables.py:257
#: apps/wei/tables.py:286 apps/wei/tables.py:317
msgid "members"
msgstr "adhérent·es"
#: apps/wei/tables.py:287
#: apps/wei/tables.py:347
msgid "suggested first year"
msgstr "1A suggéré·es"
#: apps/wei/tables.py:293
#: apps/wei/tables.py:353
msgid "validated first year"
msgstr "1A validé·es"
#: apps/wei/tables.py:299
#: apps/wei/tables.py:359
msgid "validated staff"
msgstr "2A+ validé·es"
#: apps/wei/tables.py:310
#: apps/wei/tables.py:370
msgid "free seats"
msgstr "sièges libres"
@@ -3340,19 +3344,15 @@ msgstr "Prix du WEI (élèves)"
msgid "WEI fee (unpaid students)"
msgstr "Prix du WEI (étudiant⋅es)"
#: apps/wei/templates/wei/base.html:53
msgid "Deposit amount"
msgstr "Caution"
#: apps/wei/templates/wei/base.html:74
msgid "WEI list"
msgstr "Liste des WEI"
#: apps/wei/templates/wei/base.html:79 apps/wei/views.py:550
#: apps/wei/templates/wei/base.html:79 apps/wei/views.py:585
msgid "Register 1A"
msgstr "Inscrire un⋅e 1A"
#: apps/wei/templates/wei/base.html:83 apps/wei/views.py:646
#: apps/wei/templates/wei/base.html:83 apps/wei/views.py:681
msgid "Register 2A+"
msgstr "Inscrire un⋅e 2A+"
@@ -3369,8 +3369,8 @@ msgid "View club"
msgstr "Voir le club"
#: apps/wei/templates/wei/bus_detail.html:26
msgid "Edit information"
msgstr "Modifier les informations"
msgid "Edit information for survey"
msgstr "Modifier les informations du sondage"
#: apps/wei/templates/wei/bus_detail.html:28
#: apps/wei/templates/wei/busteam_detail.html:24
@@ -3389,8 +3389,8 @@ msgstr "Télécharger au format PDF"
#: apps/wei/templates/wei/survey.html:11
#: apps/wei/templates/wei/survey_closed.html:11
#: apps/wei/templates/wei/survey_end.html:11 apps/wei/views.py:1165
#: apps/wei/views.py:1220 apps/wei/views.py:1267
#: apps/wei/templates/wei/survey_end.html:11 apps/wei/views.py:1246
#: apps/wei/views.py:1305 apps/wei/views.py:1352
msgid "Survey WEI"
msgstr "Questionnaire WEI"
@@ -3419,19 +3419,27 @@ msgstr "M'inscrire au WEI ! 1A"
msgid "Register to the WEI! 2A+"
msgstr "M'inscrire au WEI ! 2A+"
#: apps/wei/templates/wei/weiclub_detail.html:40
#: apps/wei/templates/wei/weiclub_detail.html:42
msgid "Update my registration"
msgstr "Modifier mon inscription"
#: apps/wei/templates/wei/weiclub_detail.html:63
#: apps/wei/templates/wei/weiclub_detail.html:47
msgid "Continue survey"
msgstr "Continuer le questionnaire"
#: apps/wei/templates/wei/weiclub_detail.html:51
msgid "Restart survey"
msgstr "Recommencer le questionnaire"
#: apps/wei/templates/wei/weiclub_detail.html:75
msgid "Members of the WEI"
msgstr "Membres du WEI"
#: apps/wei/templates/wei/weiclub_detail.html:89
#: apps/wei/templates/wei/weiclub_detail.html:87
msgid "Unvalidated registrations"
msgstr "Inscriptions non validées"
#: apps/wei/templates/wei/weiclub_detail.html:99
#: apps/wei/templates/wei/weiclub_detail.html:97
msgid "Attribute buses"
msgstr "Répartition dans les bus"
@@ -3467,6 +3475,10 @@ msgstr "Informations brutes du sondage"
msgid "The algorithm didn't run."
msgstr "L'algorithme n'a pas été exécuté."
#: apps/wei/templates/wei/weimembership_form.html:98 apps/wei/views.py:1029
msgid "Deposit check given"
msgstr "Chèque de caution donné"
#: apps/wei/templates/wei/weimembership_form.html:105
msgid "preferred team"
msgstr "équipe préférée"
@@ -3522,33 +3534,31 @@ msgstr "Paiements requis"
msgid "Membership fees: %(amount)s"
msgstr "Frais d'inscription : %(amount)s"
#: apps/wei/templates/wei/weimembership_form.html:153
#: apps/wei/templates/wei/weimembership_form.html:154
#, python-format
msgid "Deposit (by Note transaction): %(amount)s"
msgstr "Caution (par transaction) : %(amount)s"
#: apps/wei/templates/wei/weimembership_form.html:157
#: apps/wei/templates/wei/weimembership_form.html:158
#, python-format
msgid "Deposit (by check): %(amount)s"
msgstr "Caution (par chèque) : %(amount)s"
#: apps/wei/templates/wei/weimembership_form.html:161
#: apps/wei/templates/wei/weimembership_form.html:163
#, python-format
msgid "Total needed: %(total)s"
msgstr "Total nécessaire : %(total)s"
#: apps/wei/templates/wei/weimembership_form.html:165
#: apps/wei/templates/wei/weimembership_form.html:167
#, python-format
msgid "Current balance: %(balance)s"
msgstr "Solde actuel : %(balance)s"
#: apps/wei/templates/wei/weimembership_form.html:172
#, fuzzy
#| msgid "The user didn't give her/his deposit check."
msgid "The user didn't give her/his caution check."
msgstr "L'utilisateur⋅rice n'a pas donné son chèque de caution."
#: apps/wei/templates/wei/weimembership_form.html:174
msgid "The user didn't give her/his caution."
msgstr "L'utilisateur⋅rice n'a pas donné sa caution."
#: apps/wei/templates/wei/weimembership_form.html:180
#: apps/wei/templates/wei/weimembership_form.html:182
msgid ""
"This user is not a member of the Kfet club for the coming year. The "
"membership will be processed automatically, the WEI registration includes "
@@ -3591,63 +3601,63 @@ msgstr "Chercher un WEI"
msgid "WEI Detail"
msgstr "Détails du WEI"
#: apps/wei/views.py:212
#: apps/wei/views.py:230
msgid "View members of the WEI"
msgstr "Voir les membres du WEI"
#: apps/wei/views.py:245
#: apps/wei/views.py:263
msgid "Find WEI Membership"
msgstr "Trouver une adhésion au WEI"
#: apps/wei/views.py:255
#: apps/wei/views.py:273
msgid "View registrations to the WEI"
msgstr "Voir les inscriptions au WEI"
#: apps/wei/views.py:284
#: apps/wei/views.py:319
msgid "Find WEI Registration"
msgstr "Trouver une inscription au WEI"
#: apps/wei/views.py:295
#: apps/wei/views.py:330
msgid "Update the WEI"
msgstr "Modifier le WEI"
#: apps/wei/views.py:316
#: apps/wei/views.py:351
msgid "Create new bus"
msgstr "Ajouter un nouveau bus"
#: apps/wei/views.py:354
#: apps/wei/views.py:389
msgid "Update bus"
msgstr "Modifier le bus"
#: apps/wei/views.py:386
#: apps/wei/views.py:421
msgid "Manage bus"
msgstr "Gérer le bus"
#: apps/wei/views.py:413
#: apps/wei/views.py:448
msgid "Create new team"
msgstr "Créer une nouvelle équipe"
#: apps/wei/views.py:457
#: apps/wei/views.py:492
msgid "Update team"
msgstr "Modifier l'équipe"
#: apps/wei/views.py:492
#: apps/wei/views.py:527
msgid "Manage WEI team"
msgstr "Gérer l'équipe WEI"
#: apps/wei/views.py:514
#: apps/wei/views.py:549
msgid "Register first year student to the WEI"
msgstr "Inscrire un⋅e 1A au WEI"
#: apps/wei/views.py:571 apps/wei/views.py:664
#: apps/wei/views.py:606 apps/wei/views.py:699
msgid "Check if you will open a Société Générale account"
msgstr "Cochez cette case si vous ouvrez un compte à la Société Générale."
#: apps/wei/views.py:582 apps/wei/views.py:694
#: apps/wei/views.py:617 apps/wei/views.py:729
msgid "This user is already registered to this WEI."
msgstr "Cette personne est déjà inscrite au WEI."
#: apps/wei/views.py:587
#: apps/wei/views.py:622
msgid ""
"This user can't be in her/his first year since he/she has already "
"participated to a WEI."
@@ -3655,65 +3665,67 @@ msgstr ""
"Cet⋅te utilisateur⋅rice ne peut pas être en première année puisqu'iel a déjà "
"participé à un WEI."
#: apps/wei/views.py:610
#: apps/wei/views.py:645
msgid "Register old student to the WEI"
msgstr "Inscrire un⋅e 2A+ au WEI"
#: apps/wei/views.py:668 apps/wei/views.py:773
#: apps/wei/views.py:703 apps/wei/views.py:826
msgid "You already opened an account in the Société générale."
msgstr "Vous avez déjà ouvert un compte auprès de la société générale."
#: apps/wei/views.py:681 apps/wei/views.py:790
#: apps/wei/views.py:716 apps/wei/views.py:822
msgid "Choose how you want to pay the deposit"
msgstr "Choisissez comment payer la caution"
#: apps/wei/views.py:733
#: apps/wei/views.py:768
msgid "Update WEI Registration"
msgstr "Modifier l'inscription WEI"
#: apps/wei/views.py:816
#: apps/wei/views.py:812
msgid "Tick if the deposit check has been given"
msgstr "Cochez si le chèque de caution a été donné"
#: apps/wei/views.py:851
msgid "No membership found for this registration"
msgstr "Pas d'adhésion trouvée pour cette inscription"
#: apps/wei/views.py:825
#: apps/wei/views.py:860
msgid "You don't have the permission to update memberships"
msgstr "Vous n'avez pas la permission de modifier une inscription"
#: apps/wei/views.py:831
#: apps/wei/views.py:866
#, python-format
msgid "You don't have the permission to update the field %(field)s"
msgstr "Vous n'avez pas la permission de modifier le champ %(field)s"
#: apps/wei/views.py:876
#: apps/wei/views.py:907
msgid "Delete WEI registration"
msgstr "Supprimer l'inscription WEI"
#: apps/wei/views.py:887
#: apps/wei/views.py:918
msgid "You don't have the right to delete this WEI registration."
msgstr "Vous n'avez pas la permission de supprimer cette inscription au WEI."
#: apps/wei/views.py:905
#: apps/wei/views.py:936
msgid "Validate WEI registration"
msgstr "Valider l'inscription WEI"
#: apps/wei/views.py:998
msgid "Please make sure the check is given before validating the registration"
msgstr ""
"Merci de vous assurer que le chèque a bien été donné avant de valider "
"l'adhésion"
#: apps/wei/views.py:1030
msgid "Only treasurers can validate this field"
msgstr "Seul·e·s les trésorier·ère·s peuvent valider ce champ"
#: apps/wei/views.py:1004
#: apps/wei/views.py:1036
msgid "Create deposit transaction"
msgstr "Créer une transaction de caution"
#: apps/wei/views.py:1005
#: apps/wei/views.py:1037
#, python-format
msgid ""
"A transaction of %(amount).2f€ will be created from the user's Note account"
msgstr ""
"Un transaction de %(amount).2f€ va être créée depuis la note de l'utilisateur"
#: apps/wei/views.py:1093
#: apps/wei/views.py:1125
#, python-format
msgid ""
"This user doesn't have enough money to join this club and pay the deposit. "
@@ -3723,20 +3735,24 @@ msgstr ""
"payer la caution. Solde actuel : %(balance)d€, crédit : %(credit)d€, "
"requis : %(needed)d€"
#: apps/wei/views.py:1146
#: apps/wei/views.py:1178
#, python-format
msgid "Deposit %(name)s"
msgstr "Caution %(name)s"
#: apps/wei/views.py:1360
#: apps/wei/views.py:1203
msgid "Update WEI Membership"
msgstr "Modifier une adhésion au WEI"
#: apps/wei/views.py:1445
msgid "Attribute buses to first year members"
msgstr "Répartir les 1A dans les bus"
#: apps/wei/views.py:1386
#: apps/wei/views.py:1471
msgid "Attribute bus"
msgstr "Attribuer un bus"
#: apps/wei/views.py:1426
#: apps/wei/views.py:1511
msgid ""
"No first year student without a bus found. Either all of them have a bus, or "
"none has filled the survey yet."
@@ -4100,9 +4116,10 @@ msgid ""
"your web browser when you are done accessing services that require "
"authentication!"
msgstr ""
"<h3>Connection réussie</h3>Vous vous êtes bien connecté au Service Central d'Authentification."
"<br/>Pour des raisons de sécurité, veuillez vous déconnecter et fermer votre navigateur internet "
"une fois que vous aurez fini d'accéder aux services qui requiert une authentification !"
"<h3>Connection réussie</h3>Vous vous êtes bien connecté au Service Central "
"d'Authentification.<br/>Pour des raisons de sécurité, veuillez vous "
"déconnecter et fermer votre navigateur internet une fois que vous aurez fini "
"d'accéder aux services qui requiert une authentification !"
#: note_kfet/templates/cas/logged.html:14
msgid "Log me out from all my sessions"
@@ -4348,6 +4365,18 @@ msgstr ""
"d'adhésion. Vous devez également valider votre adresse email en suivant le "
"lien que vous avez reçu."
#~ msgid "Choose {NB_WORDS} words:"
#~ msgstr "Choisissez {NB_WORDS} mots :"
#~ msgid "Deposit amount"
#~ msgstr "Caution"
#~ msgid ""
#~ "Please make sure the check is given before validating the registration"
#~ msgstr ""
#~ "Merci de vous assurer que le chèque a bien été donné avant de valider "
#~ "l'adhésion"
#~ msgid "caution amount"
#~ msgstr "montant de la caution"

View File

@@ -305,8 +305,8 @@ PIC_WIDTH = 200
PIC_RATIO = 1
# Custom phone number format
PHONENUMBER_DB_FORMAT = 'NATIONAL'
PHONENUMBER_DEFAULT_REGION = 'FR'
PHONENUMBER_DB_FORMAT = 'E164'
PHONENUMBER_DEFAULT_REGION = None
# We add custom information to CAS, in order to give a normalized name to other services
CAS_AUTH_CLASS = 'member.auth.CustomAuthUser'

View File

@@ -30,6 +30,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
<link rel="stylesheet" href="{% static "font-awesome/css/font-awesome.min.css" %}">
<link rel="stylesheet" href="{% static "css/custom.css" %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/css/intlTelInput.css">
{# JQuery, Bootstrap and Turbolinks JavaScript #}
<script src="{% static "jquery/jquery.min.js" %}"></script>
<script src="{% static "popper.js/umd/popper.min.js" %}"></script>
@@ -41,6 +43,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
{# Translation in javascript files #}
<script src="{% static "js/jsi18n/"|add:LANGUAGE_CODE|add:".js" %}"></script>
<script src="https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/js/intlTelInput.min.js"></script>
{# If extra ressources are needed for a form, load here #}
{% if form.media %}
{{ form.media }}

View File

@@ -19,7 +19,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endblocktrans %}
</div>
<form method="post">
<form method="post" id="profile_form">
{% csrf_token %}
{{ form|crispy }}
{{ profile_form|crispy }}
@@ -31,3 +31,45 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div>
</div>
{% endblock %}
{% block extrajavascript %}
<!-- intl-tel-input CSS/JS -->
<script>
(() => {
const input = document.querySelector("input[name='phone_number']");
const form = document.querySelector("#profile_form");
if (!input || !form) {
console.error("Input phone_number ou form introuvable.");
}
const iti = window.intlTelInput(input, {
initialCountry: "auto",
nationalMode: false,
autoPlaceholder: "off",
geoIpLookup: callback => {
fetch("https://ipapi.co/json")
.then(res => res.json())
.then(data => callback(data.country_code))
.catch(() => callback("fr"));
},
loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.5.2/build/js/utils.js"),
});
form.addEventListener("submit", function(e){
if (!input.value.trim()) {
return;
}
const number = iti.getNumber(intlTelInput.utils.numberFormat.E164);
if (number) {
input.value = number;
form.submit();
} else {
e.preventDefault();
input.focus();
}
});
})();
</script>
{% endblock %}