1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-11-29 05:20:06 +01:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Maxime JUST
3666a85a52 Add date for Rennes 2025-11-27 07:09:30 +01:00
Maxime JUST
07e13ea6ee Add date Strasbourg 2025-11-16 10:42:04 +01:00
Maxime JUST
27a4bdf98e Add responsabilities of accompanying coaches + translate message about pending dates 2025-11-11 19:34:42 +01:00
Maxime JUST
af60d27402 Add messages for missing date tournaments 2025-11-11 18:50:31 +01:00
Maxime JUST
49729485b7 Fix linters + Fix translations 2025-11-11 18:50:01 +01:00
Maxime JUST
c8eefb0991 Add distinction between scientific coach and accompanying coach 2025-11-11 11:21:03 +00:00
Maxime JUST
1bea4d0188 Add migrations not in the repository 2025-11-07 09:54:38 +01:00
Maxime JUST
b0be8f5525 Add 2026 informations 2025-11-06 10:04:27 +01:00
Emmy D'Anello
8af11cd56f Clôture des listes Sympa 2025-10-30 20:00:26 +01:00
Emmy D'Anello
5c372f7582 Clôture des listes Sympa 2025-10-30 19:51:21 +01:00
Emmy D'Anello
bd230ccaf6 Utilisation de Python 3.13 par défaut, flake8-django pas encore supporté 2025-10-30 18:47:33 +01:00
Emmy D'Anello
46779488c1 Dates tournois franciliens 2025-10-30 18:39:48 +01:00
Emmy D'Anello
f49897cd5b Test sur les tirages au sort réparé 2025-10-30 18:34:20 +01:00
23 changed files with 757 additions and 475 deletions

View File

@@ -37,7 +37,7 @@ py314:
linters: linters:
stage: quality-assurance stage: quality-assurance
image: python:3-alpine image: python:3.13-alpine
before_script: before_script:
- pip install tox --no-cache-dir - pip install tox --no-cache-dir
script: tox -e linters script: tox -e linters

View File

@@ -1,4 +1,4 @@
FROM python:3.14-alpine FROM python:3.13-alpine
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
ENV DJANGO_ALLOW_ASYNC_UNSAFE 1 ENV DJANGO_ALLOW_ASYNC_UNSAFE 1

View File

