diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 21fc99a..e4f83f3 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: TFJM\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-07-09 13:22+0200\n" +"POT-Creation-Date: 2024-10-28 20:14+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Emmy D'Anello \n" "Language-Team: LANGUAGE \n" @@ -217,7 +217,7 @@ msgstr "" msgid "Toggle fullscreen mode" msgstr "Inverse le mode plein écran" -#: chat/templates/chat/content.html:76 tfjm/templates/navbar.html:125 +#: chat/templates/chat/content.html:76 tfjm/templates/navbar.html:126 msgid "Log out" msgstr "Déconnexion" @@ -244,14 +244,14 @@ msgid "ETEAM Chat" msgstr "Chat de l'ETEAM" #: chat/templates/chat/login.html:10 chat/templates/chat/login.html:12 -#: chat/urls.py:13 tfjm/templates/navbar.html:74 +#: chat/urls.py:13 tfjm/templates/navbar.html:72 msgid "Chat" msgstr "Chat" #: chat/templates/chat/login.html:10 chat/templates/chat/login.html:36 #: registration/templates/registration/password_reset_complete.html:10 #: tfjm/templates/base.html:89 tfjm/templates/base.html:90 -#: tfjm/templates/navbar.html:106 +#: tfjm/templates/navbar.html:107 #: tfjm/templates/registration/includes/login.html:22 #: tfjm/templates/registration/login.html:7 #: tfjm/templates/registration/login.html:8 @@ -269,7 +269,7 @@ msgstr "équipes" msgid "round" msgstr "tour" -#: draw/apps.py:10 draw/consumers.py:1042 tfjm/templates/navbar.html:68 +#: draw/apps.py:10 draw/consumers.py:1042 tfjm/templates/navbar.html:66 msgid "Draw" msgstr "Tirage au sort" @@ -311,7 +311,7 @@ msgstr "Le tirage au sort du tournoi {tournament} va commencer." #: draw/consumers.py:256 draw/consumers.py:282 draw/consumers.py:692 #: draw/consumers.py:910 draw/consumers.py:1000 draw/consumers.py:1022 -#: draw/consumers.py:1125 draw/templates/draw/tournament_content.html:5 +#: draw/consumers.py:1124 draw/templates/draw/tournament_content.html:5 msgid "The draw has not started yet." msgstr "Le tirage au sort n'a pas encore commencé." @@ -446,9 +446,9 @@ msgid "" "from the ranking of the first round, in order to mix the teams between the " "two days." msgstr "" -"Le tirage au sort du tour {round} commence. L'ordre de passage est déterminé à " -"partir du classement du premier tour, afin de mélanger les équipes entre les " -"deux jours." +"Le tirage au sort du tour {round} commence. L'ordre de passage est déterminé " +"à partir du classement du premier tour, afin de mélanger les équipes entre " +"les deux jours." #: draw/consumers.py:1034 #, python-brace-format @@ -456,8 +456,8 @@ msgid "" "The draw of the round {round} is starting. The passage order is another time " "randomly drawn." msgstr "" -"Le tirage au sort du tour {round} commence. L'ordre de passage est à nouveau tiré " -"au hasard." +"Le tirage au sort du tour {round} commence. L'ordre de passage est à nouveau " +"tiré au hasard." #: draw/consumers.py:1043 msgid "The draw of the second round is starting!" @@ -1034,18 +1034,18 @@ msgid "The team is already validated or the validation is pending." msgstr "La validation de l'équipe est déjà faite ou en cours." #: participation/forms.py:94 participation/forms.py:367 -#: registration/forms.py:126 registration/forms.py:148 -#: registration/forms.py:170 registration/forms.py:192 -#: registration/forms.py:214 registration/forms.py:236 -#: registration/forms.py:295 registration/forms.py:328 +#: registration/forms.py:141 registration/forms.py:163 +#: registration/forms.py:185 registration/forms.py:207 +#: registration/forms.py:229 registration/forms.py:251 +#: registration/forms.py:310 registration/forms.py:343 msgid "The uploaded file size must be under 2 Mo." msgstr "Le fichier envoyé doit peser moins de 2 Mo." -#: participation/forms.py:96 registration/forms.py:128 -#: registration/forms.py:150 registration/forms.py:172 -#: registration/forms.py:194 registration/forms.py:216 -#: registration/forms.py:238 registration/forms.py:297 -#: registration/forms.py:330 +#: participation/forms.py:96 registration/forms.py:143 +#: registration/forms.py:165 registration/forms.py:187 +#: registration/forms.py:209 registration/forms.py:231 +#: registration/forms.py:253 registration/forms.py:312 +#: registration/forms.py:345 msgid "The uploaded file must be a PDF, PNG of JPEG file." msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG." @@ -2509,7 +2509,7 @@ msgid "Edit tournament" msgstr "Modifier le tournoi" #: participation/templates/participation/tournament_detail.html:80 -#: tfjm/templates/navbar.html:37 +#: tfjm/templates/navbar.html:35 msgid "Teams" msgstr "Équipes" @@ -2652,7 +2652,7 @@ msgid "Warning: non-free format" msgstr "Attention : format non libre" #: participation/views.py:62 tfjm/templates/base.html:84 -#: tfjm/templates/navbar.html:43 +#: tfjm/templates/navbar.html:41 msgid "Create team" msgstr "Créer une équipe" @@ -2665,7 +2665,7 @@ msgid "You are already in a team." msgstr "Vous êtes déjà dans une équipe." #: participation/views.py:103 tfjm/templates/base.html:79 -#: tfjm/templates/navbar.html:48 +#: tfjm/templates/navbar.html:46 msgid "Join team" msgstr "Rejoindre une équipe" @@ -2898,27 +2898,50 @@ msgstr "Marquer comme en attente" msgid "Mark as invalid" msgstr "Marquer comme invalide" -#: registration/forms.py:23 +#: registration/forms.py:25 msgid "role" msgstr "rôle" -#: registration/forms.py:25 +#: registration/forms.py:27 msgid "participant" msgstr "participant⋅e" -#: registration/forms.py:26 registration/models.py:516 +#: registration/forms.py:28 registration/models.py:516 msgid "coach" msgstr "encadrant⋅e" -#: registration/forms.py:36 registration/forms.py:61 registration/forms.py:92 +#: registration/forms.py:38 registration/forms.py:76 registration/forms.py:107 msgid "This email address is already used." msgstr "Cette adresse e-mail est déjà utilisée." -#: registration/forms.py:286 +#: registration/forms.py:45 +#, fuzzy +#| msgid "" +#| "Registrations for the tournament of {tournament} are ending on the {date:" +#| "%Y-%m-%d %H:%M}." +msgid "" +"Registrations are not opened yet. They will open on the {opening_date:%Y-%m-" +"%d %H:%M}." +msgstr "" +"Les inscriptions pour le tournoi de {tournament} se terminent le {date:%d/%m/" +"%Y %H:%M}." + +#: registration/forms.py:49 +#, fuzzy +#| msgid "" +#| "Registrations for the tournament of {tournament} are ending on the {date:" +#| "%Y-%m-%d %H:%M}." +msgid "" +"Registrations for this year are closed since {opening_date:%Y-%m-%d %H:%M}." +msgstr "" +"Les inscriptions pour le tournoi de {tournament} se terminent le {date:%d/%m/" +"%Y %H:%M}." + +#: registration/forms.py:301 msgid "Pending" msgstr "En attente" -#: registration/forms.py:305 registration/forms.py:338 +#: registration/forms.py:320 registration/forms.py:353 msgid "You must upload your receipt." msgstr "Vous devez envoyer votre justificatif." @@ -3909,15 +3932,37 @@ msgstr "" #: registration/templates/registration/signup.html:5 #: registration/templates/registration/signup.html:12 -#: registration/templates/registration/signup.html:26 registration/views.py:48 +#: registration/templates/registration/signup.html:39 registration/views.py:48 msgid "Sign up" msgstr "Inscription" -#: registration/templates/registration/signup.html:21 +#: registration/templates/registration/signup.html:17 +msgid "" +"Thank you for your great interest, but registrations are not opened yet!" +msgstr "" +"Merci pour votre grand intérêt, mais les inscriptions ne sont pas encore ouvertes !" + +#: registration/templates/registration/signup.html:18 +msgid "They will open on:" +msgstr "Elles ouvriront le :" + +#: registration/templates/registration/signup.html:19 +msgid "Please come back at this time to register!" +msgstr "Merci de revenir à ce moment-là pour vous inscrire !" + +#: registration/templates/registration/signup.html:23 +msgid "Registrations are closed for this year. We hope to see you next year!" +msgstr "Les inscriptions sont closes pour cette année. Nous espérons vous revoir l'année prochaine !" + +#: registration/templates/registration/signup.html:24 +msgid "If needed, you can contact us by mail." +msgstr "Si nécessaire, vous pouvez nous contacter par mail." + +#: registration/templates/registration/signup.html:34 msgid "By registering, you certify that you have read and accepted our" msgstr "En vous inscrivant, vous certifiez avoir lu et accepté notre" -#: registration/templates/registration/signup.html:22 +#: registration/templates/registration/signup.html:35 msgid "privacy policy" msgstr "politique de confidentialité" @@ -4274,11 +4319,11 @@ msgstr "Privé, réservé aux utilisateur⋅rices explicitement autorisé⋅es" msgid "Admin users" msgstr "Administrateur⋅rices" -#: tfjm/settings.py:173 +#: tfjm/settings.py:174 msgid "English" msgstr "Anglais" -#: tfjm/settings.py:174 +#: tfjm/settings.py:175 msgid "French" msgstr "Français" @@ -4521,39 +4566,39 @@ msgstr "" "Si vous ne finalisez pas votre inscription avant la date limite indiquée, " "vous ne pourrez malheureusement pas participer au 𝕋𝔽𝕁𝕄²." -#: tfjm/templates/navbar.html:19 tfjm/urls.py:34 +#: tfjm/templates/navbar.html:17 tfjm/urls.py:33 msgid "Home" msgstr "Accueil" -#: tfjm/templates/navbar.html:24 +#: tfjm/templates/navbar.html:22 msgid "Tournament" msgstr "Tournoi" -#: tfjm/templates/navbar.html:28 +#: tfjm/templates/navbar.html:26 msgid "Tournaments" msgstr "Tournois" -#: tfjm/templates/navbar.html:34 +#: tfjm/templates/navbar.html:32 msgid "Users" msgstr "Utilisateur⋅rices" -#: tfjm/templates/navbar.html:54 +#: tfjm/templates/navbar.html:52 msgid "My team" msgstr "Mon équipe" -#: tfjm/templates/navbar.html:59 +#: tfjm/templates/navbar.html:57 msgid "My participation" msgstr "Ma participation" -#: tfjm/templates/navbar.html:80 +#: tfjm/templates/navbar.html:78 msgid "Administration" msgstr "Administration" -#: tfjm/templates/navbar.html:88 +#: tfjm/templates/navbar.html:86 msgid "Search…" msgstr "Chercher…" -#: tfjm/templates/navbar.html:97 +#: tfjm/templates/navbar.html:95 msgid "Return to admin view" msgstr "Retourner à l'interface administrateur⋅rice" @@ -4561,7 +4606,7 @@ msgstr "Retourner à l'interface administrateur⋅rice" msgid "Register" msgstr "S'inscrire" -#: tfjm/templates/navbar.html:118 +#: tfjm/templates/navbar.html:119 msgid "My account" msgstr "Mon compte" diff --git a/registration/forms.py b/registration/forms.py index bbebc06..4f16d42 100644 --- a/registration/forms.py +++ b/registration/forms.py @@ -7,6 +7,8 @@ from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User from django.core.exceptions import ValidationError from django.forms import FileInput +from django.utils import timezone +from django.utils.text import format_lazy from django.utils.translation import gettext_lazy as _ from .models import CoachRegistration, ParticipantRegistration, Payment, \ @@ -36,6 +38,19 @@ class SignupForm(UserCreationForm): self.add_error("email", _("This email address is already used.")) return email + def clean(self): + # Check that registrations are opened + now = timezone.now() + if now < settings.REGISTRATION_DATES['open']: + self.add_error(None, format_lazy(_("Registrations are not opened yet. " + "They will open on the {opening_date:%Y-%m-%d %H:%M}."), + opening_date=settings.REGISTRATION_DATES['open'])) + elif now > settings.REGISTRATION_DATES['close']: + self.add_error(None, format_lazy(_("Registrations for this year are closed since " + "{closing_date:%Y-%m-%d %H:%M}."), + closing_date=settings.REGISTRATION_DATES['close'])) + return super().clean() + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["first_name"].required = True diff --git a/registration/models.py b/registration/models.py index d16e197..e68bab6 100644 --- a/registration/models.py +++ b/registration/models.py @@ -1,7 +1,7 @@ # Copyright (C) 2020 by Animath # SPDX-License-Identifier: GPL-3.0-or-later -from datetime import date, datetime +from datetime import date from django.conf import settings from django.contrib.sites.models import Site @@ -774,7 +774,7 @@ class Payment(models.Model): return checkout_intent tournament = self.tournament - year = datetime.now().year + year = timezone.now().year base_site = "https://" + Site.objects.first().domain checkout_intent = helloasso.create_checkout_intent( amount=100 * self.amount, diff --git a/registration/templates/registration/signup.html b/registration/templates/registration/signup.html index 740e853..35e87e3 100644 --- a/registration/templates/registration/signup.html +++ b/registration/templates/registration/signup.html @@ -9,30 +9,42 @@ {% endblock %} {% block content %} -

