mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2025-11-17 18:57:48 +01:00
Compare commits
12 Commits
399e223b33
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07e13ea6ee | ||
|
|
27a4bdf98e | ||
|
|
af60d27402 | ||
|
|
49729485b7 | ||
|
|
c8eefb0991 | ||
|
|
1bea4d0188 | ||
|
|
b0be8f5525 | ||
|
|
8af11cd56f
|
||
|
|
5c372f7582
|
||
|
|
bd230ccaf6
|
||
|
|
46779488c1
|
||
|
|
f49897cd5b
|
@@ -37,7 +37,7 @@ py314:
|
||||
|
||||
linters:
|
||||
stage: quality-assurance
|
||||
image: python:3-alpine
|
||||
image: python:3.13-alpine
|
||||
before_script:
|
||||
- pip install tox --no-cache-dir
|
||||
script: tox -e linters
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM python:3.14-alpine
|
||||
FROM python:3.13-alpine
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV DJANGO_ALLOW_ASYNC_UNSAFE 1
|
||||
|
||||
@@ -165,6 +165,20 @@ Ne pas oublier de partager le dossier en écriture à l'adresse
|
||||
``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
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -1,6 +1,5 @@
|
||||
# Copyright (C) 2023 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
import asyncio
|
||||
from random import shuffle
|
||||
|
||||
from asgiref.sync import sync_to_async
|
||||
@@ -712,15 +711,12 @@ class TestDraw(TestCase):
|
||||
{'tid': tid, 'type': 'export_visibility', 'visible': False})
|
||||
|
||||
# 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'})
|
||||
|
||||
# Purge receive queue
|
||||
while True:
|
||||
try:
|
||||
await communicator.receive_json_from()
|
||||
except asyncio.TimeoutError:
|
||||
break
|
||||
while (await communicator.receive_json_from())['type'] != "abort":
|
||||
pass
|
||||
|
||||
if 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
@@ -80,13 +80,17 @@ class ParticipationForm(forms.ModelForm):
|
||||
if settings.SINGLE_TOURNAMENT:
|
||||
del self.fields['tournament']
|
||||
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"""
|
||||
<div class=\"alert alert-warning\">
|
||||
<h5 class=\"alert-heading\">{_("IMPORTANT")}</h4>
|
||||
{_("""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_text}
|
||||
</div>
|
||||
"""
|
||||
unified_registration_tournament_ids = ",".join(
|
||||
|
||||
24
participation/management/commands/delete_old_sympa_lists.py
Normal file
24
participation/management/commands/delete_old_sympa_lists.py
Normal 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)
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -71,12 +71,20 @@ class Team(models.Model):
|
||||
def coaches(self):
|
||||
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):
|
||||
if any(not r.email_confirmed for r in self.participants.all()):
|
||||
return False
|
||||
if self.students.count() < 4:
|
||||
return False
|
||||
if not self.coaches.exists():
|
||||
if not self.scientific_coaches.exists():
|
||||
return False
|
||||
if not self.participation.tournament:
|
||||
return False
|
||||
|
||||
@@ -22,9 +22,18 @@
|
||||
<dt class="col-sm-6 text-sm-end">{% trans "Access code:" %}</dt>
|
||||
<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">
|
||||
{% 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 %}
|
||||
{% empty %}
|
||||
{% trans "any" %}
|
||||
|
||||
@@ -285,6 +285,7 @@ class TestStudentParticipation(TestCase):
|
||||
self.coach.registration.vaccine_sheet = "authorization/vaccine/coach"
|
||||
self.coach.registration.photo_authorization = "authorization/photo/coach"
|
||||
self.coach.registration.email_confirmed = True
|
||||
self.coach.registration.is_scientific_coach = True
|
||||
self.coach.registration.save()
|
||||
|
||||
self.client.force_login(self.superuser)
|
||||
|
||||
@@ -61,7 +61,7 @@ class RegistrationAdmin(PolymorphicParentModelAdmin):
|
||||
|
||||
@admin.register(ParticipantRegistration)
|
||||
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',)
|
||||
search_fields = ('user__first_name', 'user__last_name', 'user__email',)
|
||||
autocomplete_fields = ('user', 'team',)
|
||||
@@ -93,7 +93,7 @@ class StudentRegistrationAdmin(PolymorphicChildModelAdmin):
|
||||
|
||||
@admin.register(CoachRegistration)
|
||||
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',)
|
||||
search_fields = ('user__first_name', 'user__last_name', 'user__email',)
|
||||
autocomplete_fields = ('user', 'team',)
|
||||
|
||||
@@ -251,6 +251,20 @@ class CoachRegistrationForm(forms.ModelForm):
|
||||
"""
|
||||
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):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not settings.SUGGEST_ANIMATH:
|
||||
@@ -258,9 +272,21 @@ class CoachRegistrationForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = CoachRegistration
|
||||
fields = ('team', 'gender', 'address', 'zip_code', 'city', 'country', 'phone_number',
|
||||
'last_degree', 'professional_activity', 'health_issues', 'housing_constraints',
|
||||
'give_contact_to_animath', 'email_confirmed',)
|
||||
fields = ('team', 'is_scientific_coach', 'is_accompanying_coach', 'confirm_accompanying', 'gender', 'address',
|
||||
'zip_code', 'city', 'country', 'phone_number', 'last_degree', 'professional_activity', 'health_issues',
|
||||
'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):
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
]
|
||||
@@ -14,6 +14,8 @@ from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone, translation
|
||||
from django.utils.crypto import get_random_string
|
||||
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.text import format_lazy
|
||||
from django.utils.timezone import localtime
|
||||
@@ -23,6 +25,8 @@ from polymorphic.models import PolymorphicModel
|
||||
from tfjm import helloasso
|
||||
from tfjm.tokens import email_validation_token
|
||||
|
||||
format_html_lazy = lazy(format_html, str)
|
||||
|
||||
|
||||
class Registration(PolymorphicModel):
|
||||
"""
|
||||
@@ -527,6 +531,28 @@ class CoachRegistration(ParticipantRegistration):
|
||||
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
|
||||
def type(self):
|
||||
return _("coach")
|
||||
|
||||
@@ -51,15 +51,33 @@
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
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 () {
|
||||
let selected_role = role_elem.options[role_elem.selectedIndex].value
|
||||
if (selected_role === "participant")
|
||||
document.getElementById("registration_form").innerHTML = document.getElementById("student_registration_form").innerHTML
|
||||
else
|
||||
document.getElementById("registration_form").innerHTML = document.getElementById("coach_registration_form").innerHTML
|
||||
setup_requirements();
|
||||
}
|
||||
role_elem.addEventListener('change', updateView)
|
||||
updateView()
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -66,7 +66,7 @@ Cochez la/les cases correspondantes.\\
|
||||
|
||||
\fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$
|
||||
{% 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
|
||||
{{ tournament.name }} du {{ tournament.date_start }} au {{ tournament.date_end }} à : {{ tournament.place }},
|
||||
{% endif %} \`a
|
||||
|
||||
@@ -68,7 +68,7 @@ Cochez la/les cases correspondantes.\\
|
||||
|
||||
\fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$
|
||||
{% 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
|
||||
{{ tournament.name }} du {{ tournament.date_start }} au {{ tournament.date_end }} à : {{ tournament.place }},
|
||||
{% endif %} \`a
|
||||
|
||||
@@ -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$)
|
||||
{% if tournament.unified_registration %} dans l'un des tournois d'Île-de-France selon sélection :
|
||||
\begin{itemize}
|
||||
\item Île-de-France 1, du 26 au 27 avril 2025 ;
|
||||
\item Île-de-France 2, du 3 au 4 mai 2025 ;
|
||||
\item Île-de-France 3, du 10 au 11 mai 2025.
|
||||
\item Île-de-France 1, du 4 au 5 avril 2026 ;
|
||||
\item Île-de-France 2, du 28 au 29 mars 2026 ;
|
||||
\item Île-de-France 3, du TBA 2026.
|
||||
\end{itemize}
|
||||
{% else %}
|
||||
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.
|
||||
|
||||
{% 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
|
||||
sur les campus de l'école soit dans l'hotel Ibis Gerland Mérieux situé 246 rue Marcel Mérieux – 69007 LYON.
|
||||
{% endif %}
|
||||
|
||||
@@ -151,6 +151,12 @@
|
||||
<dd class="col-sm-6"><a href="mailto:{{ email }}">{{ email }}</a></dd>
|
||||
{% endwith %}
|
||||
{% 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>
|
||||
<dd class="col-sm-6">{{ user_object.registration.last_degree }}</dd>
|
||||
|
||||
|
||||
@@ -385,19 +385,19 @@ if TFJM_APP == "TFJM":
|
||||
RULES_LINK = "https://tfjm.org/reglement"
|
||||
|
||||
REGISTRATION_DATES = dict(
|
||||
open=datetime.fromisoformat("2025-01-15T12:00:00+0100"),
|
||||
close=datetime.fromisoformat("2025-03-02T22:00:00+0100"),
|
||||
open=datetime.fromisoformat("2025-11-12T00:00:00+0100"),
|
||||
close=datetime.fromisoformat("2026-01-08T22:00:00+0100"),
|
||||
)
|
||||
|
||||
PROBLEMS = [
|
||||
"Une bonne humeur contagieuse",
|
||||
"Drôles de toboggans",
|
||||
"Plats à tarte gradués",
|
||||
"Transformation de papillons",
|
||||
"Gerrymandering",
|
||||
"Le cauchemar de la ligne 20-25",
|
||||
"Taxes routières",
|
||||
"Points colorés sur un cercle",
|
||||
"Guerre à l'apéro",
|
||||
"Jeu du moulin",
|
||||
"Poison dans les boissons",
|
||||
"Colliers de perles",
|
||||
"Parcours d'escalade",
|
||||
"Malaise dans la salle d'attente",
|
||||
"Double et chiffres",
|
||||
"Tri trop rapide",
|
||||
]
|
||||
elif TFJM_APP == "ETEAM":
|
||||
PREFERRED_LANGUAGE_CODE = 'en'
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
<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, Rennes, 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 %}
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user