@@ -165,6 +165,20 @@ Ne pas oublier de partager le dossier en écriture à l'adresse
``plateforme-tfjm@plateforme-tfjm.iam.gserviceaccount.com``. ``plateforme-tfjm@plateforme-tfjm.iam.gserviceaccount.com``.
Anciennes listes de diffusion
"""""""""""""""""""""""""""""
Les listes Sympa doivent être fermées pour être correctement recréées. Un script permet
de supprimer toutes les listes commençant par ``equipe``, ``orga`` ou ``jury`` :
.. code:: bash
./manage.py delete_old_sympa_lists
Attention : les listes closes ne sont pas supprimées. Rendez-vous sur la page
`https://lists.tfjm.org/sympa/get_closed_lists`_ pour supprimer les listes ainsi fermées.
À la fin du tournoi À la fin du tournoi
------------------- -------------------

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.2.8 on 2025-11-06 18:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('draw', '0006_alter_round_current_pool'),
]
operations = [
migrations.AlterField(
model_name='teamdraw',
name='accepted',
field=models.PositiveSmallIntegerField(choices=[(1, 'Problem #1'), (2, 'Problem #2'), (3, 'Problem #3'), (4, 'Problem #4'), (5, 'Problem #5'), (6, 'Problem #6'), (7, 'Problem #7'), (8, 'Problem #8')], default=None, null=True, verbose_name='accepted problem'),
),
migrations.AlterField(
model_name='teamdraw',
name='purposed',
field=models.PositiveSmallIntegerField(choices=[(1, 'Problem #1'), (2, 'Problem #2'), (3, 'Problem #3'), (4, 'Problem #4'), (5, 'Problem #5'), (6, 'Problem #6'), (7, 'Problem #7'), (8, 'Problem #8')], default=None, null=True, verbose_name='purposed problem'),
),
]

View File

@@ -1,6 +1,5 @@
# Copyright (C) 2023 by Animath # Copyright (C) 2023 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import asyncio
from random import shuffle from random import shuffle
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
@@ -712,15 +711,12 @@ class TestDraw(TestCase):
{'tid': tid, 'type': 'export_visibility', 'visible': False}) {'tid': tid, 'type': 'export_visibility', 'visible': False})
# Cancel all steps and reset all # Cancel all steps and reset all
for i in range(1000): for i in range(150):
await communicator.send_json_to({'tid': tid, 'type': 'cancel'}) await communicator.send_json_to({'tid': tid, 'type': 'cancel'})
# Purge receive queue # Purge receive queue
while True: while (await communicator.receive_json_from())['type'] != "abort":
try: pass
await communicator.receive_json_from()
except asyncio.TimeoutError:
break
if await Draw.objects.filter(tournament_id=tid).aexists(): if await Draw.objects.filter(tournament_id=tid).aexists():
print((await Draw.objects.filter(tournament_id=tid).aexists())) print((await Draw.objects.filter(tournament_id=tid).aexists()))

File diff suppressed because it is too large Load Diff

View File

@@ -80,13 +80,17 @@ class ParticipationForm(forms.ModelForm):
if settings.SINGLE_TOURNAMENT: if settings.SINGLE_TOURNAMENT:
del self.fields['tournament'] del self.fields['tournament']
self.helper = FormHelper() self.helper = FormHelper()
idf_text = _(
'For the tournaments in the region "Île-de-France": registration is '
'unified for each tournament. By choosing a tournament "Île-de-France", '
"you're accepting that your team may be selected for one of these tournaments. "
'In case of date conflict, please write them in your motivation letter.'
)
idf_warning_banner = f""" idf_warning_banner = f"""
<div class=\"alert alert-warning\"> <div class=\"alert alert-warning\">
<h5 class=\"alert-heading\">{_("IMPORTANT")}</h4> <h5 class=\"alert-heading\">{_("IMPORTANT")}</h4>
{_("""For the tournaments in the region "Île-de-France": registration is {idf_text}
unified for each tournament. By choosing a tournament "Île-de-France",
you're accepting that your team may be selected for one of these tournaments.
In case of date conflict, please write them in your motivation letter.""")}
</div> </div>
""" """
unified_registration_tournament_ids = ",".join( unified_registration_tournament_ids = ",".join(

View File

@@ -0,0 +1,24 @@
# Copyright (C) 2025 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.core.management import BaseCommand
from tfjm.lists import get_sympa_client
class Command(BaseCommand):
def handle(self, *args, **options):
"""
Supprime les listes de diffusion Sympa.
Toutes les listess commençant par "equipe", "orga" ou "jury" sont fermées.
Attention : la fermeture n'est pas définitive, il faut ensuite se rendre sur Sympa
pour supprimer les listes fermées.
"""
if not settings.ML_MANAGEMENT:
return
sympa = get_sympa_client()
for mailing_list in sympa.all_lists():
address = mailing_list.list_address
if address.startswith("equipe") or address.startswith("orga") or address.startswith("jury"):
sympa.delete_list(address)

View File

@@ -0,0 +1,34 @@
# Generated by Django 5.2.8 on 2025-11-06 18:53
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('participation', '0023_tournament_unified_registration'),
]
operations = [
migrations.AlterField(
model_name='passage',
name='solution_number',
field=models.PositiveSmallIntegerField(choices=[(1, 'Problem #1'), (2, 'Problem #2'), (3, 'Problem #3'), (4, 'Problem #4'), (5, 'Problem #5'), (6, 'Problem #6'), (7, 'Problem #7'), (8, 'Problem #8')], verbose_name='reported solution'),
),
migrations.AlterField(
model_name='pool',
name='round',
field=models.PositiveSmallIntegerField(choices=[(1, 'Round 1'), (2, 'Round 2')], verbose_name='round'),
),
migrations.AlterField(
model_name='solution',
name='problem',
field=models.PositiveSmallIntegerField(choices=[(1, 'Problem #1'), (2, 'Problem #2'), (3, 'Problem #3'), (4, 'Problem #4'), (5, 'Problem #5'), (6, 'Problem #6'), (7, 'Problem #7'), (8, 'Problem #8')], verbose_name='problem'),
),
migrations.AlterField(
model_name='team',
name='trigram',
field=models.CharField(help_text='The code must be composed of 3 uppercase letters.', max_length=4, unique=True, validators=[django.core.validators.RegexValidator('^[A-Z]{3}[A-Z]*$'), django.core.validators.RegexValidator('^(?!BIT$|CNO$|CRO$|CUL$|FTG$|FCK$|FUC$|FUK$|FYS$|HIV$|IST$|MST$|KKK$|KYS$|SEX$)', message='This team code is forbidden.')], verbose_name='code'),
),
]

View File

@@ -71,12 +71,20 @@ class Team(models.Model):
def coaches(self): def coaches(self):
return self.participants.filter(coachregistration__isnull=False) return self.participants.filter(coachregistration__isnull=False)
@property
def scientific_coaches(self):
return self.participants.filter(coachregistration__isnull=False, coachregistration__is_scientific_coach=True)
@property
def accompanying_coaches(self):
return self.participants.filter(coachregistration__isnull=False, coachregistration__is_accompanying_coach=True)
def can_validate(self): def can_validate(self):
if any(not r.email_confirmed for r in self.participants.all()): if any(not r.email_confirmed for r in self.participants.all()):
return False return False
if self.students.count() < 4: if self.students.count() < 4:
return False return False
if not self.coaches.exists(): if not self.scientific_coaches.exists():
return False return False
if not self.participation.tournament: if not self.participation.tournament:
return False return False

View File

@@ -22,9 +22,18 @@
<dt class="col-sm-6 text-sm-end">{% trans "Access code:" %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "Access code:" %}</dt>
<dd class="col-sm-6">{{ team.access_code }}</dd> <dd class="col-sm-6">{{ team.access_code }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans "Coaches:" %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "Scientific coaches:" %}</dt>
<dd class="col-sm-6"> <dd class="col-sm-6">
{% for coach in team.coaches.all %} {% for coach in team.scientific_coaches.all %}
<a href="{% url "registration:user_detail" pk=coach.user.pk %}">{{ coach }}</a>{% if not forloop.last %},{% endif %}
{% empty %}
{% trans "any" %}
{% endfor %}
</dd>
<dt class="col-sm-6 text-sm-end">{% trans "Accompanying coaches:" %}</dt>
<dd class="col-sm-6">
{% for coach in team.accompanying_coaches.all %}
<a href="{% url "registration:user_detail" pk=coach.user.pk %}">{{ coach }}</a>{% if not forloop.last %},{% endif %} <a href="{% url "registration:user_detail" pk=coach.user.pk %}">{{ coach }}</a>{% if not forloop.last %},{% endif %}
{% empty %} {% empty %}
{% trans "any" %} {% trans "any" %}

View File

@@ -285,6 +285,7 @@ class TestStudentParticipation(TestCase):
self.coach.registration.vaccine_sheet = "authorization/vaccine/coach" self.coach.registration.vaccine_sheet = "authorization/vaccine/coach"
self.coach.registration.photo_authorization = "authorization/photo/coach" self.coach.registration.photo_authorization = "authorization/photo/coach"
self.coach.registration.email_confirmed = True self.coach.registration.email_confirmed = True
self.coach.registration.is_scientific_coach = True
self.coach.registration.save() self.coach.registration.save()
self.client.force_login(self.superuser) self.client.force_login(self.superuser)

View File

@@ -61,7 +61,7 @@ class RegistrationAdmin(PolymorphicParentModelAdmin):
@admin.register(ParticipantRegistration) @admin.register(ParticipantRegistration)
class ParticipantRegistrationAdmin(PolymorphicChildModelAdmin): class ParticipantRegistrationAdmin(PolymorphicChildModelAdmin):
list_display = ('user', 'first_name', 'last_name', 'type', 'team', 'email_confirmed',) list_display = ('user', 'first_name', 'last_name', 'type', 'team', 'email_confirmed')
list_filter = ('email_confirmed',) list_filter = ('email_confirmed',)
search_fields = ('user__first_name', 'user__last_name', 'user__email',) search_fields = ('user__first_name', 'user__last_name', 'user__email',)
autocomplete_fields = ('user', 'team',) autocomplete_fields = ('user', 'team',)
@@ -93,7 +93,7 @@ class StudentRegistrationAdmin(PolymorphicChildModelAdmin):
@admin.register(CoachRegistration) @admin.register(CoachRegistration)
class CoachRegistrationAdmin(PolymorphicChildModelAdmin): class CoachRegistrationAdmin(PolymorphicChildModelAdmin):
list_display = ('user', 'first_name', 'last_name', 'team', 'email_confirmed',) list_display = ('user', 'first_name', 'last_name', 'team', 'email_confirmed', 'is_accompanying_coach', 'is_scientific_coach')
list_filter = ('email_confirmed',) list_filter = ('email_confirmed',)
search_fields = ('user__first_name', 'user__last_name', 'user__email',) search_fields = ('user__first_name', 'user__last_name', 'user__email',)
autocomplete_fields = ('user', 'team',) autocomplete_fields = ('user', 'team',)

View File

@@ -251,6 +251,20 @@ class CoachRegistrationForm(forms.ModelForm):
""" """
A coach can tell its professional activity. A coach can tell its professional activity.
""" """
ACCOMPANYING_CONFIRM_CHOICES = [
("presence", _("I undertake to be present throughout the entire tournament weekend alongside the team (including overnight stays).")),
("rules", _("I undertake to respond to the team's (non-mathematical) problems and not to hesitate to discuss them with the tournament "
"organisers, who will be able to help.")),
("cancelling", _("In case of absence, I undertake to notify the organisers as soon as possible, providing a replacement if possible.")),
]
confirm_accompanying = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=ACCOMPANYING_CONFIRM_CHOICES,
label=_("Responsabilities of accompanying coaches")
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if not settings.SUGGEST_ANIMATH: if not settings.SUGGEST_ANIMATH:
@@ -258,9 +272,21 @@ class CoachRegistrationForm(forms.ModelForm):
class Meta: class Meta:
model = CoachRegistration model = CoachRegistration
fields = ('team', 'gender', 'address', 'zip_code', 'city', 'country', 'phone_number', fields = ('team', 'is_scientific_coach', 'is_accompanying_coach', 'confirm_accompanying', 'gender', 'address',
'last_degree', 'professional_activity', 'health_issues', 'housing_constraints', 'zip_code', 'city', 'country', 'phone_number', 'last_degree', 'professional_activity', 'health_issues',
'give_contact_to_animath', 'email_confirmed',) 'housing_constraints', 'give_contact_to_animath', 'email_confirmed')
def clean(self):
cleaned = super().clean()
if cleaned.get("is_accompanying_coach"):
selected = set(cleaned.get("confirm_accompanying") or [])
required = {key for key, _ in self.ACCOMPANYING_CONFIRM_CHOICES}
if selected != required:
self.add_error(
"confirm_accompanying",
_("Please tick all the required confirmations."),
)
return cleaned
class VolunteerRegistrationForm(forms.ModelForm): class VolunteerRegistrationForm(forms.ModelForm):

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.2.8 on 2025-11-06 18:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('registration', '0015_alter_participantregistration_gender'),
]
operations = [
migrations.AddField(
model_name='coachregistration',
name='is_accompanying_coach',
field=models.BooleanField(default=False, help_text='Accompanies the team during the weekend and stays for the entire tournament.', verbose_name='Accompanying coach'),
),
migrations.AddField(
model_name='coachregistration',
name='is_scientific_coach',
field=models.BooleanField(default=False, help_text='Provides scientific guidance: methodology, content review, and project mentoring during the preparation phase. <a href="https://tfjm.org/wp-content/uploads/2024/01/note____l_intention_des_encadrants.pdf" target="_blank" rel="noopener">see practical sheet</a>.', verbose_name='Scientific coach'),
),
]

View File

@@ -14,6 +14,8 @@ from django.urls import reverse, reverse_lazy
from django.utils import timezone, translation from django.utils import timezone, translation
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.utils.functional import lazy
from django.utils.html import format_html
from django.utils.http import urlsafe_base64_encode from django.utils.http import urlsafe_base64_encode
from django.utils.text import format_lazy from django.utils.text import format_lazy
from django.utils.timezone import localtime from django.utils.timezone import localtime
@@ -23,6 +25,8 @@ from polymorphic.models import PolymorphicModel
from tfjm import helloasso from tfjm import helloasso
from tfjm.tokens import email_validation_token from tfjm.tokens import email_validation_token
format_html_lazy = lazy(format_html, str)
class Registration(PolymorphicModel): class Registration(PolymorphicModel):
""" """
@@ -527,6 +531,28 @@ class CoachRegistration(ParticipantRegistration):
verbose_name=_("professional activity"), verbose_name=_("professional activity"),
) )
is_scientific_coach = models.BooleanField(
default=False,
verbose_name=_("Scientific coach"),
help_text=format_html_lazy(
'{} <a href="{}" target="_blank" rel="noopener">{}</a>.',
_("Provides scientific guidance: methodology, content review, and project mentoring during the preparation phase."),
"https://tfjm.org/wp-content/uploads/2024/01/note____l_intention_des_encadrants.pdf",
_("see practical sheet"),
),
)
is_accompanying_coach = models.BooleanField(
default=False,
verbose_name=_("Accompanying coach"),
help_text=format_html_lazy(
'{} <a href="{}" target="_blank" rel="noopener">{}</a>.',
_("Accompanies the team during the weekend and stays for the entire tournament."),
"https://tfjm.org/wp-content/uploads/2025/11/Fiches_pratiques_TFJM2.pdf",
_("see practical sheet"),
)
)
@property @property
def type(self): def type(self):
return _("coach") return _("coach")

View File

@@ -51,15 +51,33 @@
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
let role_elem = document.getElementById("id_role") let role_elem = document.getElementById("id_role")
function setup_requirements() {
const main = document.getElementById('id_is_accompanying_coach');
const group = document.getElementById('div_id_confirm_accompanying');
function toggle(){
if(main.checked) {
group.style.display = "block";
} else {
group.style.display = "none";
}
}
main.addEventListener('change', toggle);
toggle();
}
function updateView () { function updateView () {
let selected_role = role_elem.options[role_elem.selectedIndex].value let selected_role = role_elem.options[role_elem.selectedIndex].value
if (selected_role === "participant") if (selected_role === "participant")
document.getElementById("registration_form").innerHTML = document.getElementById("student_registration_form").innerHTML document.getElementById("registration_form").innerHTML = document.getElementById("student_registration_form").innerHTML
else else
document.getElementById("registration_form").innerHTML = document.getElementById("coach_registration_form").innerHTML document.getElementById("registration_form").innerHTML = document.getElementById("coach_registration_form").innerHTML
setup_requirements();
} }
role_elem.addEventListener('change', updateView) role_elem.addEventListener('change', updateView)
updateView() updateView()
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -66,7 +66,7 @@ Cochez la/les cases correspondantes.\\
\fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$ \fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$
{% if tournament.unified_registration %} dans {% if tournament.unified_registration %} dans
l'un des tournois d'Île-de-France (selon sélection : du 26 au 27 avril 2025, du 3 au 4 mai 2025, ou du 10 au 11 mai 2025) l'un des tournois d'Île-de-France (selon sélection : du 4 au 5 mai 2026, du 28 au 29 mars 2026, ou TBA 2026)
{% else %} de {% else %} de
{{ tournament.name }} du {{ tournament.date_start }} au {{ tournament.date_end }} à : {{ tournament.place }}, {{ tournament.name }} du {{ tournament.date_start }} au {{ tournament.date_end }} à : {{ tournament.place }},
{% endif %} \`a {% endif %} \`a

View File

@@ -68,7 +68,7 @@ Cochez la/les cases correspondantes.\\
\fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$ \fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$
{% if tournament.unified_registration %} dans {% if tournament.unified_registration %} dans
l'un des tournois d'Île-de-France (selon sélection : du 26 au 27 avril 2025, du 3 au 4 mai 2025, ou du 10 au 11 mai 2025) l'un des tournois d'Île-de-France (selon sélection : du 4 au 5 mai 2026, du 28 au 29 mars 2026, ou TBA 2026)
{% else %} de {% else %} de
{{ tournament.name }} du {{ tournament.date_start }} au {{ tournament.date_end }} à : {{ tournament.place }}, {{ tournament.name }} du {{ tournament.date_start }} au {{ tournament.date_end }} à : {{ tournament.place }},
{% endif %} \`a {% endif %} \`a

View File

@@ -54,9 +54,9 @@ né\cdt{}e le {{ registration.birth_date|default:"\underline{\phantom{dd/mm/aaaa
à participer au Tournoi Français des Jeunes Mathématiciennes et Mathématiciens ($\mathbb{TFJM}^2$) à participer au Tournoi Français des Jeunes Mathématiciennes et Mathématiciens ($\mathbb{TFJM}^2$)
{% if tournament.unified_registration %} dans l'un des tournois d'Île-de-France selon sélection : {% if tournament.unified_registration %} dans l'un des tournois d'Île-de-France selon sélection :
\begin{itemize} \begin{itemize}
\item Île-de-France 1, du 26 au 27 avril 2025 ; \item Île-de-France 1, du 4 au 5 avril 2026 ;
\item Île-de-France 2, du 3 au 4 mai 2025 ; \item Île-de-France 2, du 28 au 29 mars 2026 ;
\item Île-de-France 3, du 10 au 11 mai 2025. \item Île-de-France 3, du TBA 2026.
\end{itemize} \end{itemize}
{% else %} {% else %}
organisé \`a : organisé \`a :
@@ -67,7 +67,7 @@ Iel se rendra au lieu indiqu\'e ci-dessus le samedi matin et quittera les lieux
ses propres moyens et sous la responsabilité du/de la représentant\cdt{}e légal\cdt{}e. ses propres moyens et sous la responsabilité du/de la représentant\cdt{}e légal\cdt{}e.
{% if tournament.name == "Lyon" %} {% if tournament.name == "Lyon" %}
Un hébergement à titre gratuit sera organisée la nuit du 10 au 11 mai 2025. Un hébergement à titre gratuit sera organisée la nuit du {{ tournament.date_start }} au {{ tournament.date_end }}.
Le/la participant\cdt{}e sera logé\cdt{}e soit dans les résidences de l'ENS de Lyon situées Le/la participant\cdt{}e sera logé\cdt{}e soit dans les résidences de l'ENS de Lyon situées
sur les campus de l'école soit dans l'hotel Ibis Gerland Mérieux situé 246 rue Marcel Mérieux 69007 LYON. sur les campus de l'école soit dans l'hotel Ibis Gerland Mérieux situé 246 rue Marcel Mérieux 69007 LYON.
{% endif %} {% endif %}

View File

@@ -151,6 +151,12 @@
<dd class="col-sm-6"><a href="mailto:{{ email }}">{{ email }}</a></dd> <dd class="col-sm-6"><a href="mailto:{{ email }}">{{ email }}</a></dd>
{% endwith %} {% endwith %}
{% elif user_object.registration.coachregistration %} {% elif user_object.registration.coachregistration %}
<dt class="col-sm-6 text-sm-end">{% trans "Scientific coach:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.is_scientific_coach|yesno }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans "Accompanying coach:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.is_accompanying_coach|yesno }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans "Most recent degree:" %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "Most recent degree:" %}</dt>
<dd class="col-sm-6">{{ user_object.registration.last_degree }}</dd> <dd class="col-sm-6">{{ user_object.registration.last_degree }}</dd>

View File

@@ -385,19 +385,19 @@ if TFJM_APP == "TFJM":
RULES_LINK = "https://tfjm.org/reglement" RULES_LINK = "https://tfjm.org/reglement"
REGISTRATION_DATES = dict( REGISTRATION_DATES = dict(
open=datetime.fromisoformat("2025-01-15T12:00:00+0100"), open=datetime.fromisoformat("2025-11-12T00:00:00+0100"),
close=datetime.fromisoformat("2025-03-02T22:00:00+0100"), close=datetime.fromisoformat("2026-01-08T22:00:00+0100"),
) )
PROBLEMS = [ PROBLEMS = [
"Une bonne humeur contagieuse", "Guerre à l'apéro",
"Drôles de toboggans", "Jeu du moulin",
"Plats à tarte gradués", "Poison dans les boissons",
"Transformation de papillons", "Colliers de perles",
"Gerrymandering", "Parcours d'escalade",
"Le cauchemar de la ligne 20-25", "Malaise dans la salle d'attente",
"Taxes routières", "Double et chiffres",
"Points colorés sur un cercle", "Tri trop rapide",
] ]
elif TFJM_APP == "ETEAM": elif TFJM_APP == "ETEAM":
PREFERRED_LANGUAGE_CODE = 'en' PREFERRED_LANGUAGE_CODE = 'en'

View File

@@ -1,4 +1,12 @@
<div id="messages"> <div id="messages">
<div class="alert alert-info fade show" role="alert">
{% load i18n %}
<h2>{% trans "Dates pending" %}</h2>
<p>{% blocktrans %}Since the dates for the tournaments in Bordeaux, Metz and Occitanie have not yet been set, we kindly invite the teams concerned to wait a little longer. If you wish, you may register for another tournament and send us an email to let us know of your interest; we will keep you informed as soon as the final dates are confirmed.
{% endblocktrans %}
</p>
</div>
{% for message in messages %} {% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert"> <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>