{% trans "Sign up" %}

- -
- {% csrf_token %} - {{ form|crispy }} -
- -
- - {% trans "By registering, you certify that you have read and accepted our" %} - {% trans "privacy policy" %}. + {% now "c" as now %} + {% if now < TFJM.REGISTRATION_DATES.open.isoformat %} +
+ {% trans "Thank you for your great interest, but registrations are not opened yet!" %} + {% trans "They will open on:" %} {{ TFJM.REGISTRATION_DATES.open|date:'DATETIME_FORMAT' }}. + {% trans "Please come back at this time to register!" %}
- - - - -
- {{ student_registration_form|crispy }} -
-
- {{ coach_registration_form|crispy }} -
+ {% elif now > TFJM.REGISTRATION_DATES.close.isoformat %} +
+ {% trans "Registrations are closed for this year. We hope to see you next year!" %} + {% trans "If needed, you can contact us by mail." %} +
+ {% else %} +
+ {% csrf_token %} + {{ form|crispy }} +
+ +
+ + {% trans "By registering, you certify that you have read and accepted our" %} + {% trans "privacy policy" %}. +
+ + +
+ +
+ {{ student_registration_form|crispy }} +
+
+ {{ coach_registration_form|crispy }} +
+ {% endif %} {% endblock %} {% block extrajavascript %} diff --git a/registration/tests.py b/registration/tests.py index 289459b..e1ba990 100644 --- a/registration/tests.py +++ b/registration/tests.py @@ -1,14 +1,17 @@ # Copyright (C) 2020 by Animath # SPDX-License-Identifier: GPL-3.0-or-later +from datetime import timedelta import os +from django.conf import settings from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.sites.models import Site from django.core.files.uploadedfile import SimpleUploadedFile -from django.test import TestCase +from django.test import override_settings, TestCase from django.urls import reverse +from django.utils import timezone from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode from participation.models import Team @@ -114,6 +117,9 @@ class TestRegistration(TestCase): self.assertRedirects(response, "http://" + Site.objects.get().domain + str(self.coach.registration.get_absolute_url()), 302, 200) + # Ensure that we are between registration dates + @override_settings(REGISTRATION_DATES={'open': timezone.now() - timedelta(days=1), + 'close': timezone.now() + timedelta(days=1)}) def test_registration(self): """ Ensure that the signup form is working successfully. @@ -223,6 +229,52 @@ class TestRegistration(TestCase): response = self.client.get(reverse("registration:email_validation_resend", args=(user.pk,))) self.assertRedirects(response, reverse("registration:email_validation_sent"), 302, 200) + def test_registration_dates(self): + """ + Test that registrations are working only between registration dates. + """ + self.client.logout() + + # Test that registration between open and close dates are working + with override_settings(REGISTRATION_DATES={'open': timezone.now() - timedelta(days=2), + 'close': timezone.now() + timedelta(days=2)}): + response = self.client.get(reverse("registration:signup")) + self.assertEqual(response.status_code, 200) + self.assertIn(" Register", response.content.decode()) + self.assertNotIn("registrations are not opened", response.content.decode()) + self.assertNotIn("Registrations are closed", response.content.decode()) + + response = self.client.post(reverse("registration:signup")) + self.assertFormError(response.context['form'], None, []) + + # Test that registration before open date is not working + with override_settings(REGISTRATION_DATES={'open': timezone.now() + timedelta(days=1), + 'close': timezone.now() + timedelta(days=2)}): + response = self.client.get(reverse("registration:signup")) + self.assertEqual(response.status_code, 200) + self.assertNotIn(" Register", response.content.decode()) + self.assertIn("registrations are not opened", response.content.decode()) + + response = self.client.post(reverse("registration:signup")) + self.assertEqual(response.status_code, 200) + self.assertFormError(response.context['form'], None, + "Registrations are not opened yet. They will open on the " + f"{settings.REGISTRATION_DATES['open']:%Y-%m-%d %H:%M}.") + + # Test that registration after close date is not working + with override_settings(REGISTRATION_DATES={'open': timezone.now() - timedelta(days=2), + 'close': timezone.now() - timedelta(days=1)}): + response = self.client.get(reverse("registration:signup")) + self.assertEqual(response.status_code, 200) + self.assertNotIn(" Register", response.content.decode()) + self.assertIn("Registrations are closed", response.content.decode()) + + response = self.client.post(reverse("registration:signup")) + self.assertEqual(response.status_code, 200) + self.assertFormError(response.context['form'], None, + "Registrations for this year are closed since " + f"{settings.REGISTRATION_DATES['close']:%Y-%m-%d %H:%M}.") + def test_login(self): """ With a registered user, try to log in diff --git a/tfjm/context_processors.py b/tfjm/context_processors.py index 2f0a043..3ab31fa 100644 --- a/tfjm/context_processors.py +++ b/tfjm/context_processors.py @@ -18,6 +18,7 @@ def tfjm_context(request): 'ML_MANAGEMENT': settings.ML_MANAGEMENT, 'PAYMENT_MANAGEMENT': settings.PAYMENT_MANAGEMENT, 'RECOMMENDED_SOLUTIONS_COUNT': settings.RECOMMENDED_SOLUTIONS_COUNT, + 'REGISTRATION_DATES': settings.REGISTRATION_DATES, 'SINGLE_TOURNAMENT': settings.SINGLE_TOURNAMENT, 'HEALTH_SHEET_REQUIRED': settings.HEALTH_SHEET_REQUIRED, 'VACCINE_SHEET_REQUIRED': settings.VACCINE_SHEET_REQUIRED, diff --git a/tfjm/settings.py b/tfjm/settings.py index 0e1c72b..652e372 100644 --- a/tfjm/settings.py +++ b/tfjm/settings.py @@ -13,6 +13,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.0/ref/settings/ """ +from datetime import datetime import os import sys @@ -365,6 +366,11 @@ if TFJM_APP == "TFJM": LOGO_FILE = "tfjm.svg" RULES_LINK = "https://tfjm.org/reglement" + REGISTRATION_DATES = dict( + open=datetime.fromisoformat("2025-01-08T12:00:00+0100"), + close=datetime.fromisoformat("2025-03-02T22:00:00+0100"), + ) + PROBLEMS = [ "Triominos", "Rassemblements mathématiques", @@ -395,6 +401,11 @@ elif TFJM_APP == "ETEAM": LOGO_FILE = "eteam.png" RULES_LINK = "https://eteam.tfjm.org/rules/" + REGISTRATION_DATES = dict( + open=datetime.fromisoformat("2024-06-01T12:00:00+0200"), + close=datetime.fromisoformat("2024-07-04T20:00:00+0200"), + ) + PROBLEMS = [ "Exploring Flatland", "A Mazing Hive", diff --git a/tfjm/templates/navbar.html b/tfjm/templates/navbar.html index 21915df..25dc01c 100644 --- a/tfjm/templates/navbar.html +++ b/tfjm/templates/navbar.html @@ -96,9 +96,12 @@ {% endif %} {% if not user.is_authenticated %} - + {% now "c" as now %} + {% if TFJM.REGISTRATION_DATES.open.isoformat <= now and now <= TFJM.REGISTRATION_DATES.close.isoformat %} + + {% endif %}