Compare commits
No commits in common. "ece128836a910532d3e52c5ddc4a35e988b7e36f" and "17057a5fe5ac9c8cc0ed8276565a98c44d358dab" have entirely different histories.
ece128836a
...
17057a5fe5
BIN
docs/_static/img/draw_choose_problem.png
vendored
Before Width: | Height: | Size: 27 KiB |
BIN
docs/_static/img/draw_choose_problem_full.png
vendored
Before Width: | Height: | Size: 84 KiB |
BIN
docs/_static/img/draw_end_round_1.png
vendored
Before Width: | Height: | Size: 124 KiB |
BIN
docs/_static/img/draw_example.png
vendored
Before Width: | Height: | Size: 101 KiB |
BIN
docs/_static/img/draw_general.png
vendored
Before Width: | Height: | Size: 107 KiB |
BIN
docs/_static/img/draw_last_rolls.png
vendored
Before Width: | Height: | Size: 16 KiB |
BIN
docs/_static/img/draw_passage_tables.png
vendored
Before Width: | Height: | Size: 81 KiB |
BIN
docs/_static/img/draw_recap.png
vendored
Before Width: | Height: | Size: 48 KiB |
BIN
docs/_static/img/draw_start.png
vendored
Before Width: | Height: | Size: 20 KiB |
BIN
docs/_static/img/draw_tournament_tabs.png
vendored
Before Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 131 KiB |
BIN
docs/_static/img/draw_waiting_passage_order.png
vendored
Before Width: | Height: | Size: 65 KiB |
BIN
docs/_static/img/draw_waiting_passage_order_full.png
vendored
Before Width: | Height: | Size: 92 KiB |
BIN
docs/_static/img/draw_waiting_problem_draw.png
vendored
Before Width: | Height: | Size: 29 KiB |
BIN
docs/_static/img/draw_waiting_problem_draw_full.png
vendored
Before Width: | Height: | Size: 86 KiB |
@ -18,7 +18,7 @@
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'Plateforme du TFJM²'
|
||||
copyright = "2020-2024"
|
||||
copyright = "2020-2023"
|
||||
author = "Animath"
|
||||
|
||||
|
||||
|
@ -2,22 +2,3 @@ Développer la plateforme
|
||||
========================
|
||||
|
||||
Cette page est dédiée aux responsables informatiques qui cherchent à contribuer à la plateforme.
|
||||
|
||||
Présentation
|
||||
------------
|
||||
|
||||
La plateforme d'inscription du TFJM² actuelle est née lors de l'édition 2020. Elle n'est
|
||||
pas la première à exister, elle succède à une précédente, moins fonctionnelle, dont les
|
||||
sources ont été perdues. Elle a été développée par Emmy D'Anello, bénévole pour Animath,
|
||||
qui la maintient au moins jusqu'en 2024.
|
||||
|
||||
La plateforme est développée en Python, utilisant le framework web
|
||||
`Django <https://www.djangoproject.com/>`_. Elle est diponible librement sous licence GPLv3
|
||||
à l'adresse `<https://gitlab.com/animath/si/plateforme-tfjm>`_.
|
||||
|
||||
L'instance de production est accessible à l'adresse `<https://inscription.tfjm.org/>`_.
|
||||
Une instance de développement est accessible à l'adresse `<https://inscription-dev.tfjm.org/>`_.
|
||||
|
||||
Les deux instances sont hébergées sur le serveur d'Animath. La documentation spécifique
|
||||
à l'installation des services d'Animath peut être trouvée à l'adresse
|
||||
`<https://doc.animath.live/>`_.
|
||||
|
@ -1,309 +0,0 @@
|
||||
Installation de la plateforme du TFJM²
|
||||
======================================
|
||||
|
||||
Cette page documente la procédure d'installation de la plateforme du TFJM²,
|
||||
aussi bien dans un environnement de développement que de production.
|
||||
|
||||
|
||||
Installation en production
|
||||
--------------------------
|
||||
|
||||
Installation de Docker
|
||||
""""""""""""""""""""""
|
||||
|
||||
Les outils du TFJM² sont déployés en utilisant `Docker <https://www.docker.com/>`_.
|
||||
Pour plus de détails sur la configuration pour le TFJM², voir
|
||||
`la page dédiée à Docker<docker.html>`_.
|
||||
|
||||
Commencez par installer Docker et Docker-Compose (qui sont a priori déjà installés
|
||||
sur la plateforme du TFJM²), en commençant par installer le dépôt APT de Docker :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Add Docker's official GPG key:
|
||||
sudo apt update
|
||||
sudo apt install ca-certificates curl gnupg
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
|
||||
# Add the repository to Apt sources:
|
||||
echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
|
||||
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt update
|
||||
|
||||
sudo apt install docker-ce docker-compose-plugin
|
||||
|
||||
Installation de la plateforme
|
||||
"""""""""""""""""""""""""""""
|
||||
|
||||
Dans le fichier ``docker-compose.yml``, configurer :
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
version: "3.8"
|
||||
services:
|
||||
# […]
|
||||
inscription:
|
||||
build: https://gitlab.com/animath/si/plateforme.git
|
||||
links:
|
||||
- postgres
|
||||
- redis
|
||||
- elasticsearch
|
||||
env_file:
|
||||
- ./secrets/inscription.env
|
||||
restart: always
|
||||
volumes:
|
||||
- "./data/inscription/media:/code/media"
|
||||
- "/etc/localtime:/etc/localtime:ro"
|
||||
networks:
|
||||
- tfjm
|
||||
labels:
|
||||
- "traefik.http.routers.inscription-tfjm2.rule=Host(`inscription.tfjm.org`, `plateforme.tfjm.org`)"
|
||||
- "traefik.http.routers.inscription-tfjm2.entrypoints=websecure"
|
||||
- "traefik.http.routers.inscription-tfjm2.tls.certresolver=mytlschallenge"
|
||||
|
||||
postgres:
|
||||
image: postgres:16
|
||||
volumes:
|
||||
- "./data/postgresql_16:/var/lib/postgresql/data"
|
||||
restart: always
|
||||
env_file:
|
||||
- ./secrets/postgres.env
|
||||
networks:
|
||||
- tfjm
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
restart: always
|
||||
networks:
|
||||
- tfjm
|
||||
|
||||
elasticsearch:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.9
|
||||
restart: always
|
||||
env_file:
|
||||
- ./secrets/elasticsearch.env
|
||||
networks:
|
||||
- tfjm
|
||||
# […]
|
||||
|
||||
En cas d'instance de pré-production, il est possible de changer de branche en
|
||||
rajoutant par exemple ``#dev`` à la fin de l'URL.
|
||||
|
||||
Les différents paramètres peuvent être modifiés si nécessaire, à commencer par les
|
||||
versions des autres services (PostgreSQL, Redis, Elasticsearch). Penser à mettre à jour
|
||||
cette documentation en cas de mise à jour future. Il est attendu d'avoir un réseau
|
||||
``tfjm`` configuré :
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
networks:
|
||||
tfjm:
|
||||
driver: bridge
|
||||
|
||||
L'URL utilisée peut être modifiée en changeant les options de Traefik, section ``labels``.
|
||||
|
||||
Configuration
|
||||
"""""""""""""
|
||||
|
||||
La configuration se fait essentiellement dans les fichiers d'environnement.
|
||||
|
||||
Pour la base de données, une seule variable d'environnement est requise :
|
||||
``POSTGRES_PASSWORD``, qui correspond au mot de passe maître de PostgreSQL.
|
||||
|
||||
Les variables d'environnement de ElasticSearch se résume à du paramétrage de mémoire :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
discovery.type=single-node
|
||||
ES_JAVA_OPTS=-Xms512m -Xmx512m
|
||||
|
||||
Redis n'a pas besoin de configuration particulière.
|
||||
|
||||
Enfin, pour la plateforme elle-même, il faut configurer les variables suivantes :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
TFJM_STAGE=prod
|
||||
DJANGO_SECRET_KEY=
|
||||
DJANGO_DB_TYPE=PostgreSQL
|
||||
DJANGO_DB_HOST=postgres
|
||||
DJANGO_DB_PORT=
|
||||
DJANGO_DB_NAME=
|
||||
DJANGO_DB_USER=
|
||||
DJANGO_DB_PASSWORD=
|
||||
REDIS_SERVER_HOST=redis
|
||||
REDIS_SERVER_PORT=6379
|
||||
SMTP_HOST=ssl0.ovh.net
|
||||
SMTP_PORT=465
|
||||
SMTP_HOST_USER=contact@tfjm.org
|
||||
SMTP_HOST_PASSWORD=
|
||||
FROM_EMAIL=Contact TFJM²
|
||||
SERVER_EMAIL=contact@tfjm.org
|
||||
HAYSTACK_INDEX_NAME=inscription-tfjm
|
||||
SYMPA_HOST=lists.tfjm.org
|
||||
SYMPA_URL=lists.tfjm.org/sympa
|
||||
SYMPA_EMAIL=contact@tfjm.org
|
||||
SYMPA_PASSWORD=
|
||||
HELLOASSO_CLIENT_ID=
|
||||
HELLOASSO_CLIENT_SECRET=
|
||||
|
||||
* ``TFJM_STAGE`` : ``prod`` ou ``dev``. ``prod`` est utilisé pour la production,
|
||||
``dev`` pour le développement ou pré-production. Permet de désactiver certaines
|
||||
fonctionnalités de production, notamment l'envoi de mails, le cache Redis, les listes
|
||||
de diffusion, et d'activer le débogage. Les erreurs seront ainsi directement affichées
|
||||
en développement tandis qu'elles seront envoyées par mail en production.
|
||||
* ``DJANGO_SECRET_KEY`` : correspond à
|
||||
`la clé secrète Django <https://docs.djangoproject.com/fr/5.0/ref/settings/#secret-key>`_,
|
||||
permettant de signer les sessions et les cookies. Il est important de la garder secrète.
|
||||
Pour la générer, il est possible d'utiliser la commande
|
||||
``python3 manage.py generate_secret_key``.
|
||||
* ``DJANGO_DB_TYPE`` : Le type de base de données à utiliser, parmi
|
||||
``PostgreSQL``, ``MySQL`` et ``SQLite``. ``SQLite`` n'est à utiliser qu'en développement
|
||||
local.
|
||||
* ``DJANGO_DB_HOST`` : L'hôte de la base de données pour les bases de données
|
||||
``PostgreSQL`` et ``MySQL``. Pour ``SQLite``, il faut mettre le chemin vers le fichier
|
||||
de base de données. Dans l'exemple ci-dessus, ``postgres`` est l'hôte Docker.
|
||||
* ``DJANGO_DB_PORT`` : Le port de la base de données. (PostgreSQL ou MySQL uniquement)
|
||||
Si laissé vide, utilisera le port par défaut du service, ``5432`` pour PostgreSQL et
|
||||
``3306`` pour MySQL.
|
||||
* ``DJANGO_DB_NAME`` : Le nom de la base de données. (PostgreSQL ou MySQL uniquement)
|
||||
* ``DJANGO_DB_USER`` : Le nom d'utilisateur de la base de données.
|
||||
(PostgreSQL ou MySQL uniquement)
|
||||
* ``DJANGO_DB_PASSWORD`` : Le mot de passe de la base de données.
|
||||
(PostgreSQL ou MySQL uniquement)
|
||||
* ``REDIS_SERVER_HOST`` : L'hôte de Redis (en production uniquement). Dans l'exemple
|
||||
ci-dessus, ``redis`` est l'hôte Docker.
|
||||
* ``REDIS_SERVER_PORT`` : Le port de Redis (en production uniquement). Si laissé vide,
|
||||
utilisera le port par défaut du service, ``6379``.
|
||||
* ``SMTP_HOST`` : L'hôte du serveur SMTP à utiliser pour envoyer les mails. Utilise par
|
||||
défaut le serveur d'OVH.
|
||||
* ``SMTP_PORT`` : Le port du serveur SMTP à utiliser pour envoyer les mails.
|
||||
* ``SMTP_HOST_USER`` : Le nom d'utilisateur du serveur SMTP à utiliser pour envoyer les
|
||||
mails. Correspond à l'identifiant OVH.
|
||||
* ``SMTP_HOST_PASSWORD`` : Le mot de passe du serveur SMTP à utiliser pour envoyer les
|
||||
mails. Correspond au mot de passe OVH.
|
||||
* ``FROM_EMAIL`` : Le nom lisible à utiliser comme expéditeur des mails
|
||||
(défaut : Contact TFJM²).
|
||||
* ``SERVER_EMAIL`` : L'adresse mail à utiliser comme expéditeur des mails
|
||||
(défaut : contact@tfjm.org).
|
||||
* ``HAYSTACK_INDEX_NAME`` : Le nom de l'index ElasticSearch à utiliser pour les recherches
|
||||
dans ElasticSearch (défaut : inscription-tfjm).
|
||||
* ``SYMPA_HOST`` : Le domaine des listes de diffusion Sympa utilisé.
|
||||
* ``SYMPA_URL`` : L'URL du serveur Sympa à utiliser pour gérer les listes de diffusion.
|
||||
* ``SYMPA_EMAIL`` : L'adresse mail à utiliser pour se connecter à Sympa.
|
||||
* ``SYMPA_PASSWORD`` : Le mot de passe à utiliser pour se connecter à Sympa.
|
||||
* ``HELLOASSO_CLIENT_ID`` : L'identifiant client HelloAsso à utiliser pour gérer les
|
||||
paiements HelloAsso.
|
||||
* ``HELLOASSO_CLIENT_SECRET`` : Le secret client HelloAsso à utiliser pour gérer les
|
||||
paiements HelloAsso. Doit être maintenu secret.
|
||||
|
||||
Installation de la base de données
|
||||
""""""""""""""""""""""""""""""""""
|
||||
|
||||
Pour gérer la base de données PostgreSQL, on peut utiliser les commandes suivantes :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker compose up -d postgres
|
||||
sudo docker compose exec -u postgres postgres createuser -P inscription_tfjm # Création du compte `inscription_tfjm` en demander un mot de passe. À ne faire qu'une seule fois
|
||||
sudo docker compose exec -u postgres postgres createdb -O inscription_tfjm inscription_tfjm # Création de la base de données `inscription_tfjm` en utilisant le compte `inscription_tfjm`
|
||||
sudo docker compose exec -u postgres pg_dump inscription_tfjm > inscription_tfjm.sql # Pour sauvegarder la base de données `inscription_tfjm`
|
||||
sudo docker compose exec -u postgres dropdb inscription_tfjm # Pour supprimer la base de données `inscription_tfjm`
|
||||
|
||||
La suppression et la recréation sont utiles en cas de réinitialisation annuelle de la base
|
||||
de données.
|
||||
|
||||
|
||||
Lancement
|
||||
"""""""""
|
||||
|
||||
Pour lancer la plateforme, il suffit de lancer la commande suivante :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker compose up -d inscription
|
||||
|
||||
Pour arrêter la plateforme, il suffit de lancer la commande suivante :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker compose stop inscription
|
||||
|
||||
En cas de mise à jour de la plateforme, il suffit de lancer la commande suivante :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo docker compose up -d --build inscription
|
||||
|
||||
Selon le principe de Docker, les données sont conservées dans un volume Docker, ici
|
||||
dans le dossier ``data/inscription``. Le reste est volatile, puisqu'il peut être recréé
|
||||
à partir du code source. Attention donc de ne pas coder en production (ce qui est de toute
|
||||
façon à proscrire !).
|
||||
|
||||
Les migrations de base de données sont automatiquement appliquées au lancement de la
|
||||
plateforme, de même que la collecte de fichiers statiques ou encore la génération de la
|
||||
documentation. Il n'y a rien à faire de spécial post-lancement, si ce n'est vérifier que
|
||||
tout fonctionne correctement.
|
||||
|
||||
|
||||
Installation en développement
|
||||
-----------------------------
|
||||
|
||||
L'installation sur une machine locale est plus légère et est utile pour pouvoir tester
|
||||
rapidement.
|
||||
|
||||
Commencez par récupérer le code source de la plateforme :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://gitlab.com/animath/si/plateforme-tfjm.git
|
||||
cd plateforme-tfjm
|
||||
|
||||
Afin de pouvoir isoler l'environnement de développement, il est conseillé d'utiliser
|
||||
un environnement virtuel Python. Commencez alors par installer Python (si ce n'est pas
|
||||
déjà fait, au moins en version 3.10) ainsi que ``virtualenv``, puis vous pouvez créer
|
||||
l'environnement virtuel, et entrer dedans :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
Il vous faut ensuite installer les dépendances de la plateforme :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
Pour exécuter des tests, il est nécessaire d'installer ``tox`` en supplément.
|
||||
|
||||
Ensuite, vous devez initialiser la base de données locale (qui sera stockée dans un
|
||||
fichier SQLite ``db.sqlite3``) :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 manage.py migrate
|
||||
|
||||
Enfin, vous pouvez lancer la plateforme :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 manage.py runserver
|
||||
|
||||
Vous pouvez alors accéder à la plateforme à l'adresse `<http://localhost:8000/>`_.
|
||||
Elle se recharge automatiquement à chaque modification du code source, inutile de la
|
||||
relancer.
|
||||
|
||||
Pour arrêter la plateforme, il suffit d'appuyer sur ``Ctrl+C`` dans le terminal.
|
||||
|
||||
Pour vous créer un compte administrateur⋅rice, il suffit de lancer la commande suivante :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 manage.py createsuperuser
|
||||
|
||||
En cas de problème, vous pouvez librement supprimer la base de données locale et la
|
||||
recréer en réexécutant la commande ``python3 manage.py migrate``.
|
645
docs/draw.rst
@ -1,645 +0,0 @@
|
||||
Tirage au sort
|
||||
==============
|
||||
|
||||
La phase de tirage au sort est celle qui va déterminer d'une part
|
||||
dans quelle poule se trouve chaque équipe et dans quelle ordre elles
|
||||
défendront leurs problèmes, et d'autre part quel problème défendra
|
||||
quelle équipe. Cette phase a lieu dans la semaine qui précède le
|
||||
tournoi, typiquement le mardi entre 19h et 21h.
|
||||
|
||||
Exception pour la finale nationale : seul le premier tour est tiré
|
||||
au sort à ce moment-là, le tirage au sort pour le second tour est
|
||||
réalisé immédiatement après le premier tour et dépend des résultats.
|
||||
Sinon, pour les finales régionales, les tirages au sort pour les
|
||||
tours 1 et 2 sont réalisés successivement, bien que les solutions
|
||||
à opposer et à rapporter pour le second tour ne sont pas accessibles
|
||||
avant la fin du premier tour.
|
||||
|
||||
**Disclaimer :** si cette documentation est normalement tenue à jour,
|
||||
seul le règlement et ses annexes font foi. Elle est essentiellement ici
|
||||
pour décrire le fonctionnement de la plateforme vis-à-vis du tirage
|
||||
au sort. En cas de litige, merci de se fier au règlement, et de
|
||||
s'y référer `sur le site du TFJM² <https://tfjm.org/reglement/>`_.
|
||||
|
||||
|
||||
Principe
|
||||
--------
|
||||
|
||||
.. warning:: Cette section arborde en détail le fonctionnement théorique
|
||||
du tirage au sort. Si vous souhaitez comprendre comment se déroule
|
||||
le tirage au sort en pratique sur la plateforme, merci de vous référer
|
||||
directement à la section `Déroulement du tirage`_.
|
||||
|
||||
Composition des poules
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Le principe du tirage au sort est détaillé dans la fiche pratique dédiée.
|
||||
|
||||
Chaque équipe commence par désigner un⋅e capitaine d'équipe qui s'occupera
|
||||
de réaliser les tirages et de prendre les décisions.
|
||||
|
||||
Les poules sont triées de la plus grande à la plus petite, en terme de
|
||||
nombre d'équipes.
|
||||
|
||||
Les capitaines d'équipe lancent un dé entre 1 et 100. S'il y a des égalités,
|
||||
alors les équipes concernées relancent leur dé jusqu'à ne plus avoir d'égalité.
|
||||
Ce score détermine les compositions des poules pour les deux tours (sauf pour
|
||||
la finale nationale ou ce n'est que le premier tour).
|
||||
|
||||
On trie ensuite les équipes par ordre croissant de score de dé. On remplit
|
||||
ensuite les poules une à une de façon gloutonne : si par exemple la
|
||||
première poule est une poule à 3 équipes, alors on prend les 3 premières
|
||||
équipes de la liste et on les place dans cette poule. On recommence ensuite
|
||||
avec la deuxième poule, etc.
|
||||
|
||||
Au sein d'une poule, l'ordre de passage des équipes est déterminé par
|
||||
l'ordre des restes modulo 100 des scores de dé multipliés par 27.
|
||||
Par exemple, si les scores de dé sont 12, 34 et 56, alors si on
|
||||
multiplie par 27 et qu'on prend le reste modulo 100, on obtient
|
||||
respectivement 24, 82 et 52. L'ordre de passage sera donc l'équipe 1,
|
||||
l'équipe 3, puis l'équipe 2. Ce choix est réalisé pour avoir un semblant
|
||||
déterministe de mélange.
|
||||
|
||||
Pour le second tour, on considère à nouveau les scores de dé, où l'équipe
|
||||
qui a eu le score le plus faible sera dans la première poule, celle qui
|
||||
a eu le second score le plus faible dans la deuxième poule, etc. L'ordre
|
||||
de passage est cette fois-ci plus simple, puisqu'il est directement croissant
|
||||
avec les scores de dé. Exception : pour les poules à 5, l'équipe avec le
|
||||
score le plus gros sera la dernière de la première poule, et il ne peut
|
||||
y avoir qu'une seule poule à 5 équipes.
|
||||
|
||||
Considérons par exemple un tournoi fictif composé de 11 équipes, avec
|
||||
une poule de 5 équipes et deux poules de 3 équipes. Les équipes sont
|
||||
AAA, BBB, CCC, DDD, EEE, FFF, GGG, HHH, III, JJJ et KKK. Les scores
|
||||
de dés sont :
|
||||
|
||||
.. table:: Exemple de tirage de dés et de répartition dans les poules
|
||||
|
||||
+-----------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| Équipe | AAA | BBB | CCC | DDD | EEE | FFF | GGG | HHH | III | JJJ | KKK |
|
||||
+=============================+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+
|
||||
| Score de dé | 45 | 17 | 64 | 3 | 98 | 41 | 34 | 63 | 86 | 23 | 70 |
|
||||
+-----------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| Poule au 1\ :sup:`er` tour | B | A | B | A | C | A | A | B | C | A | C |
|
||||
+-----------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| Poule au 2\ :sup:`ème` tour | C | B | B | A | A | B | A | A | A | C | C |
|
||||
+-----------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
|
||||
Explication : les équipes **DDD**, **BBB**, **JJJ**, **GGG** et **FFF** sont les
|
||||
équipes ayant réalisé le plus bas score (respectivement 3, 17, 23, 34 et, 41) et
|
||||
sont alors placées dans la poule **A**. Les équipes **AAA**, **HHH** et **CCC**
|
||||
sont les trois suivantes (avec pour scores 45, 63 et 64) et composeront alors
|
||||
la poule **B** tandis que les équipes **KKK**, **III** et **EEE** (avec pour
|
||||
scores 70, 86 et 98) seront dans la poule **C** au premier tour.
|
||||
|
||||
Ainsi pour le second tour, l'équipe **DDD** ira dans la poule **A**, **BBB**
|
||||
dans la poule **B**, **JJJ** dans la poule **C**, **GGG** dans la poule **A**,
|
||||
**FFF** dans la poule **B**, **AAA** dans la poule **C**, **HHH** dans la poule
|
||||
**A**, **CCC** dans la poule **B**, **KKK** dans la poule **C**, **III** dans
|
||||
la poule **A** et enfin **EEE** également dans la poule **A** puisqu'elle y est
|
||||
forcée.
|
||||
|
||||
Pour ce qui est de l'ordre de passage :
|
||||
|
||||
.. table:: Exemple d'ordre de passage pour le premier tour
|
||||
|
||||
+--------------------------------+-----------------------------+-----------------+-----------------+
|
||||
| Poule | Poule A | Poule B | Poule C |
|
||||
+--------------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| Équipe | DDD | BBB | JJJ | GGG | FFF | AAA | HHH | CCC | KKK | III | EEE |
|
||||
+================================+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+
|
||||
| Score de dé | 3 | 17 | 23 | 34 | 41 | 45 | 63 | 64 | 70 | 86 | 98 |
|
||||
+--------------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| Score de dé fois 27 modulo 100 | 81 | 59 | 21 | 18 | 7 | 15 | 1 | 28 | 90 | 22 | 46 |
|
||||
+--------------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| Ordre de passage dans la poule | 5 | 4 | 3 | 2 | 1 | 2 | 1 | 3 | 3 | 1 | 2 |
|
||||
+--------------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
|
||||
.. table:: Exemple d'ordre de passage pour le second tour
|
||||
|
||||
+--------------------------------+-----------------------------+-----------------+-----------------+
|
||||
| Poule | Poule A | Poule B | Poule C |
|
||||
+--------------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| Équipe | DDD | GGG | HHH | III | EEE | BBB | FFF | CCC | JJJ | AAA | KKK |
|
||||
+================================+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+=====+
|
||||
| Score de dé | 3 | 34 | 63 | 86 | 98 | 17 | 41 | 64 | 23 | 45 | 70 |
|
||||
+--------------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
| Ordre de passage dans la poule | 1 | 2 | 3 | 4 | 5 | 1 | 2 | 3 | 1 | 2 | 3 |
|
||||
+--------------------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
|
||||
|
||||
Pour la finale nationale, les ordres de passage et les répartitions dans les poules
|
||||
du second tour sont décidé⋅es dans l'ordre décroissant des résultats obtenus au
|
||||
premier tour. Si par exemple l'équipe **AAA** a fini en tête du premier tour, elle
|
||||
sera alors la première à passer dans la poule **A** du second tour.
|
||||
|
||||
|
||||
Tirage des problèmes
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Une fois les équipes réparties dans les poules, on tire au sort les problèmes. Ce
|
||||
tirage se déroule poule par poule.
|
||||
|
||||
Au début du tirage au sort des problèmes d'une poule, les équipes de la poule
|
||||
commencent par tirer un nouveau dé à 100 faces. S'il y a des égalités, alors
|
||||
les équipes concernées relancent leur dé jusqu'à ne plus avoir d'égalité.
|
||||
Les équipes sont triées dans l'ordre décroissant de leur score de dé. L'équipe
|
||||
ayant tiré le plus gros score est invitée à tirer son problème en première.
|
||||
|
||||
Il y a deux contraintes de tirage :
|
||||
|
||||
* Il n'est pas possible de choisir le même problème qu'une autre équipe de la poule ;
|
||||
* Il n'est pas possible de défendre le même problème pour les deux jours.
|
||||
|
||||
À noter qu'il n'est pas impossible de tirer et de choisir un problème qui n'a
|
||||
pas été traité par l'équipe, bien que cela est fortement non recommandé.
|
||||
|
||||
Une fois l'ordre de tirage établi, les équipes tirent tour à tour leur problème,
|
||||
tant qu'elle ne l'ont pas encore choisi. Pour cela, l'équipe active tire au sort
|
||||
un problème. Il est attendu que le problème tiré garantisse les deux contraintes,
|
||||
et que donc il n'est pas possible de tirer un problème déjà choisi par une autre
|
||||
équipe, ou bien le problème défendu lors du tour 1 si on est au tour 2.
|
||||
|
||||
L'équipe a désormais deux choix :
|
||||
|
||||
* Accepter le problème. Dans ce cas, ce sera ce problème qu'elle défendra, et
|
||||
son tirage est termé.
|
||||
* Refuser le problème. Dans ce cas, la main passe à l'équipe suivante, selon le
|
||||
score des dés. Exception : si le problème tiré est un problème qui a déjà été
|
||||
refusé auparavant, alors dans ce cas l'équipe peut immédiatement tirer un
|
||||
nouveau problème (mais elle a tout de même le choix de l'accepter ou de le
|
||||
refuser).
|
||||
|
||||
**Attention :** si une équipe refuse trop de problèmes, alors elle pourra être
|
||||
pénalisée. Chaque équipe a droit à ``P - 5`` refus sans pénalités, où ``P``
|
||||
est le nombre de problèmes disponibles cette année. Par exemple, s'il y a
|
||||
8 problèmes cette année, alors les équipes ont droit à 3 refus sans pénalités.
|
||||
Seuls les refus distincts comptent : refuser une deuxième fois un problème
|
||||
déjà refusé ne compte pas. Au-delà de ces refus gratuits, l'équipe se verra
|
||||
dotée d'une pénalité de 25 % sur le coefficient de l'oral de défense, par
|
||||
refus. Par exemple, si une équipe refuse 4 problèmes avec un coefficient
|
||||
sur l'oral de défense normalement à ``1.6``, son coefficient passera à ``1.2``.
|
||||
|
||||
Une fois que toutes les équipes de la poule ont tiré leur problème, on passe
|
||||
à la poule suivante. Une fois que toutes les poules ont vu leurs problèmes
|
||||
tirés, on recommence pour le second tour, à partir de la première poule (déjà
|
||||
définie), sauf pour la finale nationale. Le tirage au sort est terminé lorsque
|
||||
toutes les poules ont vu leurs problèmes tirés pour tous les tours.
|
||||
|
||||
|
||||
Récupération des solutions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Les solutions défendues pour le premier tour dans une poule sont immédiatement
|
||||
accessibles après le tirage au sort aux équipes qui doivent opposer ou rapporter
|
||||
ces solutions. Pour le second tour, elles ne seront disponibles qu'après la fin
|
||||
du premier tour.
|
||||
|
||||
Elles seront également envoyées par les organisateurices localaux, dans les mêmes
|
||||
délais.
|
||||
|
||||
|
||||
Tirage au sort sur la plateforme
|
||||
--------------------------------
|
||||
|
||||
Le tirage est sort est géré directement sur la plateforme, de façon intuitive,
|
||||
ergonomique et dynamique.
|
||||
|
||||
Il est accessible à l'adresse `<https://inscription.tfjm.org/draw/>`_, ou bien
|
||||
en accédant à l'onglet « Tirage au sort » sur le bandeau de navigation, pourvu
|
||||
d'être organisateurice ou bien d'être dans une équipe validée.
|
||||
|
||||
|
||||
Présentation de l'interface
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
L'interface est divisée en 5 sections :
|
||||
|
||||
* Le nom du tournoi dans lequel se passe le tirage au sort ;
|
||||
* Les derniers résultats de dés par équipe ;
|
||||
* Le récapitulatif du tirage au sort en cours ;
|
||||
* L'étape actuelle ;
|
||||
* Les tableaux de passage par poule.
|
||||
|
||||
.. figure:: _static/img/draw_general.png
|
||||
:alt: Interface générale du tirage au sort
|
||||
|
||||
Interface générale du tirage au sort
|
||||
|
||||
Sur cette capture d'écran, on voit par exemple que l'on est actuellement dans le
|
||||
tournoi de Strasbourg. Le tirage du tour 1 est déjà terminé, et on est en train
|
||||
de tirer la poule B2.
|
||||
|
||||
Nom du tournoi
|
||||
..............
|
||||
|
||||
Les différents onglets représentent les différents tournois.
|
||||
|
||||
Pour une équipe participante ou un⋅e organisateurice local⋅e, il peut n'y avoir
|
||||
qu'un seul tournoi, celui qui concerne l'utilisateurice.
|
||||
|
||||
Les administrateurices ont accès à tous les onglets, ce qui peut permettre de
|
||||
passer d'un tirage au sort à un autre facilement, en un clic et sans délai.
|
||||
|
||||
.. figure:: _static/img/draw_tournament_tabs.png
|
||||
:alt: Onglets des différents tirages au sort par tournoi
|
||||
|
||||
Le tournoi de Strasbourg est le tournoi sélectionné dans cet exemple.
|
||||
|
||||
|
||||
Derniers résultats de dés
|
||||
.........................
|
||||
|
||||
Les derniers jets de dés sont affichés dans cette section, avec le trigramme de
|
||||
chaque équipe et son score de dé s'il existe. Le score est vert si l'équipe a
|
||||
déjà tiré son dé, sinon il est jaune.
|
||||
|
||||
Les organisateurices peuvent cliquer sur le score de dé pour jeter un dé à la place
|
||||
de l'équipe, ce qui est notamment utile à des fins de débuggage ou de souci technique
|
||||
avec l'équipe en question.
|
||||
|
||||
.. figure:: _static/img/draw_last_rolls.png
|
||||
:alt: Derniers jets de dés
|
||||
|
||||
Ici, les équipes BBB, DDD, EEE, GGG et HHH ont déjà tiré leur dé, avec pour scores
|
||||
respectifs 57, 66, 58, 41 et 68, tandis que les équipes AAA, CCC, FFF et III n'ont
|
||||
pas encore tiré leur dé.
|
||||
|
||||
Récapitulatif du tirage au sort
|
||||
...............................
|
||||
|
||||
La section de gauche affiche le récapitulatif du tirage au sort en cours.
|
||||
Elle n'est là qu'à des fins d'affichage, il n'est pas possible d'interagir avec.
|
||||
|
||||
La colonne de gauche concerne le premier tour, et la colonne de droite le second tour.
|
||||
Chaque colonne est divisée en plusieurs parties, une pour chaque poule. Enfin, chaque
|
||||
poule contient la liste des équipes membres, triées par ordre de passage. Elles ne sont
|
||||
affichées que lorsque l'ordre de passage est déterminé.
|
||||
|
||||
Le tour actuel de tirage est mis en surbrillance. La poule actuelle qui réalise son
|
||||
tirage est sur fond vert, l'équipe qui doit tirer son problème est sur fond bleu.
|
||||
|
||||
La cellule d'une équipe affiche son trigramme, son problème sélectionné pour ce tour
|
||||
(s'il est déjà choisi), l'ensemble des problèmes qu'elle a refusé, et le cas échéant
|
||||
ses pénalités.
|
||||
|
||||
.. figure:: _static/img/draw_recap.png
|
||||
:alt: Récapitulatif du tirage au sort
|
||||
|
||||
Récapitulatif du tirage au sort en cours
|
||||
|
||||
La poule A1 est composée des équipes **GGG**, **AAA** et **III**, qui défendront
|
||||
dans cet ordre. L'équipe **GGG** a tiré le problème 7, et l'a accepté. L'équipe
|
||||
**AAA** a tiré le problème 3, et l'a accepté, après avoir refusé le problème 2.
|
||||
L'équipe **III** a tiré le problème 1, et l'a accepté, après avoir refusé les
|
||||
problèmes 3, 4, 2 et 6, et a donc une pénalité de 25 % sur son coefficient de
|
||||
l'oral de défense. Notez qu'en poule B1, l'équipe **BBB** a bien pu accepter le
|
||||
problème 8 après l'avoir refusé une première fois.
|
||||
|
||||
Dans la poule B2, actuellement en tirage, l'équipe **EEE** a déjà accepté le
|
||||
problème 6, tandis que l'équipe **CCC** a refusé les problèmes 5 et 2 et que
|
||||
l'équipe **AAA** a refusé le problème 2. C'est actuellement au tour de
|
||||
l'équipe **AAA**, qui doit choisir d'accepter ou de refuser le problème 7
|
||||
qu'elle vient de tirer.
|
||||
|
||||
Notez la présence du bouton « Annuler la dernière étape », visible uniquement
|
||||
pour les organisateurices. Il permet d'annuler la dernière action qui vient
|
||||
d'avoir lieu. Il est utile en cas de souci technique ou de mauvaise manipulation.
|
||||
|
||||
En cas de besoin majeur, un bouton « Annuler » est disponible en bas de l'interface
|
||||
afin de réinitialiser le tirage.
|
||||
|
||||
|
||||
Étape actuelle
|
||||
..............
|
||||
|
||||
La partie de droite de l'écran est dédiée à l'explication de l'étape en cours du tirage
|
||||
au sort. Tout est expliqué dans un encadré bleu.
|
||||
|
||||
À noter qu'un bouton « Exporter » peut être disponible pour les organisateurices à la
|
||||
suite du tirage d'une poule. Il est utile pour valider le tirage et transmettre les
|
||||
données au reste de la plateforme, débloquant notamment l'accès aux différentes solutions
|
||||
pour les équipes.
|
||||
|
||||
Le tirage au sort est découpé en 4 phases majeures :
|
||||
|
||||
Composition des poules
|
||||
''''''''''''''''''''''
|
||||
|
||||
La première phase est la composition des poules. Elle est détaillée dans la section
|
||||
« Composition des poules ». Le texte affiché :
|
||||
|
||||
.. note::
|
||||
Nous allons commencer le tirage des problèmes.
|
||||
Vous pouvez à tout moment poser toute question si quelque chose n'est pas clair ou ne va pas.
|
||||
|
||||
Nous allons d'abord tirer les poules et l'ordre de passage pour le premier tour avec toutes les équipes puis pour chaque poule, nous tirerons l'ordre de tirage pour le tour et les problèmes.
|
||||
|
||||
Les capitaines, vous pouvez désormais toustes lancer un dé 100, en cliquant sur le gros bouton. Les poules et l'ordre de passage lors du premier tour sera l'ordre croissant des dés, c'est-à-dire que le plus petit lancer sera le premier à passer dans la poule A.
|
||||
|
||||
Pour plus de détails sur le déroulement du tirage au sort, le règlement est accessible sur https://tfjm.org/reglement.
|
||||
|
||||
Un gros émoji « dé » 🎲 est affiché pour les participant⋅es, leur permettant de lancer
|
||||
leur dé. Le résultat sera affiché dans la section « Derniers jets de dés », et le
|
||||
bouton disparaîtra.
|
||||
|
||||
Les organisateurices peuvent également appuyer sur le bouton, ce qui aura pour effet
|
||||
de lancer un dé pour une équipe qui n'a pas encore lancé son dé. Cela peut être utile
|
||||
pour débugger ou pour aider une équipe qui a un souci technique. Rappelons toutefois
|
||||
qu'il suffit de cliquer sur le score de dé d'une équipe pour lancer son dé à sa place.
|
||||
|
||||
.. figure:: _static/img/draw_waiting_passage_order.png
|
||||
:alt: Étape de composition des poules
|
||||
|
||||
Le tirage au sort est en attente de la composition des poules.
|
||||
|
||||
|
||||
Ordre de tirage des problèmes
|
||||
'''''''''''''''''''''''''''''
|
||||
|
||||
Une fois les poules constituées, il faut déterminer dans quel ordre les équipes
|
||||
tireront leur problème, par le biais d'un nouveau jet de dé. Un texte par exemple
|
||||
peut être :
|
||||
|
||||
.. note::
|
||||
Nous passons au tirage des problèmes pour la poule Poule A1, entre les équipes GGG, AAA, III. Les capitaines peuvent lancer un dé 100 en cliquant sur le gros bouton pour déterminer l'ordre de tirage. L'équipe réalisant le plus gros score pourra tirer en premier.
|
||||
|
||||
Pour plus de détails sur le déroulement du tirage au sort, le règlement est accessible sur https://tfjm.org/reglement.
|
||||
|
||||
L'émoji « dé » 🎲 n'est affiché que pour les équipes membre de la poule concernée.
|
||||
Encore une fois, les organisateurices peuvent lancer le dé à la place d'une équipe
|
||||
en cliquant sur son score de dé, ou bien en cliquant sur l'émoji.
|
||||
|
||||
.. figure:: _static/img/draw_waiting_choose_problem_order.png
|
||||
:alt: Étape de tirage de l'ordre de tirage des problèmes
|
||||
|
||||
Le tirage au sort est en attente du tirage l'ordre de tirage pour la poule A1.
|
||||
|
||||
|
||||
Tirage des problèmes
|
||||
''''''''''''''''''''
|
||||
|
||||
Une fois l'ordre de tirage déterminé, les équipes sont invitées à tirer leurs
|
||||
problèmes. Le texte affiché est par exemple :
|
||||
|
||||
.. note::
|
||||
C'est au tour de l'équipe GGG de choisir son problème. Cliquez sur l'urne au milieu pour tirer un problème au sort.
|
||||
|
||||
Pour plus de détails sur le déroulement du tirage au sort, le règlement est accessible sur https://tfjm.org/reglement.
|
||||
|
||||
Un émoji « urne » 🗳️ est visible uniquement pour l'équipe active, qui doit tirer
|
||||
son problème. Elle est invitée à cliquer dessus. À nouveau, les organisateurices
|
||||
peuvent cliquer dessus à la place de l'équipe.
|
||||
|
||||
.. figure:: _static/img/draw_waiting_problem_draw.png
|
||||
:alt: Étape de tirage des problèmes
|
||||
|
||||
Le tirage au sort est en attente du tirage du problème de l'équipe GGG.
|
||||
|
||||
|
||||
Choix du problème
|
||||
'''''''''''''''''
|
||||
|
||||
Une fois le problème tiré, l'équipe peut alors choisir de l'accepter ou de le
|
||||
refuser. Le texte affiché est par exemple :
|
||||
|
||||
.. note::
|
||||
L'équipe GGG a tiré le problème 7 : Drôles de cookies. Elle peut décider d'accepter ou de refuser ce problème. Il reste 3 refus sans pénalité.
|
||||
|
||||
Pour plus de détails sur le déroulement du tirage au sort, le règlement est accessible sur https://tfjm.org/reglement.
|
||||
|
||||
Deux boutons sont affichés sur la page, un bouton « Accepter », en vert,
|
||||
et un bouton « Refuser », en rouge. L'équipe peut cliquer sur l'un ou l'autre
|
||||
pour faire son choix. Les organisateurices peuvent également cliquer sur l'un
|
||||
ou l'autre pour faire le choix à la place de l'équipe. Ces boutons sont
|
||||
invisibles pour les autres équipes.
|
||||
|
||||
.. figure:: _static/img/draw_choose_problem.png
|
||||
:alt: Étape de choix du problème
|
||||
|
||||
L'équipe GGG a tiré le problème 7. Elle peut choisir de l'accepter ou de le refuser.
|
||||
|
||||
.. TODO
|
||||
.. note::
|
||||
Cette section sera mise à jour plus tard.
|
||||
|
||||
|
||||
Tableaux de passage par poule
|
||||
.............................
|
||||
|
||||
Ces tableaux, actualisés en temps réel, permettent d'afficher quels seront les
|
||||
différents passages lors d'une poule. En particulier, quelle équipe défendra
|
||||
quel problème, et quelle équipe opposera et laquelle rapportera.
|
||||
|
||||
.. figure:: _static/img/draw_passage_tables.png
|
||||
:alt: Tableaux de passage par poule
|
||||
|
||||
Tableaux de passage par poule
|
||||
|
||||
|
||||
Déroulement du tirage
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Cette section décrit le déroulement du tirage au sort sur la plateforme, sans
|
||||
rentrer dans les détails théoriques. Si la partie théorique vous intéresse,
|
||||
rendez-vous à la section `Principe`_.
|
||||
|
||||
Démarrage du tirage au sort
|
||||
...........................
|
||||
|
||||
Si le tirage n'a pas encore commencé, un message d'alerte s'affiche.
|
||||
|
||||
Les organisateurices peuvent lancer le tirage au sort en cliquant sur le bouton
|
||||
« Démarrer ! ». Iels doivent d'abord paramétrer le format du tirage, en indiquant
|
||||
le nombre d'équipe par poule, en séparant les nombres par des plus. Par exemple,
|
||||
pour un tournoi de 9 équipes avec 3 poules de 3 équipes, on écrira ``3+3+3``.
|
||||
|
||||
.. figure:: _static/img/draw_start.png
|
||||
:alt: Création du tirage au sort
|
||||
|
||||
Le formulaire de lancement du tirage au sort. Ici, on souhaite un tirage
|
||||
au sort avec 3 poules de 3 équipes, pour le tournoi de Strasbourg.
|
||||
|
||||
Attention : si toutes les poules n'ont pas la même capacité, il est essentiel de
|
||||
mettre les poules dans l'ordre décroissant de capacité. Par exemple, pour un tournoi
|
||||
de 8 équipes avec une poule de 5 équipes et une poules de 3 équipes, il faut
|
||||
écrire ``5+3``. De plus, il n'est pas possible d'avoir 2 poules de 5 équipes.
|
||||
|
||||
|
||||
Composition des poules
|
||||
......................
|
||||
|
||||
Les capitaines d'équipe sont invités à lancer un dé à 100 faces. Pour cela, ils
|
||||
peuvent cliquer sur le gros bouton avec l'émoji « dé » 🎲 sur la droite de l'écran.
|
||||
Le résultat est affiché dans la section « Derniers jets de dés ».
|
||||
|
||||
.. figure:: _static/img/draw_waiting_passage_order_full.png
|
||||
:alt: Étape de composition des poules
|
||||
|
||||
Le tirage au sort est en attente de la composition des poules.
|
||||
Les équipes **AAA**, **CCC** et **EEE** ont déjà lancé leur dé, avec pour
|
||||
scores 66, 34 et 97.
|
||||
|
||||
Une fois le bouton cliqué, il disparaît.
|
||||
|
||||
Les organisateurices peuvent également cliquer sur le score de dé d'une équipe
|
||||
pour lancer le dé à sa place. Cela peut être utile pour débugger ou pour aider
|
||||
une équipe qui a un souci technique. Il est possible de cliquer sur le dé, ce
|
||||
qui a pour effet de lancer le dé pour l'équipe qui n'a pas encore tiré son dé.
|
||||
|
||||
Les poules sont constituées dès que toutes les équipes ont tiré leur dé, selon
|
||||
le principe énoncé dans la partie `Principe`_. S'il y a des égalités, alors les
|
||||
équipes concernées relancent leur dé jusqu'à ne plus avoir d'égalité.
|
||||
|
||||
|
||||
Ordre de tirage des problèmes
|
||||
.............................
|
||||
|
||||
Une fois les poules constituées, il faut déterminer dans quel ordre les équipes
|
||||
tireront leur problème, par le biais d'un nouveau jet de dé. Les équipes sont
|
||||
invitées à lancer un dé à 100 faces. Pour cela, elles peuvent cliquer sur le
|
||||
gros bouton avec l'émoji « dé » 🎲 sur la droite de l'écran. Le résultat est
|
||||
affiché dans la section « Derniers jets de dés ».
|
||||
|
||||
.. figure:: _static/img/draw_waiting_choose_problem_order_full.png
|
||||
:alt: Étape de tirage de l'ordre de tirage des problèmes
|
||||
|
||||
Le tirage au sort est en attente du tirage l'ordre de tirage pour la poule A1.
|
||||
L'équipe **BBB** a déjà tiré son dé, avec pour score 56. Les équipes **CCC**
|
||||
et **DDD** sont alors attendues.
|
||||
|
||||
Une fois le bouton cliqué, il disparaît.
|
||||
|
||||
À nouveau, les organisateurices peuvent cliquer sur le score de dé d'une équipe
|
||||
pour lancer le dé à sa place. Cela peut être utile pour débugger ou pour aider
|
||||
une équipe qui a un souci technique. Il est possible de cliquer sur le dé, ce
|
||||
qui a pour effet de lancer le dé pour l'équipe qui n'a pas encore tiré son dé.
|
||||
|
||||
Si deux équipes réalisent le même score, alors les équipes concernées relancent
|
||||
leur dé jusqu'à ne plus avoir d'égalité.
|
||||
|
||||
|
||||
Tirage des problèmes
|
||||
....................
|
||||
|
||||
Les équipes membre de la poule active sont invitées à tirer leur problème, dans
|
||||
l'ordre déterminé par les dés précédents.
|
||||
|
||||
L'équipe active est invitée à cliquer sur l'urne 🗳️ sur la droite de l'écran pour
|
||||
tirer un problème au sort.
|
||||
|
||||
.. figure:: _static/img/draw_waiting_problem_draw_full.png
|
||||
:alt: Étape de tirage des problèmes
|
||||
|
||||
L'équipe **DDD** est la première à tirer son problème, puisqu'elle a réalisé
|
||||
le plus gros score. Elle est mise en surbrillance.
|
||||
|
||||
Les organisateurices peuvent également cliquer sur l'urne pour lancer le dé à la
|
||||
place de l'équipe. Cela peut être utile pour débugger ou pour aider une équipe qui
|
||||
a un souci technique.
|
||||
|
||||
Le problème tiré ne peut pas être le même que celui d'une autre équipe de la poule,
|
||||
ni le même que celui défendu par l'équipe lors du premier tour si on est au second
|
||||
tour. Il peut en revanche être un problème déjà refusé auparavant.
|
||||
|
||||
|
||||
Choix du problème
|
||||
.................
|
||||
|
||||
Une fois le problème tiré, l'équipe peut alors choisir de l'accepter ou de le
|
||||
refuser. Elle est invitée à cliquer sur le bouton « Accepter » ou « Refuser »
|
||||
pour faire son choix.
|
||||
|
||||
.. figure:: _static/img/draw_choose_problem_full.png
|
||||
:alt: Étape de choix du problème
|
||||
|
||||
L'équipe **DDD** a tiré le problème 3. Elle peut choisir de l'accepter ou de le refuser.
|
||||
|
||||
Si elle accepte le problème, alors elle a terminé, et la main passe à l'équipe
|
||||
suivante. Ce sera ce problème qu'elle défendra.
|
||||
|
||||
Si elle refuse le problème, alors :
|
||||
|
||||
* Soit le problème n'avait pas encore été refusé. Dans ce cas, la main passe à
|
||||
l'équipe suivante, selon l'ordre de tirage des problèmes. Si le nombre de refus
|
||||
dépasse ``P - 3`` où ``P`` est le nombre de problèmes, alors l'équipe se verra
|
||||
dotée d'une pénalité de 25 % sur son coefficient de l'oral de défense. Ces
|
||||
pénalités sont cumulables.
|
||||
* Soit le problème avait déjà été refusé. Dans ce cas, l'équipe peut revenir sur
|
||||
sa décision et accepter le problème, ou bien le rejeter gratuitement et tirer
|
||||
immédiatement un nouveau problème. Cela ne compte pas comme un refus
|
||||
supplémentaire et ne peut ajouter de pénalité.
|
||||
|
||||
Les organisateurices peuvent à nouveau cliquer sur les boutons à la place de
|
||||
l'équipe. Cela peut être utile pour débugger ou pour aider une équipe qui a un
|
||||
souci technique.
|
||||
|
||||
.. figure:: _static/img/draw_example.png
|
||||
:alt: Exemple de tirage de la poule A1
|
||||
|
||||
Dans cet exemple, l'équipe **DDD** a tiré le problème 3, et l'a accepté.
|
||||
L'équipe **BBB** a d'abord refusé le problème 6, et a accepté plus tard
|
||||
le problème 5. L'équipe **CCC**, à qui c'est le tour, a refusé les problèmes
|
||||
8, 4, 2 et 6. Puisqu'il y a 8 problèmes, elle a une pénalité de 25 % sur son
|
||||
coefficient de l'oral de défense. Elle vient de tirer le problème 2, qu'elle
|
||||
avait déjà refusé. Elle est donc libre de le refuser à nouveau sans pénalité
|
||||
supplémentaire, ou bien de l'accepter, comme indiqué dans l'encadré à droite.
|
||||
|
||||
Lorsque toutes les équipes de la poule ont accepté leur problème, on passe à la
|
||||
poule suivante, en revenant à l'étape de tirage au sort de l'ordre des problèmes.
|
||||
Lorsqu'un tour est terminé, on passe au tour suivant, à la première poule.
|
||||
|
||||
Exception : pour la finale, on ne tire au sort que le premier tour.
|
||||
|
||||
.. figure:: _static/img/draw_end_round_1.png
|
||||
:alt: Fin du tirage au sort du premier tour
|
||||
|
||||
Toutes les équipes ont tiré leur problème pour le premier tour. On passe
|
||||
au second tour, à la poule A2.
|
||||
|
||||
|
||||
Fin du tirage au sort
|
||||
.....................
|
||||
|
||||
Le tirage se termine lorsque toutes les équipes se sont vues attribuer un problème
|
||||
pour chacun des tours.
|
||||
|
||||
Les organisateurices peuvent alors cliquer sur le bouton « Exporter » pour valider
|
||||
le tirage et transmettre les données au reste de la plateforme, débloquant notamment
|
||||
l'accès aux différentes solutions pour les équipes. Attention : cette opération n'est
|
||||
pas réversible facilement.
|
||||
|
||||
Spécificité de la finale
|
||||
........................
|
||||
|
||||
Pour la finale, le tirage au sort est légèrement différent. Seul le premier tour
|
||||
est tiré au sort initialement. Pour le second tour, les poules et ordres de passage
|
||||
sont déterminés selon le classement du premier tour.
|
||||
|
||||
Avant de reprendre le tirage au sort du second tour, il est essentiel que les notes
|
||||
du premier tour soient rentrées correctement. En effet, ce sont ces scores qui
|
||||
détermineront les poules et l'ordre de passage pour le second tour.
|
||||
|
||||
Pour lancer le second tour, un⋅e organisateurice doit cliquer sur le bouton
|
||||
« Continuer ». Le tirage reprend ensuite normalement, à partir de la poule **A2**.
|
||||
|
||||
.. danger::
|
||||
À terme, il sera possible de réaliser ce second tirage au sort IRL, et de rentrer
|
||||
facilement les données au fur et à mesure du tirage.
|
||||
|
||||
|
||||
Annulation d'une étape
|
||||
......................
|
||||
|
||||
Il est possible d'annuler la dernière étape du tirage au sort. Cela peut être utile
|
||||
en cas de souci technique ou de mauvaise manipulation. Cette option est uniquement
|
||||
réservée aux organisateurices.
|
||||
|
||||
Pour cela, il suffit de cliquer sur le bouton « Annuler la dernière étape » dans
|
||||
la partie « Récapitulatif ». Cela annule immédiatement la dernière action. Il est
|
||||
possible de continuer à remonter le temps ainsi.
|
||||
|
||||
En cas de plus gros problème, il est possible de cliquer sur le bouton « Annuler »
|
||||
en bas de page. Cela réinitialise le tirage au sort, et permet de recommencer
|
||||
depuis le début. Cette suppression est irréversible, soyez sûr⋅es de ce que vous
|
||||
faites avant de cliquer dessus.
|
@ -12,7 +12,6 @@ administrateur⋅rice.
|
||||
|
||||
user
|
||||
orga
|
||||
draw
|
||||
|
||||
|
||||
.. toctree::
|
||||
@ -20,4 +19,3 @@ administrateur⋅rice.
|
||||
:caption: Développer
|
||||
|
||||
dev/index
|
||||
dev/install
|
||||
|
@ -32,17 +32,16 @@ Les informations suivantes sont requises pour tout le monde :
|
||||
* Genre
|
||||
* Adresse postale
|
||||
* Numéro de téléphone de contact
|
||||
* Problèmes de santé à déclarer (allergies,…)
|
||||
* Contraintes de logement à déclarer (problèmes médicaux, contraintes horaires, questions de genre,…)
|
||||
* Problèmes de santé à déclarer
|
||||
* Donne son consentement pour se faire recontacter par Animath
|
||||
|
||||
Informations demandées exclusivement aux élèves :
|
||||
|
||||
* Classe (seconde ou avant/première/terminale)
|
||||
* Établissement scolaire
|
||||
* Nom d'un⋅e responsable légal⋅e
|
||||
* Numéro de téléphone d'un⋅e responsable légal⋅e
|
||||
* Adresse e-mail d'un⋅e responsable légal⋅e
|
||||
* Nom du responsable légal
|
||||
* Numéro de téléphone du responsable légal
|
||||
* Adresse e-mail du responsable légal
|
||||
|
||||
Informations exclusivement demandées aux encadrant⋅es :
|
||||
|
||||
@ -137,8 +136,8 @@ Pour valider votre inscription, vous devez :
|
||||
* Transmettre une lettre de motivation.
|
||||
|
||||
La lettre de motivation doit être envoyée une seule fois pour toute l'équipe, peut être envoyée
|
||||
depuis l'interface « Mon équipe », au format PDF, dont le contenu est défini dans le
|
||||
règlement : https://tfjm.org/reglement/.
|
||||
depuis l'interface « Mon équipe », au format PDF, dont le contenu est défini dans l'article 5.3
|
||||
du guide de læ participant⋅e : https://tfjm.org/reglement/.
|
||||
|
||||
Concernant les documents personnels, ils peuvent être envoyés depuis le menu « Mon compte », qui
|
||||
peut être trouvé en haut à droite dans la barre de navigation. Chaque fichier doit être envoyé
|
||||
@ -202,7 +201,9 @@ Envoyer ses solutions
|
||||
Participer au tirage au sort
|
||||
----------------------------
|
||||
|
||||
La documentation des tirages au sort est disponible sur `la page dédiée <draw.html>`_.
|
||||
.. TODO
|
||||
.. note::
|
||||
Cette section sera mise à jour plus tard.
|
||||
|
||||
|
||||
Envoyer ses notes de synthèse
|
||||
|
@ -899,7 +899,7 @@ class DrawConsumer(AsyncJsonWebsocketConsumer):
|
||||
if already_refused:
|
||||
msg += "Cela n'ajoute pas de pénalité."
|
||||
else:
|
||||
msg += "Cela ajoute une pénalité de 0.5 sur le coefficient de l'oral de la défense."
|
||||
msg += "Cela ajoute une pénalité de 0.5 sur le coefficient de l'oral de læ défenseur⋅se."
|
||||
self.tournament.draw.last_message = msg
|
||||
await self.tournament.draw.asave()
|
||||
|
||||
|
@ -145,7 +145,7 @@ class Draw(models.Model):
|
||||
# The problem can be rejected
|
||||
s += "Elle peut décider d'accepter ou de refuser ce problème. "
|
||||
if len(td.rejected) >= len(settings.PROBLEMS) - 5:
|
||||
s += "Refuser ce problème ajoutera une nouvelle pénalité de 0.5 sur le coefficient de l'oral de la défense."
|
||||
s += "Refuser ce problème ajoutera une nouvelle pénalité de 0.5 sur le coefficient de l'oral de læ défenseur⋅se."
|
||||
else:
|
||||
s += f"Il reste {len(settings.PROBLEMS) - 5 - len(td.rejected)} refus sans pénalité."
|
||||
case 'WAITING_FINAL':
|
||||
|
@ -14,7 +14,7 @@ from django.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.text import format_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from registration.models import VolunteerRegistration, Payment
|
||||
from registration.models import VolunteerRegistration
|
||||
from tfjm.lists import get_sympa_client
|
||||
|
||||
|
||||
@ -66,140 +66,6 @@ class Team(models.Model):
|
||||
def coaches(self):
|
||||
return self.participants.filter(coachregistration__isnull=False)
|
||||
|
||||
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():
|
||||
return False
|
||||
if not self.participation.tournament:
|
||||
return False
|
||||
if any(not r.photo_authorization for r in self.participants.all()):
|
||||
return False
|
||||
if not self.motivation_letter:
|
||||
return False
|
||||
if not self.participation.tournament.remote:
|
||||
if any(r.under_18 and not r.health_sheet for r in self.students.all()):
|
||||
return False
|
||||
if any(r.under_18 and not r.vaccine_sheet for r in self.students.all()):
|
||||
return False
|
||||
if any(r.under_18 and not r.parental_authorization for r in self.students.all()):
|
||||
return False
|
||||
return True
|
||||
|
||||
def important_informations(self):
|
||||
informations = []
|
||||
|
||||
if self.participation.valid is None:
|
||||
if not self.participation.tournament:
|
||||
text = _("The team {trigram} is not registered to any tournament. "
|
||||
"You can register the team to a tournament using <a href='{url}'>this link</a>.")
|
||||
url = reverse_lazy("participation:update_team", args=(self.pk,))
|
||||
content = format_lazy(text, trigram=self.trigram, url=url)
|
||||
informations.append({
|
||||
'title': _("No tournament"),
|
||||
'type': "danger",
|
||||
'priority': 4,
|
||||
'content': content,
|
||||
})
|
||||
else:
|
||||
text = _("Registrations for the tournament of {tournament} are ending on the {date:%Y-%m-%d %H:%M}.")
|
||||
content = format_lazy(text,
|
||||
tournament=self.participation.tournament.name,
|
||||
date=self.participation.tournament.inscription_limit)
|
||||
informations.append({
|
||||
'title': _("Registrations closure"),
|
||||
'type': "info",
|
||||
'priority': 1,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
if not self.motivation_letter:
|
||||
text = _("The team {trigram} has not uploaded a motivation letter. "
|
||||
"You can upload your motivation letter using <a href='{url}'>this link</a>.")
|
||||
url = reverse_lazy("participation:upload_team_motivation_letter", args=(self.pk,))
|
||||
content = format_lazy(text, trigram=self.trigram, url=url)
|
||||
informations.append({
|
||||
'title': _("No motivation letter"),
|
||||
'type': "danger",
|
||||
'priority': 10,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
nb_students = self.students.count()
|
||||
nb_coaches = self.coaches.count()
|
||||
if nb_students < 4:
|
||||
text = _("The team {trigram} has less than 4 students ({nb_students}). "
|
||||
"You can invite more students to join the team using "
|
||||
"the invite code <strong>{code}</strong>.")
|
||||
content = format_lazy(text, trigram=self.trigram, nb_students=nb_students, code=self.access_code)
|
||||
informations.append({
|
||||
'title': _("Not enough students"),
|
||||
'type': "warning",
|
||||
'priority': 7,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
if not nb_coaches:
|
||||
text = _("The team {trigram} has no coach. "
|
||||
"You can invite a coach to join the team using the invite code <strong>{code}</strong>.")
|
||||
content = format_lazy(text, trigram=self.trigram, nb_students=nb_students, code=self.access_code)
|
||||
informations.append({
|
||||
'title': _("No coach"),
|
||||
'type': "warning",
|
||||
'priority': 8,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
if nb_students > 6 or nb_coaches > 2:
|
||||
text = _("The team {trigram} has more than 6 students ({nb_students}) "
|
||||
"or more than 2 coaches ({nb_coaches})."
|
||||
"You have to restrict the number of students and coaches to 6 and 2, respectively.")
|
||||
content = format_lazy(text, trigram=self.trigram, nb_students=nb_students, nb_coaches=nb_coaches)
|
||||
informations.append({
|
||||
'title': _("Too many members"),
|
||||
'type': "warning",
|
||||
'priority': 7,
|
||||
'content': content,
|
||||
})
|
||||
elif nb_students >= 4 and nb_coaches >= 1:
|
||||
if self.can_validate():
|
||||
text = _("The team {trigram} is ready to be validated. "
|
||||
"You can request validation on <a href='{url}'>the page of your team</a>.")
|
||||
url = reverse_lazy("participation:team_detail", args=(self.pk,))
|
||||
content = format_lazy(text, trigram=self.trigram, url=url)
|
||||
informations.append({
|
||||
'title': _("Validate team"),
|
||||
'type': "success",
|
||||
'priority': 2,
|
||||
'content': content,
|
||||
})
|
||||
else:
|
||||
text = _("The team {trigram} has enough participants, but is not ready to be validated. "
|
||||
"Please make sure that all the participants have uploaded the required documents. "
|
||||
"To invite more participants, use the invite code <strong>{code}</strong>.")
|
||||
content = format_lazy(text, trigram=self.trigram, code=self.access_code)
|
||||
informations.append({
|
||||
'title': _("Validate team"),
|
||||
'type': "warning",
|
||||
'priority': 10,
|
||||
'content': content,
|
||||
})
|
||||
elif self.participation.valid is False:
|
||||
text = _("The team {trigram} has not been validated by the organizers yet. Please be patient.")
|
||||
content = format_lazy(text, trigram=self.trigram)
|
||||
informations.append({
|
||||
'title': _("Pending validation"),
|
||||
'type': "warning",
|
||||
'priority': 2,
|
||||
'content': content,
|
||||
})
|
||||
else:
|
||||
informations.extend(self.participation.important_informations())
|
||||
|
||||
return informations
|
||||
|
||||
@property
|
||||
def email(self):
|
||||
"""
|
||||
@ -462,42 +328,6 @@ class Participation(models.Model):
|
||||
def __str__(self):
|
||||
return _("Participation of the team {name} ({trigram})").format(name=self.team.name, trigram=self.team.trigram)
|
||||
|
||||
def important_informations(self):
|
||||
informations = []
|
||||
|
||||
missing_payments = Payment.objects.filter(registration__in=self.team.participants.all(), valid=False)
|
||||
if missing_payments.exists():
|
||||
text = _("<p>The team {trigram} has {nb_missing_payments} missing payments. Each member of the team "
|
||||
"must have a valid payment (or send a scholarship notification) "
|
||||
"to participate to the tournament.</p>"
|
||||
"<p>Participants that have not paid yet are: {participants}.</p>")
|
||||
content = format_lazy(text, trigram=self.team.trigram, nb_missing_payments=missing_payments.count(),
|
||||
participants=", ".join(str(p.registration) for p in missing_payments.all()))
|
||||
informations.append({
|
||||
'title': _("Missing payments"),
|
||||
'type': "danger",
|
||||
'priority': 10,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
if timezone.now() <= self.tournament.solution_limit:
|
||||
text = _("<p>The solutions for the tournament of {tournament} are due on the {date:%Y-%m-%d %H:%M}.</p>"
|
||||
"<p>You have currently sent <strong>{nb_solutions}</strong> solutions. "
|
||||
"We suggest to send at least <strong>{min_solutions}</strong> different solutions.</p>"
|
||||
"<p>You can upload your solutions on <a href='{url}'>your participation page</a>.</p>")
|
||||
url = reverse_lazy("participation:participation_detail", args=(self.pk,))
|
||||
content = format_lazy(text, tournament=self.tournament.name, date=self.tournament.solution_limit,
|
||||
nb_solutions=self.solutions.count(), min_solutions=len(settings.PROBLEMS) - 3,
|
||||
url=url)
|
||||
informations.append({
|
||||
'title': _("Solutions due"),
|
||||
'type': "info",
|
||||
'priority': 1,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
return informations
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("participation")
|
||||
verbose_name_plural = _("participations")
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
{% load django_tables2 i18n %}
|
||||
|
||||
{% block content-title %}
|
||||
{% block contenttitle %}
|
||||
<h1>{% trans "All teams" %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -81,7 +81,7 @@ Tour {{ pool.round }} \;-- Poule {{ pool.get_letter_display }}{{ page }} \;-- {%
|
||||
|
||||
\vspace{15mm}
|
||||
|
||||
\LARGE Nom jur\'e\textperiodcentered{}e :
|
||||
\LARGE Nom de læ jur\'e\textperiodcentered{}e :
|
||||
{% if is_jury %}\underline{ {{ user.first_name|safe }} {{ user.last_name|safe }} }{% else %}\underline{\phantom{Phrase suffisamment longue pour le nom}}{% endif %}
|
||||
$\qquad$ Signature : \underline{\phantom{Phrase moins longue}}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
{% load django_tables2 i18n %}
|
||||
|
||||
{% block content-title %}
|
||||
{% block contenttitle %}
|
||||
<h1>{% trans "All tournaments" %}</h1>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -438,7 +438,6 @@ class TestStudentParticipation(TestCase):
|
||||
self.user.registration.save()
|
||||
|
||||
# Team is valid
|
||||
self.team.participation.tournament = self.tournament
|
||||
self.team.participation.valid = True
|
||||
self.team.participation.save()
|
||||
response = self.client.post(reverse("participation:team_leave"))
|
||||
@ -480,7 +479,6 @@ class TestStudentParticipation(TestCase):
|
||||
reverse("participation:participation_detail", args=(self.team.participation.pk,)),
|
||||
302, 403)
|
||||
|
||||
self.team.participation.tournament = self.tournament
|
||||
self.team.participation.valid = True
|
||||
self.team.participation.save()
|
||||
response = self.client.get(reverse("participation:my_participation_detail"))
|
||||
|
@ -90,7 +90,7 @@ class JoinTeamView(LoginRequiredMixin, FormView):
|
||||
model = Team
|
||||
form_class = JoinTeamForm
|
||||
extra_context = dict(title=_("Join team"))
|
||||
template_name = "participation/join_team.html"
|
||||
template_name = "participation/create_team.html"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
user = request.user
|
||||
@ -180,7 +180,15 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
|
||||
context["validation_form"] = ValidateParticipationForm(self.request.POST or None)
|
||||
# A team is complete when there are at least 4 members plus a coache that have sent their authorizations,
|
||||
# their health sheet, they confirmed their email address and under-18 people sent their parental authorization.
|
||||
context["can_validate"] = team.can_validate()
|
||||
# TODO: Add vaccine sheets
|
||||
context["can_validate"] = team.students.count() >= 4 and team.coaches.exists() and \
|
||||
team.participation.tournament and \
|
||||
all(r.photo_authorization for r in team.participants.all()) and \
|
||||
(team.participation.tournament.remote
|
||||
or all(r.health_sheet for r in team.students.all() if r.under_18)) and \
|
||||
(team.participation.tournament.remote
|
||||
or all(r.parental_authorization for r in team.students.all() if r.under_18)) and \
|
||||
team.motivation_letter
|
||||
|
||||
return context
|
||||
|
||||
|
@ -228,7 +228,6 @@ class PaymentForm(forms.ModelForm):
|
||||
self.fields["valid"].widget.choices[0] = ('unknown', _("Pending"))
|
||||
|
||||
def clean_scholarship_file(self):
|
||||
print(self.files)
|
||||
if "scholarship_file" in self.files:
|
||||
file = self.files["scholarship_file"]
|
||||
if file.size > 2e6:
|
||||
@ -241,7 +240,7 @@ class PaymentForm(forms.ModelForm):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
if "type" in cleaned_data and cleaned_data["type"] == "scholarship" \
|
||||
and "scholarship_file" not in self.files and not self.instance.scholarship_file:
|
||||
and "scholarship_file" not in cleaned_data and not self.instance.scholarship_file:
|
||||
self.add_error("scholarship_file", _("You must upload your scholarship attestation."))
|
||||
|
||||
return cleaned_data
|
||||
|
@ -12,7 +12,6 @@ from django.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
from django.utils.text import format_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
from polymorphic.models import PolymorphicModel
|
||||
@ -89,28 +88,6 @@ class Registration(PolymorphicModel):
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy("registration:user_detail", args=(self.user_id,))
|
||||
|
||||
def registration_informations(self):
|
||||
return []
|
||||
|
||||
def important_informations(self):
|
||||
informations = []
|
||||
if not self.email_confirmed:
|
||||
text = _("Your email address is not validated. Please click on the link you received by email. "
|
||||
"You can resend a mail by clicking on <a href=\"{send_email_url}\">this link</a>.")
|
||||
send_email_url = reverse_lazy("registration:email_validation_resend", args=(self.user_id,))
|
||||
content = format_lazy(text, send_email_url=send_email_url)
|
||||
informations.append({
|
||||
'title': "Validation e-mail",
|
||||
'type': "warning",
|
||||
'priority': 0,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
informations.extend(self.registration_informations())
|
||||
|
||||
informations.sort(key=lambda info: (info['priority'], info['title']))
|
||||
return informations
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user.first_name} {self.user.last_name}"
|
||||
|
||||
@ -219,38 +196,6 @@ class ParticipantRegistration(Registration):
|
||||
def form_class(self): # pragma: no cover
|
||||
raise NotImplementedError
|
||||
|
||||
def registration_informations(self):
|
||||
informations = []
|
||||
if not self.team:
|
||||
text = _("You are not in a team. You can <a href=\"{create_url}\">create one</a> "
|
||||
"or <a href=\"{join_url}\">join an existing one</a> to participate.")
|
||||
create_url = reverse_lazy("participation:create_team")
|
||||
join_url = reverse_lazy("participation:join_team")
|
||||
content = format_lazy(text, create_url=create_url, join_url=join_url)
|
||||
informations.append({
|
||||
'title': _("No team"),
|
||||
'type': "danger",
|
||||
'priority': 1,
|
||||
'content': content,
|
||||
})
|
||||
else:
|
||||
if self.team.participation.tournament:
|
||||
if not self.photo_authorization:
|
||||
text = _("You have not uploaded your photo authorization. "
|
||||
"You can do it by clicking on <a href=\"{photo_url}\">this link</a>.")
|
||||
photo_url = reverse_lazy("registration:upload_user_photo_authorization", args=(self.id,))
|
||||
content = format_lazy(text, photo_url=photo_url)
|
||||
informations.append({
|
||||
'title': _("Photo authorization"),
|
||||
'type': "danger",
|
||||
'priority': 5,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
informations.extend(self.team.important_informations())
|
||||
|
||||
return informations
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("participant registration")
|
||||
verbose_name_plural = _("participant registrations")
|
||||
@ -326,68 +271,6 @@ class StudentRegistration(ParticipantRegistration):
|
||||
from registration.forms import StudentRegistrationForm
|
||||
return StudentRegistrationForm
|
||||
|
||||
def registration_informations(self):
|
||||
informations = super().registration_informations()
|
||||
if self.team and self.team.participation.tournament and self.under_18:
|
||||
if not self.parental_authorization:
|
||||
text = _("You have not uploaded your parental authorization. "
|
||||
"You can do it by clicking on <a href=\"{parental_url}\">this link</a>.")
|
||||
parental_url = reverse_lazy("registration:upload_user_parental_authorization", args=(self.id,))
|
||||
content = format_lazy(text, parental_url=parental_url)
|
||||
informations.append({
|
||||
'title': _("Parental authorization"),
|
||||
'type': "danger",
|
||||
'priority': 5,
|
||||
'content': content,
|
||||
})
|
||||
if not self.health_sheet:
|
||||
text = _("You have not uploaded your health sheet. "
|
||||
"You can do it by clicking on <a href=\"{health_url}\">this link</a>.")
|
||||
health_url = reverse_lazy("registration:upload_user_health_sheet", args=(self.id,))
|
||||
content = format_lazy(text, health_url=health_url)
|
||||
informations.append({
|
||||
'title': _("Health sheet"),
|
||||
'type': "danger",
|
||||
'priority': 5,
|
||||
'content': content,
|
||||
})
|
||||
if not self.vaccine_sheet:
|
||||
text = _("You have not uploaded your vaccine sheet. "
|
||||
"You can do it by clicking on <a href=\"{vaccine_url}\">this link</a>.")
|
||||
vaccine_url = reverse_lazy("registration:upload_user_vaccine_sheet", args=(self.id,))
|
||||
content = format_lazy(text, vaccine_url=vaccine_url)
|
||||
informations.append({
|
||||
'title': _("Vaccine sheet"),
|
||||
'type': "danger",
|
||||
'priority': 5,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
if self.team and self.team.participation.valid:
|
||||
if self.payment.valid is False:
|
||||
text = _("You have to pay {amount} € for your registration, or send a scholarship "
|
||||
"notification or a payment proof. "
|
||||
"You can do it on <a href=\"{url}\">the payment page</a>.")
|
||||
url = reverse_lazy("registration:update_payment", args=(self.payment.id,))
|
||||
content = format_lazy(text, amount=self.team.participation.tournament.price, url=url)
|
||||
informations.append({
|
||||
'title': _("Payment"),
|
||||
'type': "danger",
|
||||
'priority': 3,
|
||||
'content': content,
|
||||
})
|
||||
elif self.payment.valid is None:
|
||||
text = _("Your payment is under approval.")
|
||||
content = text
|
||||
informations.append({
|
||||
'title': _("Payment"),
|
||||
'type': "warning",
|
||||
'priority': 3,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
return informations
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("student registration")
|
||||
verbose_name_plural = _("student registrations")
|
||||
@ -451,41 +334,6 @@ class VolunteerRegistration(Registration):
|
||||
from registration.forms import VolunteerRegistrationForm
|
||||
return VolunteerRegistrationForm
|
||||
|
||||
def important_informations(self):
|
||||
informations = []
|
||||
|
||||
for tournament in self.organized_tournaments.all():
|
||||
if timezone.now() < tournament.inscription_limit \
|
||||
or tournament.participations.filter(valid=True).count() < tournament.max_teams:
|
||||
text = _("Registrations for tournament {tournament} are closing on {date:%Y-%m-%d %H:%M}. "
|
||||
"There are for now {validated_teams} validated teams (+ {pending_teams} pending) "
|
||||
"on {max_teams} expected.")
|
||||
content = format_lazy(text, tournament=tournament.name, date=tournament.inscription_limit,
|
||||
validated_teams=tournament.participations.filter(valid=True).count(),
|
||||
pending_teams=tournament.participations.filter(valid=False).count(),
|
||||
max_teams=tournament.max_teams)
|
||||
informations.append({
|
||||
'title': _("Registrations"),
|
||||
'type': "info",
|
||||
'priority': 2,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
for pending_participation in tournament.participations.filter(valid=False).all():
|
||||
text = _("The team {trigram} requested to be validated for the tournament of {tournament}. "
|
||||
"You can check the status of the team on the <a href=\"{url}\">team page</a>.")
|
||||
url = reverse_lazy("participation:team_detail", args=(pending_participation.team.id,))
|
||||
content = format_lazy(text, trigram=pending_participation.team.trigram,
|
||||
tournament=tournament.name, url=url)
|
||||
informations.append({
|
||||
'title': _("Pending validation"),
|
||||
'type': "warning",
|
||||
'priority': 4,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
return informations
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("volunteer registration")
|
||||
verbose_name_plural = _("volunteer registrations")
|
||||
|
@ -3,8 +3,50 @@
|
||||
{% load crispy_forms_filters i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="alert alert-warning">
|
||||
Le formulaire de paiement est temporairement désactivé. Il sera accessible d'ici quelques jours.
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div id="form-content">
|
||||
<div class="alert alert-info text-justify">
|
||||
<p>
|
||||
{% blocktrans trimmed with price=payment.registration.team.participation.tournament.price %}
|
||||
The price of the tournament is {{ price }} €. The participation fee is offered for coaches
|
||||
and for students who have a scholarship. If so, please send us your scholarship attestation.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can pay with a credit card through
|
||||
<a class="alert-link" href="https://www.helloasso.com/associations/animath/evenements/tfjm-2023-tournois-regionaux">our Hello Asso page</a>.
|
||||
To make the validation of the payment easier, <span class="text-danger">please use the same e-mail
|
||||
address that you use on this platform.</span> The payment verification will be checked automatically
|
||||
under 10 minutes, you don't necessary need to fill this form.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can also send a bank transfer to the bank account of Animath. You must put in the reference of the
|
||||
transfer the mention "TFJMpu" followed by the last name and the first name of the student.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
IBAN : FR76 1027 8065 0000 0206 4290 127<br>
|
||||
BIC : CMCIFR2A
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
If any payment mean is available to you, please contact us at <a class="alert-link" href="mailto:contact@tfjm.org">contact@tfjm.org</a>
|
||||
to find a solution to your difficulties.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">{% trans "Update" %}</button>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
|
||||
|
@ -263,7 +263,6 @@ class UserUpdateView(UserMixin, UpdateView):
|
||||
Update the detail about a user and its registration.
|
||||
"""
|
||||
model = User
|
||||
context_object_name = "user_object"
|
||||
form_class = UserForm
|
||||
template_name = "registration/update_user.html"
|
||||
|
||||
@ -456,17 +455,13 @@ class PaymentUpdateView(LoginRequiredMixin, UpdateView):
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
if not self.request.user.registration.is_admin:
|
||||
form.fields["type"].widget.choices = list(form.fields["type"].widget.choices)[:-1]
|
||||
del form.fields["type"].widget.choices[-1]
|
||||
del form.fields["valid"]
|
||||
return form
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.registration.is_admin:
|
||||
form.instance.valid = None
|
||||
old_instance = Payment.objects.get(pk=self.object.pk)
|
||||
if old_instance.scholarship_file:
|
||||
old_instance.scholarship_file.delete()
|
||||
old_instance.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content-title %}
|
||||
{% block contenttitle %}
|
||||
<h1>À propos</h1>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
{% load i18n static %}
|
||||
{% load static i18n static %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
|
||||
<html lang="{{ LANGUAGE_CODE|default:"fr" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %} class="position-relative h-100">
|
||||
<html lang="{{ LANGUAGE_CODE|default:"en" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %} class="position-relative h-100">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>
|
||||
{% block title %}{{ title }}{% endblock title %} - Plateforme du TFJM²
|
||||
</title>
|
||||
<meta name="description" content="Plateforme d'inscription au TFJM².">
|
||||
<meta name="description" content="Plateform d'inscription au TFJM².">
|
||||
|
||||
{# Favicon #}
|
||||
<link rel="shortcut icon" href="{% static "favicon.ico" %}">
|
||||
@ -25,7 +25,7 @@
|
||||
{# Bootstrap JavaScript #}
|
||||
<script src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
|
||||
|
||||
{# bootstrap-select for beautiful selects and JQuery dependency #}
|
||||
{# bootstrap-select for beautyful selects and JQuery dependency #}
|
||||
<script src="{% static 'jquery/jquery.min.js' %}"></script>
|
||||
<script src="{% static 'bootstrap-select/js/bootstrap-select.min.js' %}"></script>
|
||||
<script src="{% static 'bootstrap-select/js/defaults-fr_FR.min.js' %}"></script>
|
||||
@ -38,31 +38,206 @@
|
||||
{% block extracss %}{% endblock %}
|
||||
</head>
|
||||
<body class="d-flex w-100 h-100 flex-column">
|
||||
{% include "navbar.html" %}
|
||||
|
||||
<div id="body-wrapper" class="row w-100 my-3">
|
||||
<aside class="col-lg-2 px-2">
|
||||
{% include "sidebar.html" %}
|
||||
</aside>
|
||||
|
||||
<main class="col d-flex flex-column">
|
||||
<div class="container">
|
||||
{% block content-title %}<h1 id="content-title">{{ title }}</h1>{% endblock %}
|
||||
|
||||
{% include "messages.html" %}
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-navbar shadow-sm">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="https://tfjm.org/">
|
||||
<img src="{% static "tfjm.svg" %}" style="height: 2em;" alt="Logo TFJM²" id="navbar-logo">
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNavDropdown"
|
||||
aria-controls="navbarNavDropdown" aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div id="navbarNavDropdown" class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "index" %}" class="nav-link"><i class="fas fa-home"></i> {% trans "Home" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#tournamentListModal">
|
||||
<i class="fas fa-calendar-day"></i> {% trans "Tournaments" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if user.is_authenticated and user.registration.is_volunteer %}
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "registration:user_list" %}" class="nav-link"><i class="fas fa-user"></i> {% trans "Users" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#teamsModal"><i class="fas fa-users"></i> {% trans "Teams" %}</a>
|
||||
</li>
|
||||
{% elif user.is_authenticated and user.registration.participates %}
|
||||
{% if not user.registration.team %}
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#createTeamModal">
|
||||
<i class="fas fa-users"></i> {% trans "Create team" %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#joinTeamModal">
|
||||
<i class="fas fa-users"></i> {% trans "Join team" %}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "participation:my_team_detail" %}" class="nav-link">
|
||||
<i class="fas fa-users"></i> {% trans "My team" %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "participation:my_participation_detail" %}" class="nav-link">
|
||||
<i class="fas fa-file-pdf"></i> {% trans "My participation" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
{% if user.registration.is_volunteer or user.registration.team %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url 'draw:index' %}">
|
||||
<i class="fas fa-archive"></i> {% trans "Draw" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li class="nav-item active d-none">
|
||||
<a class="nav-link" href="{% url "participation:chat" %}">
|
||||
<i class="fas fa-comments"></i> {% trans "Chat" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if user.registration.is_admin %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "admin:index" %}"><i class="fas fa-cog"></i> {% trans "Administration" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<ul class="navbar-nav ms-auto">
|
||||
{% if user.registration.is_admin %}
|
||||
<form class="navbar-form d-flex" role="search" onsubmit="event.preventDefault()">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="{% trans "Search…" %}" name="q" id="search-term" value="{{ request.GET.q }}">
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-default" data-bs-toggle="modal" data-bs-target="#searchModal"><i class="fa fa-search text-body"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if "_fake_user_id" in request.session %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "registration:reset_admin" %}?path={{ request.path }}"><i class="fas fa-tools"></i> {% trans "Return to admin view" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if not user.is_authenticated %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "registration:signup" %}"><i class="fas fa-user-plus"></i> {% trans "Register" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#" data-bs-toggle="modal" data-bs-target="#loginModal">
|
||||
<i class="fas fa-sign-in-alt"></i> {% trans "Log in" %}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink"
|
||||
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-user"></i> {{ user.first_name }} {{ user.last_name }}
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownMenuLink">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url "registration:my_account_detail" %}">
|
||||
<i class="fas fa-user"></i> {% trans "My account" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url "logout" %}">
|
||||
<i class="fas fa-sign-out-alt"></i> {% trans "Log out" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="mb-auto flex-shrink-0">
|
||||
{% block fullcontent %}
|
||||
<div class="{% block containertype %}container{% endblock %} my-3">
|
||||
{% block contenttitle %}<h1>{{ title }}</h1>{% endblock %}
|
||||
{% if user.is_authenticated and not user.registration.email_confirmed %}
|
||||
<div class="alert alert-warning alert-dismissible" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
{% url "registration:email_validation_resend" pk=user.pk as send_email_url %}
|
||||
{% blocktrans trimmed %}
|
||||
Your email address is not validated. Please click on the link you received by email.
|
||||
You can resend a mail by clicking on <a href="{{ send_email_url }}">this link</a>.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="messages">
|
||||
{% 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>
|
||||
{{ message | safe }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div id="content">
|
||||
{% block content %}
|
||||
<p>Default content...</p>
|
||||
{% endblock content %}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
<aside class="col-lg-2"></aside>
|
||||
</div>
|
||||
<footer class="text-primary mt-auto py-2">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-1">
|
||||
<span class="text-muted mr-1">
|
||||
<a target="_blank" href="mailto:contact@tfjm.org"
|
||||
class="text-muted"><i class="fas fa-envelope"></i> {% trans "Contact us" %}</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<form action="{% url 'set_language' %}" method="post"
|
||||
class="form-inline">
|
||||
{% csrf_token %}
|
||||
<select title="language" name="language"
|
||||
class="form-control form-control-sm language"
|
||||
onchange="this.form.submit()">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% for lang_code, lang_name in LANGUAGES %}
|
||||
<option value="{{ lang_code }}"
|
||||
{% if lang_code == LANGUAGE_CODE %}
|
||||
selected{% endif %}>
|
||||
{{ lang_name }} ({{ lang_code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<noscript>
|
||||
<input type="submit">
|
||||
</noscript>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
|
||||
{% include "footer.html" %}
|
||||
<a target="_blank" class="text-muted" href="{% url "about" %}">{% trans "About" %}</a> —
|
||||
<a target="_blank" class="text-muted"
|
||||
href="https://gitlab.com/animath/si/plateforme-tfjm">
|
||||
<i class="fab fa-gitlab"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-1 text-end">
|
||||
<a href="#" class="text-muted">
|
||||
<i class="fa fa-arrow-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
{% trans "All tournaments" as modal_title %}
|
||||
{% include "base_modal.html" with modal_id="tournamentList" modal_additional_class="modal-lg" %}
|
||||
|
@ -1,51 +0,0 @@
|
||||
{% load i18n static %}
|
||||
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
|
||||
|
||||
<footer class="text-primary mt-lg-auto">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-lg-1">
|
||||
<span class="text-muted mr-1">
|
||||
<a target="_blank" href="mailto:contact@tfjm.org"
|
||||
class="text-muted"><i class="fas fa-envelope"></i> {% trans "Contact us" %}</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-2 col-lg-1">
|
||||
<form action="{% url 'set_language' %}" method="post"
|
||||
class="form-inline">
|
||||
{% csrf_token %}
|
||||
<select title="language" name="language"
|
||||
class="form-control form-control-sm language"
|
||||
onchange="this.form.submit()">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% for lang_code, lang_name in LANGUAGES %}
|
||||
<option value="{{ lang_code }}"
|
||||
{% if lang_code == LANGUAGE_CODE %}
|
||||
selected{% endif %}>
|
||||
{{ lang_name }} ({{ lang_code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<noscript>
|
||||
<input type="submit">
|
||||
</noscript>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-sm-7 col-lg-9">
|
||||
|
||||
<a target="_blank" class="text-muted" href="{% url "about" %}">{% trans "About" %}</a> —
|
||||
<a target="_blank" class="text-muted" href="/doc/">Documentation</a> —
|
||||
<a target="_blank" class="text-muted"
|
||||
href="https://gitlab.com/animath/si/plateforme-tfjm">
|
||||
<i class="fab fa-gitlab"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-1 text-end">
|
||||
<a href="#" class="text-muted">
|
||||
<i class="fa fa-arrow-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
@ -1,6 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
|
||||
<div class="alert alert-success">
|
||||
<p>
|
||||
Les inscriptions pour la session 2024 sont à présent ouvertes, vous pouvez créer votre compte.
|
||||
Prenez garde toutefois aux dates indiquées qui sont pour l'instant provisoires.
|
||||
</p>
|
||||
<p>
|
||||
Une documentation plus complète est disponible à l'adresse
|
||||
<a href="https://inscription.tfjm.org/doc/">https://inscription.tfjm.org/doc/</a>
|
||||
et sera progressivement actualisée.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
Diverses améliorations fonctionnelles pourront être apportées au site au cours des prochaines semaines.
|
||||
</div>
|
||||
|
||||
<div class="jumbotron p-5">
|
||||
<div class="row text-center">
|
||||
<h1 class="display-4">
|
||||
@ -51,13 +69,8 @@
|
||||
<h2>J'ai une question</h2>
|
||||
|
||||
<p class="text-justify">
|
||||
N'hésitez pas à consulter la <a href="/doc/" target="_blank">documentation</a> du site, pour vérifier si
|
||||
la réponse ne s'y trouve pas déjà. Référez-vous également bien sûr au
|
||||
<a href="https://tfjm.org/reglement/" target="_blank">règlement du 𝕋𝔽𝕁𝕄²</a>.
|
||||
Pour toute autre question, n'hésitez pas à nous contacter par mail à l'adresse
|
||||
<a href="mailto:contact@tfjm.org">
|
||||
contact@tfjm.org
|
||||
</a>.
|
||||
Pour toute question, vous pouvez soit la poser dans <code>#faq</code> dans l'onglet chat comme indiqué ci-dessus, soit nous
|
||||
contacter par mail à l'adresse <a href="mailto:contact@tfjm.org">contact@tfjm.org</a>.
|
||||
</p>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
@ -65,4 +78,6 @@
|
||||
ne pourrez malheureusement pas participer au 𝕋𝔽𝕁𝕄².
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,8 +0,0 @@
|
||||
<div id="messages">
|
||||
{% 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>
|
||||
{{ message | safe }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
@ -1,124 +0,0 @@
|
||||
{% load i18n static %}
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-navbar shadow-sm">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="https://tfjm.org/">
|
||||
<img src="{% static "tfjm.svg" %}" style="height: 2em;" alt="Logo TFJM²" id="navbar-logo">
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNavDropdown"
|
||||
aria-controls="navbarNavDropdown" aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div id="navbarNavDropdown" class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "index" %}" class="nav-link"><i class="fas fa-home"></i> {% trans "Home" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#tournamentListModal">
|
||||
<i class="fas fa-calendar-day"></i> {% trans "Tournaments" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if user.is_authenticated and user.registration.is_volunteer %}
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "registration:user_list" %}" class="nav-link"><i class="fas fa-user"></i> {% trans "Users" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#teamsModal"><i class="fas fa-users"></i> {% trans "Teams" %}</a>
|
||||
</li>
|
||||
{% elif user.is_authenticated and user.registration.participates %}
|
||||
{% if not user.registration.team %}
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#createTeamModal">
|
||||
<i class="fas fa-users"></i> {% trans "Create team" %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#joinTeamModal">
|
||||
<i class="fas fa-users"></i> {% trans "Join team" %}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "participation:my_team_detail" %}" class="nav-link">
|
||||
<i class="fas fa-users"></i> {% trans "My team" %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "participation:my_participation_detail" %}" class="nav-link">
|
||||
<i class="fas fa-file-pdf"></i> {% trans "My participation" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
{% if user.registration.is_volunteer or user.registration.team %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url 'draw:index' %}">
|
||||
<i class="fas fa-archive"></i> {% trans "Draw" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li class="nav-item active d-none">
|
||||
<a class="nav-link" href="{% url "participation:chat" %}">
|
||||
<i class="fas fa-comments"></i> {% trans "Chat" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if user.registration.is_admin %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "admin:index" %}"><i class="fas fa-cog"></i> {% trans "Administration" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<ul class="navbar-nav ms-auto">
|
||||
{% if user.registration.is_admin %}
|
||||
<form class="navbar-form d-flex" role="search" onsubmit="event.preventDefault()">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="{% trans "Search…" %}" name="q" id="search-term" value="{{ request.GET.q }}">
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-default" data-bs-toggle="modal" data-bs-target="#searchModal"><i class="fa fa-search text-body"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if "_fake_user_id" in request.session %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "registration:reset_admin" %}?path={{ request.path }}"><i class="fas fa-tools"></i> {% trans "Return to admin view" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if not user.is_authenticated %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "registration:signup" %}"><i class="fas fa-user-plus"></i> {% trans "Register" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#" data-bs-toggle="modal" data-bs-target="#loginModal">
|
||||
<i class="fas fa-sign-in-alt"></i> {% trans "Log in" %}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink"
|
||||
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-user"></i> {{ user.first_name }} {{ user.last_name }}
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownMenuLink">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url "registration:my_account_detail" %}">
|
||||
<i class="fas fa-user"></i> {% trans "My account" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url "logout" %}">
|
||||
<i class="fas fa-sign-out-alt"></i> {% trans "Log out" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
{% load i18n crispy_forms_filters %}
|
||||
|
||||
{% block title %}{% trans "Log in" %}{% endblock %}
|
||||
{% block content-title %}<h1>{% trans "Log in" %}</h1>{% endblock %}
|
||||
{% block contenttitle %}<h1>{% trans "Log in" %}</h1>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if user.is_authenticated %}
|
||||
|
@ -1,40 +0,0 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark-subtle">
|
||||
<div class="d-lg-none btn" data-bs-toggle="collapse"
|
||||
data-bs-target="#sidebar-card" aria-controls="sidebar-card" aria-expanded="false"
|
||||
aria-label="Toggle information sidebar">
|
||||
<h3 class="card-title">
|
||||
{% trans "Informations" %}
|
||||
<span class="d-lg-none">
|
||||
<span class="badge text-small bg-danger">
|
||||
<i class="fa fa-warning"></i>
|
||||
{{ user.registration.important_informations|length }}
|
||||
</span>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="d-none d-lg-block">
|
||||
<h3 class="card-title">{% trans "Informations" %}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="sidebar-card" class="collapse d-lg-block">
|
||||
<div class="card-body">
|
||||
{% for information in user.registration.important_informations %}
|
||||
<div class="card my-2">
|
||||
<div class="card-header bg-dark-subtle">
|
||||
<h5 class="card-title">{{ information.title }}</h5>
|
||||
</div>
|
||||
<div class="card-body bg-{{ information.type }}-subtle">
|
||||
{{ information.content|safe }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|