From 3d9bd88a4199ee5cb38e506bdd112d29988d55e8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 27 Dec 2020 11:14:35 +0100 Subject: [PATCH] Reset project --- .dockerignore | 1 - .gitignore | 4 +- Dockerfile | 31 - README.md | 65 - apps/api/__init__.py | 1 - apps/api/apps.py | 10 - apps/api/serializers.py | 80 -- apps/api/urls.py | 26 - apps/api/viewsets.py | 124 -- apps/member/__init__.py | 1 - apps/member/admin.py | 56 - apps/member/apps.py | 10 - apps/member/forms.py | 83 -- apps/member/management/__init__.py | 0 apps/member/management/commands/__init__.py | 0 apps/member/management/commands/create_su.py | 32 - .../management/commands/extract_solutions.py | 75 - .../management/commands/import_olddb.py | 309 ----- apps/member/migrations/0001_initial.py | 133 -- .../migrations/0002_auto_20200919_2015.py | 57 - .../0003_tfjmuser_email_confirmed.py | 18 - apps/member/migrations/__init__.py | 0 apps/member/models.py | 399 ------ apps/member/tables.py | 26 - apps/member/templatetags/__init__.py | 0 apps/member/templatetags/getconfig.py | 25 - apps/member/tokens.py | 26 - apps/member/urls.py | 24 - apps/member/views.py | 374 ----- apps/tournament/__init__.py | 1 - apps/tournament/admin.py | 31 - apps/tournament/apps.py | 10 - apps/tournament/forms.py | 262 ---- apps/tournament/migrations/0001_initial.py | 92 -- apps/tournament/migrations/__init__.py | 0 apps/tournament/models.py | 432 ------ apps/tournament/tables.py | 164 --- apps/tournament/urls.py | 24 - apps/tournament/views.py | 662 --------- entrypoint.sh | 12 - index.html | 12 - locale/fr/LC_MESSAGES/django.po | 1221 ----------------- manage.py | 21 - nginx_tfjm.conf | 19 - requirements.txt | 16 - templates/400.html | 8 - templates/403.html | 13 - templates/404.html | 13 - templates/500.html | 8 - templates/amount_input.html | 11 - templates/autocomplete_model.html | 9 - templates/base.html | 227 --- templates/index.html | 9 - templates/mail_templates/add_organizer.html | 20 - templates/mail_templates/add_organizer.txt | 12 - .../add_organizer_for_tournament.html | 18 - templates/mail_templates/add_team.html | 16 - .../mail_templates/change_email_address.html | 16 - templates/mail_templates/change_password.html | 18 - templates/mail_templates/confirm_email.html | 18 - .../mail_templates/forgotten_password.html | 20 - templates/mail_templates/join_team.html | 17 - templates/mail_templates/register.html | 16 - .../request_payment_validation.html | 26 - .../mail_templates/request_validation.html | 19 - .../mail_templates/request_validation.txt | 9 - .../mail_templates/select_for_final.html | 21 - templates/mail_templates/select_for_final.txt | 12 - .../mail_templates/unvalidate_payment.html | 24 - templates/mail_templates/unvalidate_team.html | 26 - templates/mail_templates/unvalidate_team.txt | 15 - .../mail_templates/validate_payment.html | 24 - templates/mail_templates/validate_team.html | 25 - templates/mail_templates/validate_team.txt | 13 - templates/member/my_account.html | 15 - templates/member/profile_list.html | 11 - templates/member/tfjmuser_detail.html | 87 -- .../email_validation_complete.html | 27 - .../email_validation_email_sent.html | 18 - templates/registration/logged_out.html | 10 - templates/registration/login.html | 25 - .../mails/email_validation_email.html | 36 - .../mails/email_validation_email.txt | 13 - .../registration/password_change_done.html | 9 - .../registration/password_change_form.html | 13 - .../registration/password_reset_complete.html | 12 - .../registration/password_reset_confirm.html | 17 - .../registration/password_reset_done.html | 10 - .../registration/password_reset_form.html | 13 - templates/registration/signup.html | 17 - templates/tournament/add_organizer.html | 11 - templates/tournament/pool_detail.html | 94 -- templates/tournament/pool_form.html | 11 - templates/tournament/pool_list.html | 12 - templates/tournament/solutions_list.html | 29 - templates/tournament/solutions_orga_list.html | 18 - templates/tournament/syntheses_list.html | 45 - templates/tournament/syntheses_orga_list.html | 18 - templates/tournament/team_detail.html | 156 --- templates/tournament/team_form.html | 11 - templates/tournament/tournament_detail.html | 67 - templates/tournament/tournament_form.html | 11 - templates/tournament/tournament_list.html | 17 - tfjm.cron | 5 - tfjm/__init__.py | 0 tfjm/asgi.py | 16 - tfjm/inputs.py | 322 ----- tfjm/middlewares.py | 118 -- tfjm/settings.py | 210 --- tfjm/settings_dev.py | 5 - tfjm/settings_prod.py | 30 - .../Autorisation_droit_image_majeur.tex | 113 -- .../Autorisation_droit_image_mineur.tex | 122 -- tfjm/static/Autorisation_parentale.tex | 66 - tfjm/static/Fiche synthèse.pdf | Bin 71958 -> 0 bytes tfjm/static/Fiche synthèse.tex | 194 --- tfjm/static/Fiche_sanitaire.pdf | Bin 54296 -> 0 bytes tfjm/static/Instructions.tex | 88 -- tfjm/static/favicon.ico | Bin 1514 -> 0 bytes tfjm/static/logo.svg | 114 -- tfjm/static/logo_animath.png | Bin 106600 -> 0 bytes tfjm/static/style.css | 47 - tfjm/urls.py | 54 - tfjm/wsgi.py | 16 - 124 files changed, 1 insertion(+), 8004 deletions(-) delete mode 100644 Dockerfile delete mode 100644 README.md delete mode 100644 apps/api/__init__.py delete mode 100644 apps/api/apps.py delete mode 100644 apps/api/serializers.py delete mode 100644 apps/api/urls.py delete mode 100644 apps/api/viewsets.py delete mode 100644 apps/member/__init__.py delete mode 100644 apps/member/admin.py delete mode 100644 apps/member/apps.py delete mode 100644 apps/member/forms.py delete mode 100644 apps/member/management/__init__.py delete mode 100644 apps/member/management/commands/__init__.py delete mode 100644 apps/member/management/commands/create_su.py delete mode 100644 apps/member/management/commands/extract_solutions.py delete mode 100644 apps/member/management/commands/import_olddb.py delete mode 100644 apps/member/migrations/0001_initial.py delete mode 100644 apps/member/migrations/0002_auto_20200919_2015.py delete mode 100644 apps/member/migrations/0003_tfjmuser_email_confirmed.py delete mode 100644 apps/member/migrations/__init__.py delete mode 100644 apps/member/models.py delete mode 100644 apps/member/tables.py delete mode 100644 apps/member/templatetags/__init__.py delete mode 100644 apps/member/templatetags/getconfig.py delete mode 100644 apps/member/tokens.py delete mode 100644 apps/member/urls.py delete mode 100644 apps/member/views.py delete mode 100644 apps/tournament/__init__.py delete mode 100644 apps/tournament/admin.py delete mode 100644 apps/tournament/apps.py delete mode 100644 apps/tournament/forms.py delete mode 100644 apps/tournament/migrations/0001_initial.py delete mode 100644 apps/tournament/migrations/__init__.py delete mode 100644 apps/tournament/models.py delete mode 100644 apps/tournament/tables.py delete mode 100644 apps/tournament/urls.py delete mode 100644 apps/tournament/views.py delete mode 100755 entrypoint.sh delete mode 100644 index.html delete mode 100644 locale/fr/LC_MESSAGES/django.po delete mode 100755 manage.py delete mode 100644 nginx_tfjm.conf delete mode 100644 requirements.txt delete mode 100644 templates/400.html delete mode 100644 templates/403.html delete mode 100644 templates/404.html delete mode 100644 templates/500.html delete mode 100644 templates/amount_input.html delete mode 100644 templates/autocomplete_model.html delete mode 100644 templates/base.html delete mode 100644 templates/index.html delete mode 100644 templates/mail_templates/add_organizer.html delete mode 100644 templates/mail_templates/add_organizer.txt delete mode 100644 templates/mail_templates/add_organizer_for_tournament.html delete mode 100644 templates/mail_templates/add_team.html delete mode 100644 templates/mail_templates/change_email_address.html delete mode 100644 templates/mail_templates/change_password.html delete mode 100644 templates/mail_templates/confirm_email.html delete mode 100644 templates/mail_templates/forgotten_password.html delete mode 100644 templates/mail_templates/join_team.html delete mode 100644 templates/mail_templates/register.html delete mode 100644 templates/mail_templates/request_payment_validation.html delete mode 100644 templates/mail_templates/request_validation.html delete mode 100644 templates/mail_templates/request_validation.txt delete mode 100644 templates/mail_templates/select_for_final.html delete mode 100644 templates/mail_templates/select_for_final.txt delete mode 100644 templates/mail_templates/unvalidate_payment.html delete mode 100644 templates/mail_templates/unvalidate_team.html delete mode 100644 templates/mail_templates/unvalidate_team.txt delete mode 100644 templates/mail_templates/validate_payment.html delete mode 100644 templates/mail_templates/validate_team.html delete mode 100644 templates/mail_templates/validate_team.txt delete mode 100644 templates/member/my_account.html delete mode 100644 templates/member/profile_list.html delete mode 100644 templates/member/tfjmuser_detail.html delete mode 100644 templates/registration/email_validation_complete.html delete mode 100644 templates/registration/email_validation_email_sent.html delete mode 100644 templates/registration/logged_out.html delete mode 100644 templates/registration/login.html delete mode 100644 templates/registration/mails/email_validation_email.html delete mode 100644 templates/registration/mails/email_validation_email.txt delete mode 100644 templates/registration/password_change_done.html delete mode 100644 templates/registration/password_change_form.html delete mode 100644 templates/registration/password_reset_complete.html delete mode 100644 templates/registration/password_reset_confirm.html delete mode 100644 templates/registration/password_reset_done.html delete mode 100644 templates/registration/password_reset_form.html delete mode 100644 templates/registration/signup.html delete mode 100644 templates/tournament/add_organizer.html delete mode 100644 templates/tournament/pool_detail.html delete mode 100644 templates/tournament/pool_form.html delete mode 100644 templates/tournament/pool_list.html delete mode 100644 templates/tournament/solutions_list.html delete mode 100644 templates/tournament/solutions_orga_list.html delete mode 100644 templates/tournament/syntheses_list.html delete mode 100644 templates/tournament/syntheses_orga_list.html delete mode 100644 templates/tournament/team_detail.html delete mode 100644 templates/tournament/team_form.html delete mode 100644 templates/tournament/tournament_detail.html delete mode 100644 templates/tournament/tournament_form.html delete mode 100644 templates/tournament/tournament_list.html delete mode 100644 tfjm.cron delete mode 100644 tfjm/__init__.py delete mode 100644 tfjm/asgi.py delete mode 100644 tfjm/inputs.py delete mode 100644 tfjm/middlewares.py delete mode 100644 tfjm/settings.py delete mode 100644 tfjm/settings_dev.py delete mode 100644 tfjm/settings_prod.py delete mode 100644 tfjm/static/Autorisation_droit_image_majeur.tex delete mode 100644 tfjm/static/Autorisation_droit_image_mineur.tex delete mode 100644 tfjm/static/Autorisation_parentale.tex delete mode 100644 tfjm/static/Fiche synthèse.pdf delete mode 100644 tfjm/static/Fiche synthèse.tex delete mode 100644 tfjm/static/Fiche_sanitaire.pdf delete mode 100644 tfjm/static/Instructions.tex delete mode 100644 tfjm/static/favicon.ico delete mode 100644 tfjm/static/logo.svg delete mode 100644 tfjm/static/logo_animath.png delete mode 100644 tfjm/static/style.css delete mode 100644 tfjm/urls.py delete mode 100644 tfjm/wsgi.py diff --git a/.dockerignore b/.dockerignore index 12143a1..efc2616 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,3 @@ __pycache__ media -import_olddb db.sqlite3 diff --git a/.gitignore b/.gitignore index 173574d..29e51da 100644 --- a/.gitignore +++ b/.gitignore @@ -38,10 +38,8 @@ coverage secrets.py *.log media/ + # Virtualenv env/ venv/ db.sqlite3 - -# Don't git personal data -import_olddb/ diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index f4e487b..0000000 --- a/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM python:3-alpine - -ENV PYTHONUNBUFFERED 1 - -# Install LaTeX requirements -RUN apk add --no-cache gettext texlive nginx gcc libc-dev libffi-dev postgresql-dev mariadb-connector-c-dev - -RUN apk add --no-cache bash - -RUN mkdir /code -WORKDIR /code -COPY requirements.txt /code/requirements.txt -RUN pip install -r requirements.txt --no-cache-dir - -COPY . /code/ - -# Configure nginx -RUN mkdir /run/nginx -RUN ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log -RUN ln -sf /code/nginx_tfjm.conf /etc/nginx/conf.d/tfjm.conf -RUN rm /etc/nginx/conf.d/default.conf - -RUN cp /code/tfjm.cron /etc/crontabs/tfjm - -# With a bashrc, the shell is better -RUN ln -s /code/.bashrc /root/.bashrc - -ENTRYPOINT ["/code/entrypoint.sh"] -EXPOSE 80 - -CMD ["./manage.py", "shell_plus", "--ptpython"] diff --git a/README.md b/README.md deleted file mode 100644 index a9459c3..0000000 --- a/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Plateforme d'inscription du TFJM² - -La plateforme du TFJM² est née pour l'édition 2020 du tournoi. D'abord codée en PHP, elle a subi une refonte totale en -Python, à l'aide du framework Web [Django](https://www.djangoproject.com/). - -Cette plateforme permet aux participants et encadrants de s'inscrire et de déposer leurs autorisations nécessaires. -Ils pourront ensuite déposer leurs solutions et notes de synthèse pour le premier tour en temps voulu. La plateforme -offre également un accès pour les organisateurs et les jurys leur permettant de communiquer avec les équipes et de -récupérer les documents nécessaires. - -Un wiki plus détaillé arrivera ultérieurement. L'interface organisateur et jury est vouée à être plus poussée. - -L'instance de production est disponible à l'adresse [inscription.tfjm.org](https://inscription.tfjm.org). - -## Installation - -Le plus simple pour installer la plateforme est d'utiliser l'image Docker incluse, qui fait tourner un serveur Nginx -exposé sur le port 80 avec le serveur Django. Ci-dessous une configuration Docker-Compose, à adapter selon vos besoins : - -```yaml - inscription-tfjm: - build: ./inscription-tfjm - links: - - postgres - ports: - - "80:80" - env_file: - - ./inscription-tfjm.env - volumes: - # - ./inscription-tfjm:/code - - ./inscription-tfjm/media:/code/media -``` - -Le volume `/code` n'est à ajouter uniquement en développement, et jamais en production. - -Il faut remplir les variables d'environnement suivantes : - -```env -TFJM_STAGE= # dev ou prod -TFJM_YEAR=2021 # Année de la session du TFJM² -DJANGO_DB_TYPE= # MySQL, PostgreSQL ou SQLite (par défaut) -DJANGO_DB_HOST= # Hôte de la base de données -DJANGO_DB_NAME= # Nom de la base de données -DJANGO_DB_USER= # Utilisateur de la base de données -DJANGO_DB_PASSWORD= # Mot de passe pour accéder à la base de données -SMTP_HOST= # Hôte SMTP pour l'envoi de mails -SMTP_PORT=465 # Port du serveur SMTP -SMTP_HOST_USER= # Utilisateur du compte SMTP -SMTP_HOST_PASSWORD= # Mot de passe du compte SMTP -FROM_EMAIL=contact@tfjm.org # Nom de l'expéditeur des mails -SERVER_EMAIL=contact@tfjm.org # Adresse e-mail expéditrice -``` - -Si le type de base de données sélectionné est SQLite, la variable `DJANGO_DB_HOST` sera utilisée en guise de chemin vers -le fichier de base de données (par défaut, `db.sqlite3`). - -En développement, il est recommandé d'utiliser SQLite pour des raisons de simplicité. Les paramètres de mail ne seront -pas utilisés, et les mails qui doivent être envoyés seront envoyés dans la console. - -En production, il est recommandé de ne pas utiliser SQLite pour des raisons de performances. - -La dernière différence entre le développment et la production est qu'en développement, chaque modification d'un fichier -est détectée et le serveur se relance automatiquement dès lors. - -Une fois le site lancé, le premier compte créé sera un compte administrateur. \ No newline at end of file diff --git a/apps/api/__init__.py b/apps/api/__init__.py deleted file mode 100644 index 08884cb..0000000 --- a/apps/api/__init__.py +++ /dev/null @@ -1 +0,0 @@ -default_app_config = 'api.apps.APIConfig' diff --git a/apps/api/apps.py b/apps/api/apps.py deleted file mode 100644 index 6e03468..0000000 --- a/apps/api/apps.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.apps import AppConfig -from django.utils.translation import gettext_lazy as _ - - -class APIConfig(AppConfig): - """ - Manage the inscription through a JSON API. - """ - name = 'api' - verbose_name = _('API') diff --git a/apps/api/serializers.py b/apps/api/serializers.py deleted file mode 100644 index 1685020..0000000 --- a/apps/api/serializers.py +++ /dev/null @@ -1,80 +0,0 @@ -from rest_framework import serializers -from member.models import TFJMUser, Authorization, MotivationLetter, Solution, Synthesis -from tournament.models import Team, Tournament, Pool - - -class UserSerializer(serializers.ModelSerializer): - """ - Serialize a User object into JSON. - """ - class Meta: - model = TFJMUser - exclude = ( - 'username', - 'password', - 'groups', - 'user_permissions', - ) - - -class TeamSerializer(serializers.ModelSerializer): - """ - Serialize a Team object into JSON. - """ - class Meta: - model = Team - fields = "__all__" - - -class TournamentSerializer(serializers.ModelSerializer): - """ - Serialize a Tournament object into JSON. - """ - class Meta: - model = Tournament - fields = "__all__" - - -class AuthorizationSerializer(serializers.ModelSerializer): - """ - Serialize an Authorization object into JSON. - """ - class Meta: - model = Authorization - fields = "__all__" - - -class MotivationLetterSerializer(serializers.ModelSerializer): - """ - Serialize a MotivationLetter object into JSON. - """ - class Meta: - model = MotivationLetter - fields = "__all__" - - -class SolutionSerializer(serializers.ModelSerializer): - """ - Serialize a Solution object into JSON. - """ - class Meta: - model = Solution - fields = "__all__" - - -class SynthesisSerializer(serializers.ModelSerializer): - """ - Serialize a Synthesis object into JSON. - """ - class Meta: - model = Synthesis - fields = "__all__" - - -class PoolSerializer(serializers.ModelSerializer): - """ - Serialize a Pool object into JSON. - """ - class Meta: - model = Pool - fields = "__all__" diff --git a/apps/api/urls.py b/apps/api/urls.py deleted file mode 100644 index b2e617f..0000000 --- a/apps/api/urls.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.conf.urls import url, include -from rest_framework import routers - -from .viewsets import UserViewSet, TeamViewSet, TournamentViewSet, AuthorizationViewSet, MotivationLetterViewSet, \ - SolutionViewSet, SynthesisViewSet, PoolViewSet - -# Routers provide an easy way of automatically determining the URL conf. -# Register each app API router and user viewset -router = routers.DefaultRouter() -router.register('user', UserViewSet) -router.register('team', TeamViewSet) -router.register('tournament', TournamentViewSet) -router.register('authorization', AuthorizationViewSet) -router.register('motivation_letter', MotivationLetterViewSet) -router.register('solution', SolutionViewSet) -router.register('synthesis', SynthesisViewSet) -router.register('pool', PoolViewSet) - -app_name = 'api' - -# Wire up our API using automatic URL routing. -# Additionally, we include login URLs for the browsable API. -urlpatterns = [ - url('^', include(router.urls)), - url('^api-auth/', include('rest_framework.urls', namespace='rest_framework')), -] diff --git a/apps/api/viewsets.py b/apps/api/viewsets.py deleted file mode 100644 index 785e446..0000000 --- a/apps/api/viewsets.py +++ /dev/null @@ -1,124 +0,0 @@ -from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import status -from rest_framework.filters import SearchFilter -from rest_framework.response import Response -from rest_framework.viewsets import ModelViewSet -from member.models import TFJMUser, Authorization, MotivationLetter, Solution, Synthesis -from tournament.models import Team, Tournament, Pool - -from .serializers import UserSerializer, TeamSerializer, TournamentSerializer, AuthorizationSerializer, \ - MotivationLetterSerializer, SolutionSerializer, SynthesisSerializer, PoolSerializer - - -class UserViewSet(ModelViewSet): - """ - Display list of users. - """ - queryset = TFJMUser.objects.all() - serializer_class = UserSerializer - filter_backends = [DjangoFilterBackend, SearchFilter] - filterset_fields = ['id', 'first_name', 'last_name', 'email', 'gender', 'student_class', 'role', 'year', 'team', - 'team__trigram', 'is_superuser', 'is_staff', 'is_active', ] - search_fields = ['$first_name', '$last_name', ] - - -class TeamViewSet(ModelViewSet): - """ - Display list of teams. - """ - queryset = Team.objects.all() - serializer_class = TeamSerializer - filter_backends = [DjangoFilterBackend, SearchFilter] - filterset_fields = ['name', 'trigram', 'validation_status', 'selected_for_final', 'access_code', 'tournament', - 'year', ] - search_fields = ['$name', 'trigram', ] - - -class TournamentViewSet(ModelViewSet): - """ - Display list of tournaments. - """ - queryset = Tournament.objects.all() - serializer_class = TournamentSerializer - filter_backends = [DjangoFilterBackend, SearchFilter] - filterset_fields = ['name', 'size', 'price', 'date_start', 'date_end', 'final', 'organizers', 'year', ] - search_fields = ['$name', ] - - -class AuthorizationViewSet(ModelViewSet): - """ - Display list of authorizations. - """ - queryset = Authorization.objects.all() - serializer_class = AuthorizationSerializer - filter_backends = [DjangoFilterBackend] - filterset_fields = ['user', 'type', ] - - -class MotivationLetterViewSet(ModelViewSet): - """ - Display list of motivation letters. - """ - queryset = MotivationLetter.objects.all() - serializer_class = MotivationLetterSerializer - filter_backends = [DjangoFilterBackend] - filterset_fields = ['team', 'team__trigram', ] - - -class SolutionViewSet(ModelViewSet): - """ - Display list of solutions. - """ - queryset = Solution.objects.all() - serializer_class = SolutionSerializer - filter_backends = [DjangoFilterBackend] - filterset_fields = ['team', 'team__trigram', 'problem', ] - - -class SynthesisViewSet(ModelViewSet): - """ - Display list of syntheses. - """ - queryset = Synthesis.objects.all() - serializer_class = SynthesisSerializer - filter_backends = [DjangoFilterBackend] - filterset_fields = ['team', 'team__trigram', 'source', 'round', ] - - -class PoolViewSet(ModelViewSet): - """ - Display list of pools. - If the request is a POST request and the format is "A;X;x;Y;y;Z;z;..." where A = 1 or 1 = 2, - X, Y, Z, ... are team trigrams, x, y, z, ... are numbers of problems, then this is interpreted as a - creation a pool for the round A with the solutions of problems x, y, z, ... of the teams X, Y, Z, ... respectively. - """ - queryset = Pool.objects.all() - serializer_class = PoolSerializer - filter_backends = [DjangoFilterBackend] - filterset_fields = ['teams', 'teams__trigram', 'round', ] - - def create(self, request, *args, **kwargs): - data = request.data - try: - spl = data.split(";") - if len(spl) >= 7: - round = int(spl[0]) - teams = [] - solutions = [] - for i in range((len(spl) - 1) // 2): - trigram = spl[1 + 2 * i] - pb = int(spl[2 + 2 * i]) - team = Team.objects.get(trigram=trigram) - solution = Solution.objects.get(team=team, problem=pb, final=team.selected_for_final) - teams.append(team) - solutions.append(solution) - pool = Pool.objects.create(round=round) - pool.teams.set(teams) - pool.solutions.set(solutions) - pool.save() - serializer = PoolSerializer(pool) - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - except BaseException: # JSON data - pass - return super().create(request, *args, **kwargs) \ No newline at end of file diff --git a/apps/member/__init__.py b/apps/member/__init__.py deleted file mode 100644 index 6bb559b..0000000 --- a/apps/member/__init__.py +++ /dev/null @@ -1 +0,0 @@ -default_app_config = 'member.apps.MemberConfig' diff --git a/apps/member/admin.py b/apps/member/admin.py deleted file mode 100644 index a41bb92..0000000 --- a/apps/member/admin.py +++ /dev/null @@ -1,56 +0,0 @@ -from django.contrib.auth.admin import admin -from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin -from member.models import TFJMUser, Document, Solution, Synthesis, MotivationLetter, Authorization, Config - - -@admin.register(TFJMUser) -class TFJMUserAdmin(admin.ModelAdmin): - """ - Django admin page for users. - """ - list_display = ('email', 'first_name', 'last_name', 'role', ) - search_fields = ('last_name', 'first_name',) - - -@admin.register(Document) -class DocumentAdmin(PolymorphicParentModelAdmin): - """ - Django admin page for any documents. - """ - child_models = (Authorization, MotivationLetter, Solution, Synthesis,) - polymorphic_list = True - - -@admin.register(Authorization) -class AuthorizationAdmin(PolymorphicChildModelAdmin): - """ - Django admin page for Authorization. - """ - - -@admin.register(MotivationLetter) -class MotivationLetterAdmin(PolymorphicChildModelAdmin): - """ - Django admin page for Motivation letters. - """ - - -@admin.register(Solution) -class SolutionAdmin(PolymorphicChildModelAdmin): - """ - Django admin page for solutions. - """ - - -@admin.register(Synthesis) -class SynthesisAdmin(PolymorphicChildModelAdmin): - """ - Django admin page for syntheses. - """ - - -@admin.register(Config) -class ConfigAdmin(admin.ModelAdmin): - """ - Django admin page for configurations. - """ diff --git a/apps/member/apps.py b/apps/member/apps.py deleted file mode 100644 index 61c9ae8..0000000 --- a/apps/member/apps.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.apps import AppConfig -from django.utils.translation import gettext_lazy as _ - - -class MemberConfig(AppConfig): - """ - The member app handles the information that concern a user, its documents, ... - """ - name = 'member' - verbose_name = _('member') diff --git a/apps/member/forms.py b/apps/member/forms.py deleted file mode 100644 index 6fb9f6c..0000000 --- a/apps/member/forms.py +++ /dev/null @@ -1,83 +0,0 @@ -from bootstrap_datepicker_plus import DatePickerInput -from django.contrib.auth.forms import UserCreationForm -from django import forms -from django.utils.translation import gettext_lazy as _ - -from .models import TFJMUser - - -class SignUpForm(UserCreationForm): - """ - Coaches and participants register on the website through this form. - TODO: Check if this form works, render it better - """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields["first_name"].required = True - self.fields["last_name"].required = True - self.fields["role"].choices = [ - ('', _("Choose a role...")), - ('3participant', _("Participant")), - ('2coach', _("Coach")), - ] - - class Meta: - model = TFJMUser - fields = ( - 'role', - 'email', - 'first_name', - 'last_name', - 'birth_date', - 'gender', - 'address', - 'postal_code', - 'city', - 'country', - 'phone_number', - 'school', - 'student_class', - 'responsible_name', - 'responsible_phone', - 'responsible_email', - 'description', - ) - widgets = { - "birth_date": DatePickerInput(), - } - - -class TFJMUserForm(forms.ModelForm): - """ - Form to update our own information when we are participant. - """ - class Meta: - model = TFJMUser - fields = ('last_name', 'first_name', 'email', 'phone_number', 'gender', 'birth_date', 'address', 'postal_code', - 'city', 'country', 'school', 'student_class', 'responsible_name', 'responsible_phone', - 'responsible_email',) - widgets = { - "birth_date": DatePickerInput(), - } - - -class CoachUserForm(forms.ModelForm): - """ - Form to update our own information when we are coach. - """ - class Meta: - model = TFJMUser - fields = ('last_name', 'first_name', 'email', 'phone_number', 'gender', 'birth_date', 'address', 'postal_code', - 'city', 'country', 'description',) - widgets = { - "birth_date": DatePickerInput(), - } - - -class AdminUserForm(forms.ModelForm): - """ - Form to update our own information when we are organizer or admin. - """ - class Meta: - model = TFJMUser - fields = ('last_name', 'first_name', 'email', 'phone_number', 'description',) diff --git a/apps/member/management/__init__.py b/apps/member/management/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apps/member/management/commands/__init__.py b/apps/member/management/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apps/member/management/commands/create_su.py b/apps/member/management/commands/create_su.py deleted file mode 100644 index 93ec091..0000000 --- a/apps/member/management/commands/create_su.py +++ /dev/null @@ -1,32 +0,0 @@ -import os -from datetime import date -from getpass import getpass -from django.core.management import BaseCommand -from member.models import TFJMUser - - -class Command(BaseCommand): - def handle(self, *args, **options): - """ - Little script that generate a superuser. - """ - email = input("Email: ") - password = "1" - confirm_password = "2" - while password != confirm_password: - password = getpass("Password: ") - confirm_password = getpass("Confirm password: ") - if password != confirm_password: - self.stderr.write(self.style.ERROR("Passwords don't match.")) - - user = TFJMUser.objects.create( - email=email, - password="", - role="admin", - year=os.getenv("TFJM_YEAR", date.today().year), - is_active=True, - is_staff=True, - is_superuser=True, - ) - user.set_password(password) - user.save() diff --git a/apps/member/management/commands/extract_solutions.py b/apps/member/management/commands/extract_solutions.py deleted file mode 100644 index 7b59ad1..0000000 --- a/apps/member/management/commands/extract_solutions.py +++ /dev/null @@ -1,75 +0,0 @@ -import os -from urllib.request import urlretrieve -from shutil import copyfile - -from django.core.management import BaseCommand -from django.utils import translation -from member.models import Solution -from tournament.models import Tournament - - -class Command(BaseCommand): - PROBLEMS = [ - 'Création de puzzles', - 'Départ en vacances', - 'Un festin stratégique', - 'Sauver les meubles', - 'Prêt à décoller !', - 'Ils nous espionnent !', - 'De joyeux bûcherons', - 'Robots auto-réplicateurs', - ] - - def add_arguments(self, parser): - parser.add_argument('dir', - type=str, - default='.', - help="Directory where solutions should be saved.") - parser.add_argument('--language', '-l', - type=str, - choices=['en', 'fr'], - default='fr', - help="Language of the title of the files.") - - def handle(self, *args, **options): - """ - Copy solutions elsewhere. - """ - d = options['dir'] - teams_dir = d + '/Par équipe' - os.makedirs(teams_dir, exist_ok=True) - - translation.activate(options['language']) - - copied = 0 - - for tournament in Tournament.objects.all(): - os.mkdir(teams_dir + '/' + tournament.name) - for team in tournament.teams.filter(validation_status='2valid'): - os.mkdir(teams_dir + '/' + tournament.name + '/' + str(team)) - for sol in tournament.solutions: - if not os.path.isfile('media/' + sol.file.name): - self.stdout.write(self.style.WARNING(("Warning: solution '{sol}' is not found. Maybe the file" - "was deleted?").format(sol=str(sol)))) - continue - copyfile('media/' + sol.file.name, teams_dir + '/' + tournament.name - + '/' + str(sol.team) + '/' + str(sol) + '.pdf') - copied += 1 - - self.stdout.write(self.style.SUCCESS("Successfully copied {copied} solutions!".format(copied=copied))) - - os.mkdir(d + '/Par problème') - - for pb in range(1, 9): - sols = Solution.objects.filter(problem=pb).all() - pbdir = d + '/Par problème/Problème n°{number} — {problem}'.format(number=pb, problem=self.PROBLEMS[pb - 1]) - os.mkdir(pbdir) - for sol in sols: - os.symlink('../../Par équipe/' + sol.tournament.name + '/' + str(sol.team) + '/' + str(sol) + '.pdf', - pbdir + '/' + str(sol) + '.pdf') - - self.stdout.write(self.style.SUCCESS("Symlinks by problem created!")) - - urlretrieve('https://tfjm.org/wp-content/uploads/2020/01/Problemes2020_23_01_v1_1.pdf', d + '/Énoncés.pdf') - - self.stdout.write(self.style.SUCCESS("Questions retrieved!")) diff --git a/apps/member/management/commands/import_olddb.py b/apps/member/management/commands/import_olddb.py deleted file mode 100644 index d3ff94f..0000000 --- a/apps/member/management/commands/import_olddb.py +++ /dev/null @@ -1,309 +0,0 @@ -import os - -from django.core.management import BaseCommand, CommandError -from django.db import transaction -from member.models import TFJMUser, Document, Solution, Synthesis, Authorization, MotivationLetter -from tournament.models import Team, Tournament - - -class Command(BaseCommand): - """ - Import the old database. - Tables must be found into the import_olddb folder, as CSV files. - """ - - def add_arguments(self, parser): - parser.add_argument('--tournaments', '-t', action="store", help="Import tournaments") - parser.add_argument('--teams', '-T', action="store", help="Import teams") - parser.add_argument('--users', '-u', action="store", help="Import users") - parser.add_argument('--documents', '-d', action="store", help="Import all documents") - - def handle(self, *args, **options): - if "tournaments" in options: - self.import_tournaments() - - if "teams" in options: - self.import_teams() - - if "users" in options: - self.import_users() - - if "documents" in options: - self.import_documents() - - @transaction.atomic - def import_tournaments(self): - """ - Import tournaments into the new database. - """ - print("Importing tournaments...") - with open("import_olddb/tournaments.csv") as f: - first_line = True - for line in f: - if first_line: - first_line = False - continue - - line = line[:-1].replace("\"", "") - args = line.split(";") - args = [arg if arg and arg != "NULL" else None for arg in args] - - if Tournament.objects.filter(pk=args[0]).exists(): - continue - - obj_dict = { - "id": args[0], - "name": args[1], - "size": args[2], - "place": args[3], - "price": args[4], - "description": args[5], - "date_start": args[6], - "date_end": args[7], - "date_inscription": args[8], - "date_solutions": args[9], - "date_syntheses": args[10], - "date_solutions_2": args[11], - "date_syntheses_2": args[12], - "final": args[13], - "year": args[14], - } - with transaction.atomic(): - Tournament.objects.create(**obj_dict) - print(self.style.SUCCESS("Tournaments imported")) - - @staticmethod - def validation_status(status): - if status == "NOT_READY": - return "0invalid" - elif status == "WAITING": - return "1waiting" - elif status == "VALIDATED": - return "2valid" - else: - raise CommandError("Unknown status: {}".format(status)) - - @transaction.atomic - def import_teams(self): - """ - Import teams into new database. - """ - self.stdout.write("Importing teams...") - with open("import_olddb/teams.csv") as f: - first_line = True - for line in f: - if first_line: - first_line = False - continue - - line = line[:-1].replace("\"", "") - args = line.split(";") - args = [arg if arg and arg != "NULL" else None for arg in args] - - if Team.objects.filter(pk=args[0]).exists(): - continue - - obj_dict = { - "id": args[0], - "name": args[1], - "trigram": args[2], - "tournament": Tournament.objects.get(pk=args[3]), - "inscription_date": args[13], - "validation_status": Command.validation_status(args[14]), - "selected_for_final": args[15], - "access_code": args[16], - "year": args[17], - } - with transaction.atomic(): - Team.objects.create(**obj_dict) - print(self.style.SUCCESS("Teams imported")) - - @staticmethod - def role(role): - if role == "ADMIN": - return "0admin" - elif role == "ORGANIZER": - return "1volunteer" - elif role == "ENCADRANT": - return "2coach" - elif role == "PARTICIPANT": - return "3participant" - else: - raise CommandError("Unknown role: {}".format(role)) - - @transaction.atomic - def import_users(self): - """ - Import users into the new database. - :return: - """ - self.stdout.write("Importing users...") - with open("import_olddb/users.csv") as f: - first_line = True - for line in f: - if first_line: - first_line = False - continue - - line = line[:-1].replace("\"", "") - args = line.split(";") - args = [arg if arg and arg != "NULL" else None for arg in args] - - if TFJMUser.objects.filter(pk=args[0]).exists(): - continue - - obj_dict = { - "id": args[0], - "email": args[1], - "username": args[1], - "password": "bcrypt$" + args[2], - "last_name": args[3], - "first_name": args[4], - "birth_date": args[5], - "gender": "male" if args[6] == "M" else "female", - "address": args[7], - "postal_code": args[8], - "city": args[9], - "country": args[10], - "phone_number": args[11], - "school": args[12], - "student_class": args[13].lower().replace('premiere', 'première') if args[13] else None, - "responsible_name": args[14], - "responsible_phone": args[15], - "responsible_email": args[16], - "description": args[17].replace("\\n", "\n") if args[17] else None, - "role": Command.role(args[18]), - "team": Team.objects.get(pk=args[19]) if args[19] else None, - "year": args[20], - "date_joined": args[23], - "is_active": args[18] == "ADMIN" or os.getenv("TFJM_STAGE", "dev") == "prod", - "is_staff": args[18] == "ADMIN", - "is_superuser": args[18] == "ADMIN", - } - with transaction.atomic(): - TFJMUser.objects.create(**obj_dict) - self.stdout.write(self.style.SUCCESS("Users imported")) - - self.stdout.write("Importing organizers...") - # We also import the information about the organizers of a tournament. - with open("import_olddb/organizers.csv") as f: - first_line = True - for line in f: - if first_line: - first_line = False - continue - - line = line[:-1].replace("\"", "") - args = line.split(";") - args = [arg if arg and arg != "NULL" else None for arg in args] - - with transaction.atomic(): - tournament = Tournament.objects.get(pk=args[2]) - organizer = TFJMUser.objects.get(pk=args[1]) - tournament.organizers.add(organizer) - tournament.save() - self.stdout.write(self.style.SUCCESS("Organizers imported")) - - @transaction.atomic - def import_documents(self): - """ - Import the documents (authorizations, motivation letters, solutions, syntheses) from the old database. - """ - self.stdout.write("Importing documents...") - with open("import_olddb/documents.csv") as f: - first_line = True - for line in f: - if first_line: - first_line = False - continue - - line = line[:-1].replace("\"", "") - args = line.split(";") - args = [arg if arg and arg != "NULL" else None for arg in args] - - if Document.objects.filter(file=args[0]).exists(): - doc = Document.objects.get(file=args[0]) - doc.uploaded_at = args[5].replace(" ", "T") - doc.save() - continue - - obj_dict = { - "file": args[0], - "uploaded_at": args[5], - } - if args[4] != "MOTIVATION_LETTER": - obj_dict["user"] = TFJMUser.objects.get(args[1]), - obj_dict["type"] = args[4].lower() - else: - try: - obj_dict["team"] = Team.objects.get(pk=args[2]) - except Team.DoesNotExist: - print("Team with pk {} does not exist, ignoring".format(args[2])) - continue - with transaction.atomic(): - if args[4] != "MOTIVATION_LETTER": - Authorization.objects.create(**obj_dict) - else: - MotivationLetter.objects.create(**obj_dict) - self.stdout.write(self.style.SUCCESS("Authorizations imported")) - - with open("import_olddb/solutions.csv") as f: - first_line = True - for line in f: - if first_line: - first_line = False - continue - - line = line[:-1].replace("\"", "") - args = line.split(";") - args = [arg if arg and arg != "NULL" else None for arg in args] - - if Document.objects.filter(file=args[0]).exists(): - doc = Document.objects.get(file=args[0]) - doc.uploaded_at = args[4].replace(" ", "T") - doc.save() - continue - - obj_dict = { - "file": args[0], - "team": Team.objects.get(pk=args[1]), - "problem": args[3], - "uploaded_at": args[4], - } - with transaction.atomic(): - try: - Solution.objects.create(**obj_dict) - except: - print("Solution exists") - self.stdout.write(self.style.SUCCESS("Solutions imported")) - - with open("import_olddb/syntheses.csv") as f: - first_line = True - for line in f: - if first_line: - first_line = False - continue - - line = line[:-1].replace("\"", "") - args = line.split(";") - args = [arg if arg and arg != "NULL" else None for arg in args] - - if Document.objects.filter(file=args[0]).exists(): - doc = Document.objects.get(file=args[0]) - doc.uploaded_at = args[5].replace(" ", "T") - doc.save() - continue - - obj_dict = { - "file": args[0], - "team": Team.objects.get(pk=args[1]), - "source": "opponent" if args[3] == "1" else "rapporteur", - "round": args[4], - "uploaded_at": args[5], - } - with transaction.atomic(): - try: - Synthesis.objects.create(**obj_dict) - except: - print("Synthesis exists") - self.stdout.write(self.style.SUCCESS("Syntheses imported")) diff --git a/apps/member/migrations/0001_initial.py b/apps/member/migrations/0001_initial.py deleted file mode 100644 index fab265e..0000000 --- a/apps/member/migrations/0001_initial.py +++ /dev/null @@ -1,133 +0,0 @@ -# Generated by Django 3.1 on 2020-09-19 18:15 - -import django.contrib.auth.models -import django.contrib.auth.validators -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), - ('contenttypes', '0002_remove_content_type_name'), - ] - - operations = [ - migrations.CreateModel( - name='Config', - fields=[ - ('key', models.CharField(max_length=255, primary_key=True, serialize=False, verbose_name='key')), - ('value', models.TextField(default='', verbose_name='value')), - ], - options={ - 'verbose_name': 'configuration', - 'verbose_name_plural': 'configurations', - }, - ), - migrations.CreateModel( - name='Document', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('file', models.FileField(unique=True, upload_to='', verbose_name='file')), - ('uploaded_at', models.DateTimeField(auto_now_add=True, verbose_name='uploaded at')), - ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_member.document_set+', to='contenttypes.contenttype')), - ], - options={ - 'verbose_name': 'document', - 'verbose_name_plural': 'documents', - }, - ), - migrations.CreateModel( - name='Authorization', - fields=[ - ('document_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='member.document')), - ('type', models.CharField(choices=[('parental_consent', 'Parental consent'), ('photo_consent', 'Photo consent'), ('sanitary_plug', 'Sanitary plug'), ('scholarship', 'Scholarship')], max_length=32, verbose_name='type')), - ], - options={ - 'verbose_name': 'authorization', - 'verbose_name_plural': 'authorizations', - }, - bases=('member.document',), - ), - migrations.CreateModel( - name='MotivationLetter', - fields=[ - ('document_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='member.document')), - ], - options={ - 'verbose_name': 'motivation letter', - 'verbose_name_plural': 'motivation letters', - }, - bases=('member.document',), - ), - migrations.CreateModel( - name='Solution', - fields=[ - ('document_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='member.document')), - ('problem', models.PositiveSmallIntegerField(verbose_name='problem')), - ('final', models.BooleanField(default=False, verbose_name='final solution')), - ], - options={ - 'verbose_name': 'solution', - 'verbose_name_plural': 'solutions', - }, - bases=('member.document',), - ), - migrations.CreateModel( - name='Synthesis', - fields=[ - ('document_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='member.document')), - ('source', models.CharField(choices=[('opponent', 'Opponent'), ('rapporteur', 'Rapporteur')], max_length=16, verbose_name='source')), - ('round', models.PositiveSmallIntegerField(choices=[(1, 'Round 1'), (2, 'Round 2')], verbose_name='round')), - ('final', models.BooleanField(default=False, verbose_name='final synthesis')), - ], - options={ - 'verbose_name': 'synthesis', - 'verbose_name_plural': 'syntheses', - }, - bases=('member.document',), - ), - migrations.CreateModel( - name='TFJMUser', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('email', models.EmailField(help_text='This should be valid and will be controlled.', max_length=254, unique=True, verbose_name='email')), - ('birth_date', models.DateField(default=None, null=True, verbose_name='birth date')), - ('gender', models.CharField(choices=[('male', 'Male'), ('female', 'Female'), ('non-binary', 'Non binary')], default=None, max_length=16, null=True, verbose_name='gender')), - ('address', models.CharField(default=None, max_length=255, null=True, verbose_name='address')), - ('postal_code', models.PositiveIntegerField(default=None, null=True, verbose_name='postal code')), - ('city', models.CharField(default=None, max_length=255, null=True, verbose_name='city')), - ('country', models.CharField(default='France', max_length=255, null=True, verbose_name='country')), - ('phone_number', models.CharField(blank=True, default=None, max_length=20, null=True, verbose_name='phone number')), - ('school', models.CharField(default=None, max_length=255, null=True, verbose_name='school')), - ('student_class', models.CharField(choices=[('seconde', 'Seconde or less'), ('première', 'Première'), ('terminale', 'Terminale')], default=None, max_length=16, null=True, verbose_name='class')), - ('responsible_name', models.CharField(default=None, max_length=255, null=True, verbose_name='responsible name')), - ('responsible_phone', models.CharField(default=None, max_length=20, null=True, verbose_name='responsible phone')), - ('responsible_email', models.EmailField(default=None, max_length=254, null=True, verbose_name='responsible email')), - ('description', models.TextField(default=None, null=True, verbose_name='description')), - ('role', models.CharField(choices=[('0admin', 'Admin'), ('1volunteer', 'Organizer'), ('2coach', 'Coach'), ('3participant', 'Participant')], max_length=16)), - ('year', models.PositiveIntegerField(default=2020, verbose_name='year')), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), - ], - options={ - 'verbose_name': 'user', - 'verbose_name_plural': 'users', - }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), - ], - ), - ] diff --git a/apps/member/migrations/0002_auto_20200919_2015.py b/apps/member/migrations/0002_auto_20200919_2015.py deleted file mode 100644 index 7997bc8..0000000 --- a/apps/member/migrations/0002_auto_20200919_2015.py +++ /dev/null @@ -1,57 +0,0 @@ -# Generated by Django 3.1 on 2020-09-19 18:15 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('member', '0001_initial'), - ('auth', '0012_alter_user_first_name_max_length'), - ('tournament', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='tfjmuser', - name='team', - field=models.ForeignKey(help_text='Concerns only coaches and participants.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='users', to='tournament.team', verbose_name='team'), - ), - migrations.AddField( - model_name='tfjmuser', - name='user_permissions', - field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), - ), - migrations.AddField( - model_name='synthesis', - name='team', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='syntheses', to='tournament.team', verbose_name='team'), - ), - migrations.AddField( - model_name='solution', - name='team', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='solutions', to='tournament.team', verbose_name='team'), - ), - migrations.AddField( - model_name='motivationletter', - name='team', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='motivation_letters', to='tournament.team', verbose_name='team'), - ), - migrations.AddField( - model_name='authorization', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='authorizations', to=settings.AUTH_USER_MODEL, verbose_name='user'), - ), - migrations.AlterUniqueTogether( - name='synthesis', - unique_together={('team', 'source', 'round', 'final')}, - ), - migrations.AlterUniqueTogether( - name='solution', - unique_together={('team', 'problem', 'final')}, - ), - ] diff --git a/apps/member/migrations/0003_tfjmuser_email_confirmed.py b/apps/member/migrations/0003_tfjmuser_email_confirmed.py deleted file mode 100644 index c4bb95b..0000000 --- a/apps/member/migrations/0003_tfjmuser_email_confirmed.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1 on 2020-09-19 20:45 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('member', '0002_auto_20200919_2015'), - ] - - operations = [ - migrations.AddField( - model_name='tfjmuser', - name='email_confirmed', - field=models.BooleanField(default=False, verbose_name='email confirmed'), - ), - ] diff --git a/apps/member/migrations/__init__.py b/apps/member/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apps/member/models.py b/apps/member/models.py deleted file mode 100644 index 5ec4ffc..0000000 --- a/apps/member/models.py +++ /dev/null @@ -1,399 +0,0 @@ -import os -from datetime import date - -from django.contrib.auth.models import AbstractUser -from django.contrib.sites.models import Site -from django.db import models -from django.template import loader -from django.utils.encoding import force_bytes -from django.utils.http import urlsafe_base64_encode -from django.utils.translation import gettext_lazy as _ -from polymorphic.models import PolymorphicModel -from tournament.models import Team, Tournament - -from .tokens import email_validation_token - - -class TFJMUser(AbstractUser): - """ - The model of registered users (organizers/juries/admins/coachs/participants) - """ - USERNAME_FIELD = 'email' - REQUIRED_FIELDS = [] - - email = models.EmailField( - unique=True, - verbose_name=_("email"), - help_text=_("This should be valid and will be controlled."), - ) - - team = models.ForeignKey( - Team, - null=True, - on_delete=models.SET_NULL, - related_name="users", - verbose_name=_("team"), - help_text=_("Concerns only coaches and participants."), - ) - - birth_date = models.DateField( - null=True, - default=None, - verbose_name=_("birth date"), - ) - - gender = models.CharField( - max_length=16, - null=True, - default=None, - choices=[ - ("male", _("Male")), - ("female", _("Female")), - ("non-binary", _("Non binary")), - ], - verbose_name=_("gender"), - ) - - address = models.CharField( - max_length=255, - null=True, - default=None, - verbose_name=_("address"), - ) - - postal_code = models.PositiveIntegerField( - null=True, - default=None, - verbose_name=_("postal code"), - ) - - city = models.CharField( - max_length=255, - null=True, - default=None, - verbose_name=_("city"), - ) - - country = models.CharField( - max_length=255, - default="France", - null=True, - verbose_name=_("country"), - ) - - phone_number = models.CharField( - max_length=20, - null=True, - blank=True, - default=None, - verbose_name=_("phone number"), - ) - - school = models.CharField( - max_length=255, - null=True, - default=None, - verbose_name=_("school"), - ) - - student_class = models.CharField( - max_length=16, - choices=[ - ('seconde', _("Seconde or less")), - ('première', _("Première")), - ('terminale', _("Terminale")), - ], - null=True, - default=None, - verbose_name="class", - ) - - responsible_name = models.CharField( - max_length=255, - null=True, - default=None, - verbose_name=_("responsible name"), - ) - - responsible_phone = models.CharField( - max_length=20, - null=True, - default=None, - verbose_name=_("responsible phone"), - ) - - responsible_email = models.EmailField( - null=True, - default=None, - verbose_name=_("responsible email"), - ) - - description = models.TextField( - null=True, - default=None, - verbose_name=_("description"), - ) - - role = models.CharField( - max_length=16, - choices=[ - ("0admin", _("Admin")), - ("1volunteer", _("Organizer")), - ("2coach", _("Coach")), - ("3participant", _("Participant")), - ] - ) - - year = models.PositiveIntegerField( - default=os.getenv("TFJM_YEAR", date.today().year), - verbose_name=_("year"), - ) - - email_confirmed = models.BooleanField( - verbose_name=_("email confirmed"), - default=False, - ) - - @property - def participates(self): - """ - Return True iff this user is a participant or a coach, ie. if the user is a member of a team that worked - for the tournament. - """ - return self.role == "3participant" or self.role == "2coach" - - @property - def organizes(self): - """ - Return True iff this user is a local or global organizer of the tournament. This includes juries. - """ - return self.role == "1volunteer" or self.role == "0admin" - - @property - def admin(self): - """ - Return True iff this user is a global organizer, ie. an administrator. This should be equivalent to be - a superuser. - """ - return self.role == "0admin" - - class Meta: - verbose_name = _("user") - verbose_name_plural = _("users") - - def save(self, *args, **kwargs): - # We ensure that the username is the email of the user. - self.username = self.email - super().save(*args, **kwargs) - - def __str__(self): - return self.first_name + " " + self.last_name - - def send_email_validation_link(self): - subject = "[TFJM²] " + str(_("Activate your Note Kfet account")) - token = email_validation_token.make_token(self) - uid = urlsafe_base64_encode(force_bytes(self.pk)) - site = Site.objects.first() - message = loader.render_to_string('registration/mails/email_validation_email.txt', - { - 'user': self, - 'domain': site.domain, - 'token': token, - 'uid': uid, - }) - html = loader.render_to_string('registration/mails/email_validation_email.html', - { - 'user': self, - 'domain': site.domain, - 'token': token, - 'uid': uid, - }) - self.email_user(subject, message, html_message=html) - - -class Document(PolymorphicModel): - """ - Abstract model of any saved document (solution, synthesis, motivation letter, authorization) - """ - file = models.FileField( - unique=True, - verbose_name=_("file"), - ) - - uploaded_at = models.DateTimeField( - auto_now_add=True, - verbose_name=_("uploaded at"), - ) - - class Meta: - verbose_name = _("document") - verbose_name_plural = _("documents") - - def delete(self, *args, **kwargs): - self.file.delete(True) - return super().delete(*args, **kwargs) - - -class Authorization(Document): - """ - Model for authorization papers (parental consent, photo consent, sanitary plug, ...) - """ - user = models.ForeignKey( - TFJMUser, - on_delete=models.CASCADE, - related_name="authorizations", - verbose_name=_("user"), - ) - - type = models.CharField( - max_length=32, - choices=[ - ("parental_consent", _("Parental consent")), - ("photo_consent", _("Photo consent")), - ("sanitary_plug", _("Sanitary plug")), - ("scholarship", _("Scholarship")), - ], - verbose_name=_("type"), - ) - - class Meta: - verbose_name = _("authorization") - verbose_name_plural = _("authorizations") - - def __str__(self): - return _("{authorization} for user {user}").format(authorization=self.type, user=str(self.user)) - - -class MotivationLetter(Document): - """ - Model for motivation letters of a team. - """ - team = models.ForeignKey( - Team, - on_delete=models.CASCADE, - related_name="motivation_letters", - verbose_name=_("team"), - ) - - class Meta: - verbose_name = _("motivation letter") - verbose_name_plural = _("motivation letters") - - def __str__(self): - return _("Motivation letter of team {team} ({trigram})").format(team=self.team.name, trigram=self.team.trigram) - - -class Solution(Document): - """ - Model for solutions of team for a given problem, for the regional or final tournament. - """ - team = models.ForeignKey( - Team, - on_delete=models.CASCADE, - related_name="solutions", - verbose_name=_("team"), - ) - - problem = models.PositiveSmallIntegerField( - verbose_name=_("problem"), - ) - - final = models.BooleanField( - default=False, - verbose_name=_("final solution"), - ) - - @property - def tournament(self): - """ - Get the concerned tournament of a solution. - Generally the local tournament of a team, but it can be the final tournament if this is a solution for the - final tournament. - """ - return Tournament.get_final() if self.final else self.team.tournament - - class Meta: - verbose_name = _("solution") - verbose_name_plural = _("solutions") - unique_together = ('team', 'problem', 'final',) - - def __str__(self): - if self.final: - return _("Solution of team {trigram} for problem {problem} for final")\ - .format(trigram=self.team.trigram, problem=self.problem) - else: - return _("Solution of team {trigram} for problem {problem}")\ - .format(trigram=self.team.trigram, problem=self.problem) - - -class Synthesis(Document): - """ - Model for syntheses of a team for a given round and for a given role, for the regional or final tournament. - """ - team = models.ForeignKey( - Team, - on_delete=models.CASCADE, - related_name="syntheses", - verbose_name=_("team"), - ) - - source = models.CharField( - max_length=16, - choices=[ - ("opponent", _("Opponent")), - ("rapporteur", _("Rapporteur")), - ], - verbose_name=_("source"), - ) - - round = models.PositiveSmallIntegerField( - choices=[ - (1, _("Round 1")), - (2, _("Round 2")), - ], - verbose_name=_("round"), - ) - - final = models.BooleanField( - default=False, - verbose_name=_("final synthesis"), - ) - - @property - def tournament(self): - """ - Get the concerned tournament of a solution. - Generally the local tournament of a team, but it can be the final tournament if this is a solution for the - final tournament. - """ - return Tournament.get_final() if self.final else self.team.tournament - - class Meta: - verbose_name = _("synthesis") - verbose_name_plural = _("syntheses") - unique_together = ('team', 'source', 'round', 'final',) - - def __str__(self): - return _("Synthesis of team {trigram} that is {source} for the round {round} of tournament {tournament}")\ - .format(trigram=self.team.trigram, source=self.get_source_display().lower(), round=self.round, - tournament=self.tournament) - - -class Config(models.Model): - """ - Dictionary of configuration variables. - """ - key = models.CharField( - max_length=255, - primary_key=True, - verbose_name=_("key"), - ) - - value = models.TextField( - default="", - verbose_name=_("value"), - ) - - class Meta: - verbose_name = _("configuration") - verbose_name_plural = _("configurations") diff --git a/apps/member/tables.py b/apps/member/tables.py deleted file mode 100644 index 779dc47..0000000 --- a/apps/member/tables.py +++ /dev/null @@ -1,26 +0,0 @@ -import django_tables2 as tables -from django_tables2 import A - -from .models import TFJMUser - - -class UserTable(tables.Table): - """ - Table of users that are matched with a given queryset. - """ - last_name = tables.LinkColumn( - "member:information", - args=[A("pk")], - ) - - first_name = tables.LinkColumn( - "member:information", - args=[A("pk")], - ) - - class Meta: - model = TFJMUser - fields = ("last_name", "first_name", "role", "date_joined", ) - attrs = { - 'class': 'table table-condensed table-striped table-hover' - } diff --git a/apps/member/templatetags/__init__.py b/apps/member/templatetags/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apps/member/templatetags/getconfig.py b/apps/member/templatetags/getconfig.py deleted file mode 100644 index 0c6d776..0000000 --- a/apps/member/templatetags/getconfig.py +++ /dev/null @@ -1,25 +0,0 @@ -from django import template - -import os - -from member.models import Config - - -def get_config(value): - """ - Return a value stored into the config table in the database with a given key. - """ - config = Config.objects.get_or_create(key=value)[0] - return config.value - - -def get_env(value): - """ - Get a specified environment variable. - """ - return os.getenv(value) - - -register = template.Library() -register.filter('get_config', get_config) -register.filter('get_env', get_env) diff --git a/apps/member/tokens.py b/apps/member/tokens.py deleted file mode 100644 index d089892..0000000 --- a/apps/member/tokens.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.contrib.auth.tokens import PasswordResetTokenGenerator - - -class AccountActivationTokenGenerator(PasswordResetTokenGenerator): - """ - Create a unique token generator to confirm email addresses. - """ - def _make_hash_value(self, user, timestamp): - """ - Hash the user's primary key and some user state that's sure to change - after an account validation to produce a token that invalidated when - it's used: - 1. The user.profile.email_confirmed field will change upon an account - validation. - 2. The last_login field will usually be updated very shortly after - an account validation. - Failing those things, settings.PASSWORD_RESET_TIMEOUT_DAYS eventually - invalidates the token. - """ - # Truncate microseconds so that tokens are consistent even if the - # database doesn't support microseconds. - login_timestamp = '' if user.last_login is None else user.last_login.replace(microsecond=0, tzinfo=None) - return str(user.pk) + str(user.email) + str(user.email_confirmed) + str(login_timestamp) + str(timestamp) - - -email_validation_token = AccountActivationTokenGenerator() diff --git a/apps/member/urls.py b/apps/member/urls.py deleted file mode 100644 index ffe31fa..0000000 --- a/apps/member/urls.py +++ /dev/null @@ -1,24 +0,0 @@ -from django.urls import path - -from .views import CreateUserView, MyAccountView, UserDetailView, AddTeamView, JoinTeamView, MyTeamView, \ - ProfileListView, OrphanedProfileListView, OrganizersListView, ResetAdminView, UserValidationEmailSentView, \ - UserResendValidationEmailView, UserValidateView - -app_name = "member" - -urlpatterns = [ - path('signup/', CreateUserView.as_view(), name="signup"), - path('validate_email/sent/', UserValidationEmailSentView.as_view(), name='email_validation_sent'), - path('validate_email/resend//', UserResendValidationEmailView.as_view(), - name='email_validation_resend'), - path('validate_email///', UserValidateView.as_view(), name='email_validation'), - path("my-account/", MyAccountView.as_view(), name="my_account"), - path("information//", UserDetailView.as_view(), name="information"), - path("add-team/", AddTeamView.as_view(), name="add_team"), - path("join-team/", JoinTeamView.as_view(), name="join_team"), - path("my-team/", MyTeamView.as_view(), name="my_team"), - path("profiles/", ProfileListView.as_view(), name="all_profiles"), - path("orphaned-profiles/", OrphanedProfileListView.as_view(), name="orphaned_profiles"), - path("organizers/", OrganizersListView.as_view(), name="organizers"), - path("reset-admin/", ResetAdminView.as_view(), name="reset_admin"), -] diff --git a/apps/member/views.py b/apps/member/views.py deleted file mode 100644 index aef0c61..0000000 --- a/apps/member/views.py +++ /dev/null @@ -1,374 +0,0 @@ -import random - -from django.conf import settings -from django.contrib.auth.mixins import LoginRequiredMixin, AccessMixin -from django.contrib.auth.models import AnonymousUser -from django.core.exceptions import PermissionDenied, ValidationError -from django.db.models import Q -from django.http import FileResponse, Http404 -from django.shortcuts import redirect, resolve_url -from django.urls import reverse_lazy -from django.utils import timezone -from django.utils.decorators import method_decorator -from django.utils.http import urlsafe_base64_decode -from django.utils.translation import gettext_lazy as _ -from django.views import View -from django.views.decorators.debug import sensitive_post_parameters -from django.views.generic import CreateView, UpdateView, DetailView, FormView, TemplateView -from django_tables2 import SingleTableView -from tournament.forms import TeamForm, JoinTeam -from tournament.models import Team, Tournament, Pool -from tournament.views import AdminMixin, TeamMixin, OrgaMixin - -from .forms import SignUpForm, TFJMUserForm, AdminUserForm, CoachUserForm -from .models import TFJMUser, Document, Solution, MotivationLetter, Synthesis -from .tables import UserTable -from .tokens import email_validation_token - - -class CreateUserView(CreateView): - """ - Signup form view. - """ - model = TFJMUser - form_class = SignUpForm - template_name = "registration/signup.html" - - # When errors are reported from the signup view, don't send passwords to admins - @method_decorator(sensitive_post_parameters('password1', 'password2',)) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) - - def form_valid(self, form): - form.instance.send_email_validation_link() - return super().form_valid(form) - - def get_success_url(self): - return reverse_lazy('member:email_validation_sent') - - -class UserValidateView(TemplateView): - """ - A view to validate the email address. - """ - title = _("Email validation") - template_name = 'registration/email_validation_complete.html' - extra_context = {"title": _("Validate email")} - - def get(self, *args, **kwargs): - """ - With a given token and user id (in params), validate the email address. - """ - assert 'uidb64' in kwargs and 'token' in kwargs - - self.validlink = False - user = self.get_user(kwargs['uidb64']) - token = kwargs['token'] - - # Validate the token - if user is not None and email_validation_token.check_token(user, token): - self.validlink = True - user.email_confirmed = True - user.save() - return self.render_to_response(self.get_context_data(), status=200 if self.validlink else 400) - - def get_user(self, uidb64): - """ - Get user from the base64-encoded string. - """ - try: - # urlsafe_base64_decode() decodes to bytestring - uid = urlsafe_base64_decode(uidb64).decode() - user = TFJMUser.objects.get(pk=uid) - except (TypeError, ValueError, OverflowError, TFJMUser.DoesNotExist, ValidationError): - user = None - return user - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['user_object'] = self.get_user(self.kwargs["uidb64"]) - context['login_url'] = resolve_url(settings.LOGIN_URL) - if self.validlink: - context['validlink'] = True - else: - context.update({ - 'title': _('Email validation unsuccessful'), - 'validlink': False, - }) - return context - - -class UserValidationEmailSentView(TemplateView): - """ - Display the information that the validation link has been sent. - """ - template_name = 'registration/email_validation_email_sent.html' - extra_context = {"title": _('Email validation email sent')} - - -class UserResendValidationEmailView(LoginRequiredMixin, DetailView): - """ - Rensend the email validation link. - """ - model = TFJMUser - extra_context = {"title": _("Resend email validation link")} - - def get(self, request, *args, **kwargs): - user = self.get_object() - - user.profile.send_email_validation_link() - - url = 'member:user_detail' if user.profile.registration_valid else 'member:future_user_detail' - return redirect(url, user.id) - - -class MyAccountView(LoginRequiredMixin, UpdateView): - """ - Update our personal data. - """ - model = TFJMUser - template_name = "member/my_account.html" - - def get_form_class(self): - # The used form can change according to the role of the user. - return AdminUserForm if self.request.user.organizes else TFJMUserForm \ - if self.request.user.role == "3participant" else CoachUserForm - - def get_object(self, queryset=None): - return self.request.user - - def get_success_url(self): - return reverse_lazy('member:my_account') - - -class UserDetailView(LoginRequiredMixin, DetailView): - """ - View the personal information of a given user. - Only organizers can see this page, since there are personal data. - """ - model = TFJMUser - form_class = TFJMUserForm - context_object_name = "tfjmuser" - - def dispatch(self, request, *args, **kwargs): - if isinstance(request.user, AnonymousUser): - raise PermissionDenied - - self.object = self.get_object() - - if not request.user.admin \ - and (self.object.team is not None and request.user not in self.object.team.tournament.organizers.all())\ - and (self.object.team is not None and self.object.team.selected_for_final - and request.user not in Tournament.get_final().organizers.all())\ - and self.request.user != self.object: - raise PermissionDenied - return super().dispatch(request, *args, **kwargs) - - def post(self, request, *args, **kwargs): - """ - An administrator can log in through this page as someone else, and act as this other person. - """ - if "view_as" in request.POST and self.request.user.admin: - session = request.session - session["admin"] = request.user.pk - obj = self.get_object() - session["_fake_user_id"] = obj.pk - return redirect(request.path) - return self.get(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - context["title"] = str(self.object) - - return context - - -class AddTeamView(LoginRequiredMixin, CreateView): - """ - Register a new team. - Users can choose the name, the trigram and a preferred tournament. - """ - model = Team - form_class = TeamForm - - def form_valid(self, form): - if self.request.user.organizes: - form.add_error('name', _("You can't organize and participate at the same time.")) - return self.form_invalid(form) - - if self.request.user.team: - form.add_error('name', _("You are already in a team.")) - return self.form_invalid(form) - - # Generate a random access code - team = form.instance - alphabet = "0123456789abcdefghijklmnopqrstuvwxyz0123456789" - code = "" - for i in range(6): - code += random.choice(alphabet) - team.access_code = code - team.validation_status = "0invalid" - - team.save() - team.refresh_from_db() - - self.request.user.team = team - self.request.user.save() - - return super().form_valid(form) - - def get_success_url(self): - return reverse_lazy("member:my_team") - - -class JoinTeamView(LoginRequiredMixin, FormView): - """ - Join a team with a given access code. - """ - model = Team - form_class = JoinTeam - template_name = "tournament/team_form.html" - - def form_valid(self, form): - team = form.cleaned_data["team"] - - if self.request.user.organizes: - form.add_error('access_code', _("You can't organize and participate at the same time.")) - return self.form_invalid(form) - - if self.request.user.team: - form.add_error('access_code', _("You are already in a team.")) - return self.form_invalid(form) - - if self.request.user.role == '2coach' and len(team.coaches) == 3: - form.add_error('access_code', _("This team is full of coachs.")) - return self.form_invalid(form) - - if self.request.user.role == '3participant' and len(team.participants) == 6: - form.add_error('access_code', _("This team is full of participants.")) - return self.form_invalid(form) - - if not team.invalid: - form.add_error('access_code', _("This team is already validated or waiting for validation.")) - - self.request.user.team = team - self.request.user.save() - return super().form_valid(form) - - def get_success_url(self): - return reverse_lazy("member:my_team") - - -class MyTeamView(TeamMixin, View): - """ - Redirect to the page of the information of our personal team. - """ - - def get(self, request, *args, **kwargs): - return redirect("tournament:team_detail", pk=request.user.team.pk) - - -class DocumentView(AccessMixin, View): - """ - View a PDF document, if we have the right. - - - Everyone can see the documents that concern itself. - - An administrator can see anything. - - An organizer can see documents that are related to its tournament. - - A jury can see solutions and syntheses that are evaluated in their pools. - """ - - def get(self, request, *args, **kwargs): - try: - doc = Document.objects.get(file=self.kwargs["file"]) - except Document.DoesNotExist: - raise Http404(_("No %(verbose_name)s found matching the query") % - {'verbose_name': Document._meta.verbose_name}) - - if request.user.is_authenticated: - grant = request.user.admin - - if isinstance(doc, Solution) or isinstance(doc, Synthesis): - grant = grant or doc.team == request.user.team or request.user in doc.tournament.organizers.all() - elif isinstance(doc, MotivationLetter): - grant = grant or doc.team == request.user.team or request.user in doc.team.tournament.organizers.all() - grant = grant or doc.team.selected_for_final and request.user in Tournament.get_final().organizers.all() - - if isinstance(doc, Solution): - for pool in doc.pools.all(): - if request.user in pool.juries.all(): - grant = True - break - if pool.round == 2 and timezone.now() < doc.tournament.date_solutions_2: - continue - if self.request.user.team in pool.teams.all(): - grant = True - elif isinstance(doc, Synthesis): - for pool in request.user.pools.all(): # If the user is a jury in the pool - if doc.team in pool.teams.all() and doc.final == pool.tournament.final: - grant = True - break - else: - pool = Pool.objects.filter(extra_access_token=self.request.session["extra_access_token"]) - if pool.exists(): - pool = pool.get() - if isinstance(doc, Solution): - grant = doc in pool.solutions.all() - elif isinstance(doc, Synthesis): - grant = doc.team in pool.teams.all() and doc.final == pool.tournament.final - else: - grant = False - else: - grant = False - - if not grant: - raise PermissionDenied - - return FileResponse(doc.file, content_type="application/pdf", filename=str(doc) + ".pdf") - - -class ProfileListView(AdminMixin, SingleTableView): - """ - List all registered profiles. - """ - model = TFJMUser - queryset = TFJMUser.objects.order_by("role", "last_name", "first_name") - table_class = UserTable - template_name = "member/profile_list.html" - extra_context = dict(title=_("All profiles"), type="all") - - -class OrphanedProfileListView(AdminMixin, SingleTableView): - """ - List all orphaned profiles, ie. participants that have no team. - """ - model = TFJMUser - queryset = TFJMUser.objects.filter((Q(role="2coach") | Q(role="3participant")) & Q(team__isnull=True))\ - .order_by("role", "last_name", "first_name") - table_class = UserTable - template_name = "member/profile_list.html" - extra_context = dict(title=_("Orphaned profiles"), type="orphaned") - - -class OrganizersListView(OrgaMixin, SingleTableView): - """ - List all organizers. - """ - model = TFJMUser - queryset = TFJMUser.objects.filter(Q(role="0admin") | Q(role="1volunteer"))\ - .order_by("role", "last_name", "first_name") - table_class = UserTable - template_name = "member/profile_list.html" - extra_context = dict(title=_("Organizers"), type="organizers") - - -class ResetAdminView(AdminMixin, View): - """ - Return to admin view, clear the session field that let an administrator to log in as someone else. - """ - - def dispatch(self, request, *args, **kwargs): - if "_fake_user_id" in request.session: - del request.session["_fake_user_id"] - return redirect(request.GET["path"]) diff --git a/apps/tournament/__init__.py b/apps/tournament/__init__.py deleted file mode 100644 index 9868b56..0000000 --- a/apps/tournament/__init__.py +++ /dev/null @@ -1 +0,0 @@ -default_app_config = 'tournament.apps.TournamentConfig' diff --git a/apps/tournament/admin.py b/apps/tournament/admin.py deleted file mode 100644 index c55cc4b..0000000 --- a/apps/tournament/admin.py +++ /dev/null @@ -1,31 +0,0 @@ -from django.contrib.auth.admin import admin - -from .models import Team, Tournament, Pool, Payment - - -@admin.register(Team) -class TeamAdmin(admin.ModelAdmin): - """ - Django admin page for teams. - """ - - -@admin.register(Tournament) -class TournamentAdmin(admin.ModelAdmin): - """ - Django admin page for tournaments. - """ - - -@admin.register(Pool) -class PoolAdmin(admin.ModelAdmin): - """ - Django admin page for pools. - """ - - -@admin.register(Payment) -class PaymentAdmin(admin.ModelAdmin): - """ - Django admin page for payments. - """ diff --git a/apps/tournament/apps.py b/apps/tournament/apps.py deleted file mode 100644 index 66a212b..0000000 --- a/apps/tournament/apps.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.apps import AppConfig -from django.utils.translation import gettext_lazy as _ - - -class TournamentConfig(AppConfig): - """ - The tournament app handles all that is related to the tournaments. - """ - name = 'tournament' - verbose_name = _('tournament') diff --git a/apps/tournament/forms.py b/apps/tournament/forms.py deleted file mode 100644 index 901b97a..0000000 --- a/apps/tournament/forms.py +++ /dev/null @@ -1,262 +0,0 @@ -import os -import re - -from django import forms -from django.db.models import Q -from django.template.defaultfilters import filesizeformat -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ - -from member.models import TFJMUser, Solution, Synthesis -from tfjm.inputs import DatePickerInput, DateTimePickerInput, AmountInput -from tournament.models import Tournament, Team, Pool - - -class TournamentForm(forms.ModelForm): - """ - Create and update tournaments. - """ - - # Only organizers can organize tournaments. Well, that's pretty normal... - organizers = forms.ModelMultipleChoiceField( - TFJMUser.objects.filter(Q(role="0admin") | Q(role="1volunteer")).order_by('role'), - label=_("Organizers"), - ) - - def clean(self): - cleaned_data = super().clean() - - if not self.instance.pk: - if Tournament.objects.filter(name=cleaned_data["data"], year=os.getenv("TFJM_YEAR")): - self.add_error("name", _("This tournament already exists.")) - if cleaned_data["final"] and Tournament.objects.filter(final=True, year=os.getenv("TFJM_YEAR")): - self.add_error("name", _("The final tournament was already defined.")) - - return cleaned_data - - class Meta: - model = Tournament - exclude = ('year',) - widgets = { - "price": AmountInput(), - "date_start": DatePickerInput(), - "date_end": DatePickerInput(), - "date_inscription": DateTimePickerInput(), - "date_solutions": DateTimePickerInput(), - "date_syntheses": DateTimePickerInput(), - "date_solutions_2": DateTimePickerInput(), - "date_syntheses_2": DateTimePickerInput(), - } - - -class OrganizerForm(forms.ModelForm): - """ - Register an organizer in the website. - """ - - class Meta: - model = TFJMUser - fields = ('last_name', 'first_name', 'email', 'is_superuser',) - - def clean(self): - cleaned_data = super().clean() - - if TFJMUser.objects.filter(email=cleaned_data["email"], year=os.getenv("TFJM_YEAR")).exists(): - self.add_error("email", _("This organizer already exist.")) - - return cleaned_data - - def save(self, commit=True): - user = self.instance - user.role = '0admin' if user.is_superuser else '1volunteer' - user.save() - super().save(commit) - - -class TeamForm(forms.ModelForm): - """ - Add and update a team. - """ - tournament = forms.ModelChoiceField( - Tournament.objects.filter(date_inscription__gte=timezone.now(), final=False), - ) - - class Meta: - model = Team - fields = ('name', 'trigram', 'tournament',) - - def clean(self): - cleaned_data = super().clean() - - cleaned_data["trigram"] = cleaned_data["trigram"].upper() - - if not re.match("[A-Z]{3}", cleaned_data["trigram"]): - self.add_error("trigram", _("The trigram must be composed of three upcase letters.")) - - if not self.instance.pk: - if Team.objects.filter(trigram=cleaned_data["trigram"], year=os.getenv("TFJM_YEAR")).exists(): - self.add_error("trigram", _("This trigram is already used.")) - - if Team.objects.filter(name=cleaned_data["name"], year=os.getenv("TFJM_YEAR")).exists(): - self.add_error("name", _("This name is already used.")) - - if cleaned_data["tournament"].date_inscription < timezone.now: - self.add_error("tournament", _("This tournament is already closed.")) - - return cleaned_data - - -class JoinTeam(forms.Form): - """ - Form to join a team with an access code. - """ - - access_code = forms.CharField( - label=_("Access code"), - max_length=6, - ) - - def clean(self): - cleaned_data = super().clean() - - if not re.match("[a-z0-9]{6}", cleaned_data["access_code"]): - self.add_error('access_code', _("The access code must be composed of 6 alphanumeric characters.")) - - team = Team.objects.filter(access_code=cleaned_data["access_code"]) - if not team.exists(): - self.add_error('access_code', _("This access code is invalid.")) - team = team.get() - if not team.invalid: - self.add_error('access_code', _("The team is already validated.")) - cleaned_data["team"] = team - - return cleaned_data - - -class SolutionForm(forms.ModelForm): - """ - Form to upload a solution. - """ - - problem = forms.ChoiceField( - label=_("Problem"), - choices=[(str(i), _("Problem #%(problem)d") % {"problem": i}) for i in range(1, 9)], - ) - - def clean_file(self): - content = self.cleaned_data['file'] - content_type = content.content_type - if content_type in ["application/pdf"]: - if content.size > 5 * 2 ** 20: - raise forms.ValidationError( - _('Please keep filesize under %(max_size)s. Current filesize %(current_size)s') % { - "max_size": filesizeformat(2 * 2 ** 20), - "current_size": filesizeformat(content.size) - }) - else: - raise forms.ValidationError(_('The file should be a PDF file.')) - return content - - class Meta: - model = Solution - fields = ('file', 'problem',) - - -class SynthesisForm(forms.ModelForm): - """ - Form to upload a synthesis. - """ - - def clean_file(self): - content = self.cleaned_data['file'] - content_type = content.content_type - if content_type in ["application/pdf"]: - if content.size > 5 * 2 ** 20: - raise forms.ValidationError( - _('Please keep filesize under %(max_size)s. Current filesize %(current_size)s') % { - "max_size": filesizeformat(2 * 2 ** 20), - "current_size": filesizeformat(content.size) - }) - else: - raise forms.ValidationError(_('The file should be a PDF file.')) - return content - - class Meta: - model = Synthesis - fields = ('file', 'source', 'round',) - - -class PoolForm(forms.ModelForm): - """ - Form to add a pool. - Should not be used: prefer to pass by API and auto-add pools with the results of the draw. - """ - - team1 = forms.ModelChoiceField( - Team.objects.filter(validation_status="2valid").all(), - empty_label=_("Choose a team..."), - label=_("Team 1"), - ) - - problem1 = forms.IntegerField( - min_value=1, - max_value=8, - initial=1, - label=_("Problem defended by team 1"), - ) - - team2 = forms.ModelChoiceField( - Team.objects.filter(validation_status="2valid").all(), - empty_label=_("Choose a team..."), - label=_("Team 2"), - ) - - problem2 = forms.IntegerField( - min_value=1, - max_value=8, - initial=2, - label=_("Problem defended by team 2"), - ) - - team3 = forms.ModelChoiceField( - Team.objects.filter(validation_status="2valid").all(), - empty_label=_("Choose a team..."), - label=_("Team 3"), - ) - - problem3 = forms.IntegerField( - min_value=1, - max_value=8, - initial=3, - label=_("Problem defended by team 3"), - ) - - def clean(self): - cleaned_data = super().clean() - - team1, pb1 = cleaned_data["team1"], cleaned_data["problem1"] - team2, pb2 = cleaned_data["team2"], cleaned_data["problem2"] - team3, pb3 = cleaned_data["team3"], cleaned_data["problem3"] - - sol1 = Solution.objects.get(team=team1, problem=pb1, final=team1.selected_for_final) - sol2 = Solution.objects.get(team=team2, problem=pb2, final=team2.selected_for_final) - sol3 = Solution.objects.get(team=team3, problem=pb3, final=team3.selected_for_final) - - cleaned_data["teams"] = [team1, team2, team3] - cleaned_data["solutions"] = [sol1, sol2, sol3] - - return cleaned_data - - def save(self, commit=True): - pool = super().save(commit) - - pool.refresh_from_db() - pool.teams.set(self.cleaned_data["teams"]) - pool.solutions.set(self.cleaned_data["solutions"]) - pool.save() - - return pool - - class Meta: - model = Pool - fields = ('round', 'juries',) diff --git a/apps/tournament/migrations/0001_initial.py b/apps/tournament/migrations/0001_initial.py deleted file mode 100644 index 22ee135..0000000 --- a/apps/tournament/migrations/0001_initial.py +++ /dev/null @@ -1,92 +0,0 @@ -# Generated by Django 3.1 on 2020-09-19 18:15 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('member', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Tournament', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='name')), - ('size', models.PositiveSmallIntegerField(help_text='Number of teams that are allowed to join the tournament.', verbose_name='size')), - ('place', models.CharField(max_length=255, verbose_name='place')), - ('price', models.PositiveSmallIntegerField(help_text='Price asked to participants. Free with a scholarship.', verbose_name='price')), - ('description', models.TextField(verbose_name='description')), - ('date_start', models.DateField(default=django.utils.timezone.now, verbose_name='date start')), - ('date_end', models.DateField(default=django.utils.timezone.now, verbose_name='date end')), - ('date_inscription', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date of registration closing')), - ('date_solutions', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date of maximal solution submission')), - ('date_syntheses', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date of maximal syntheses submission for the first round')), - ('date_solutions_2', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date when solutions of round 2 are available')), - ('date_syntheses_2', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date of maximal syntheses submission for the second round')), - ('final', models.BooleanField(help_text='It should be only one final tournament.', verbose_name='final tournament')), - ('year', models.PositiveIntegerField(default=2020, verbose_name='year')), - ('organizers', models.ManyToManyField(help_text='List of all organizers that can see and manipulate data of the tournament and the teams.', related_name='organized_tournaments', to=settings.AUTH_USER_MODEL, verbose_name='organizers')), - ], - options={ - 'verbose_name': 'tournament', - 'verbose_name_plural': 'tournaments', - }, - ), - migrations.CreateModel( - name='Team', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='name')), - ('trigram', models.CharField(help_text='The trigram should be composed of 3 capitalize letters, that is a funny acronym for the team.', max_length=3, verbose_name='trigram')), - ('inscription_date', models.DateTimeField(auto_now_add=True, verbose_name='inscription date')), - ('validation_status', models.CharField(choices=[('0invalid', 'Registration not validated'), ('1waiting', 'Waiting for validation'), ('2valid', 'Registration validated')], max_length=8, verbose_name='validation status')), - ('selected_for_final', models.BooleanField(default=False, verbose_name='selected for final')), - ('access_code', models.CharField(max_length=6, unique=True, verbose_name='access code')), - ('year', models.PositiveIntegerField(default=2020, verbose_name='year')), - ('tournament', models.ForeignKey(help_text='The tournament where the team is registered.', on_delete=django.db.models.deletion.PROTECT, related_name='_teams', to='tournament.tournament', verbose_name='tournament')), - ], - options={ - 'verbose_name': 'team', - 'verbose_name_plural': 'teams', - 'unique_together': {('name', 'year'), ('trigram', 'year')}, - }, - ), - migrations.CreateModel( - name='Pool', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('round', models.PositiveIntegerField(choices=[(1, 'Round 1'), (2, 'Round 2')], verbose_name='round')), - ('extra_access_token', models.CharField(default='', help_text='Let other users access to the pool data without logging in.', max_length=64, verbose_name='extra access token')), - ('juries', models.ManyToManyField(related_name='pools', to=settings.AUTH_USER_MODEL, verbose_name='juries')), - ('solutions', models.ManyToManyField(related_name='pools', to='member.Solution', verbose_name='solutions')), - ('teams', models.ManyToManyField(related_name='pools', to='tournament.Team', verbose_name='teams')), - ], - options={ - 'verbose_name': 'pool', - 'verbose_name_plural': 'pools', - }, - ), - migrations.CreateModel( - name='Payment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('method', models.CharField(choices=[('not_paid', 'Not paid'), ('credit_card', 'Credit card'), ('check', 'Bank check'), ('transfer', 'Bank transfer'), ('cash', 'Cash'), ('scholarship', 'Scholarship')], default='not_paid', max_length=16, verbose_name='payment method')), - ('validation_status', models.CharField(choices=[('0invalid', 'Registration not validated'), ('1waiting', 'Waiting for validation'), ('2valid', 'Registration validated')], max_length=8, verbose_name='validation status')), - ('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='tournament.team', verbose_name='team')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='payment', to=settings.AUTH_USER_MODEL, verbose_name='user')), - ], - options={ - 'verbose_name': 'payment', - 'verbose_name_plural': 'payments', - }, - ), - ] diff --git a/apps/tournament/migrations/__init__.py b/apps/tournament/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/apps/tournament/models.py b/apps/tournament/models.py deleted file mode 100644 index 11d5886..0000000 --- a/apps/tournament/models.py +++ /dev/null @@ -1,432 +0,0 @@ -import os -import random - -from django.core.mail import send_mail -from django.db import models -from django.template.loader import render_to_string -from django.urls import reverse_lazy -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ - - -class Tournament(models.Model): - """ - Store the information of a tournament. - """ - - name = models.CharField( - max_length=255, - verbose_name=_("name"), - ) - - organizers = models.ManyToManyField( - 'member.TFJMUser', - related_name="organized_tournaments", - verbose_name=_("organizers"), - help_text=_("List of all organizers that can see and manipulate data of the tournament and the teams."), - ) - - size = models.PositiveSmallIntegerField( - verbose_name=_("size"), - help_text=_("Number of teams that are allowed to join the tournament."), - ) - - place = models.CharField( - max_length=255, - verbose_name=_("place"), - ) - - price = models.PositiveSmallIntegerField( - verbose_name=_("price"), - help_text=_("Price asked to participants. Free with a scholarship."), - ) - - description = models.TextField( - verbose_name=_("description"), - ) - - date_start = models.DateField( - default=timezone.now, - verbose_name=_("date start"), - ) - - date_end = models.DateField( - default=timezone.now, - verbose_name=_("date end"), - ) - - date_inscription = models.DateTimeField( - default=timezone.now, - verbose_name=_("date of registration closing"), - ) - - date_solutions = models.DateTimeField( - default=timezone.now, - verbose_name=_("date of maximal solution submission"), - ) - - date_syntheses = models.DateTimeField( - default=timezone.now, - verbose_name=_("date of maximal syntheses submission for the first round"), - ) - - date_solutions_2 = models.DateTimeField( - default=timezone.now, - verbose_name=_("date when solutions of round 2 are available"), - ) - - date_syntheses_2 = models.DateTimeField( - default=timezone.now, - verbose_name=_("date of maximal syntheses submission for the second round"), - ) - - final = models.BooleanField( - verbose_name=_("final tournament"), - help_text=_("It should be only one final tournament."), - ) - - year = models.PositiveIntegerField( - default=os.getenv("TFJM_YEAR", timezone.now().year), - verbose_name=_("year"), - ) - - @property - def teams(self): - """ - Get all teams that are registered to this tournament, with a distinction for the final tournament. - """ - return self._teams if not self.final else Team.objects.filter(selected_for_final=True) - - @property - def linked_organizers(self): - """ - Display a list of the organizers with links to their personal page. - """ - return [''.format(url=reverse_lazy("member:information", args=(user.pk,))) + str(user) + '' - for user in self.organizers.all()] - - @property - def solutions(self): - """ - Get all sent solutions for this tournament. - """ - from member.models import Solution - return Solution.objects.filter(final=self.final) if self.final \ - else Solution.objects.filter(team__tournament=self, final=False) - - @property - def syntheses(self): - """ - Get all sent syntheses for this tournament. - """ - from member.models import Synthesis - return Synthesis.objects.filter(final=self.final) if self.final \ - else Synthesis.objects.filter(team__tournament=self, final=False) - - @classmethod - def get_final(cls): - """ - Get the final tournament. - This should exist and be unique. - """ - return cls.objects.get(year=os.getenv("TFJM_YEAR"), final=True) - - class Meta: - verbose_name = _("tournament") - verbose_name_plural = _("tournaments") - - def send_mail_to_organizers(self, template_name, subject="Contact TFJM²", **kwargs): - """ - Send a mail to all organizers of the tournament. - The template of the mail should be found either in templates/mail_templates/.html for the HTML - version and in templates/mail_templates/.txt for the plain text version. - The context of the template contains the tournament and the user. Extra context can be given through the kwargs. - """ - context = kwargs - context["tournament"] = self - for user in self.organizers.all(): - context["user"] = user - message = render_to_string("mail_templates/" + template_name + ".txt", context=context) - message_html = render_to_string("mail_templates/" + template_name + ".html", context=context) - send_mail(subject, message, "contact@tfjm.org", [user.email], html_message=message_html) - from member.models import TFJMUser - for user in TFJMUser.objects.get(is_superuser=True).all(): - context["user"] = user - message = render_to_string("mail_templates/" + template_name + ".txt", context=context) - message_html = render_to_string("mail_templates/" + template_name + ".html", context=context) - send_mail(subject, message, "contact@tfjm.org", [user.email], html_message=message_html) - - def __str__(self): - return self.name - - -class Team(models.Model): - """ - Store information about a registered team. - """ - - name = models.CharField( - max_length=255, - verbose_name=_("name"), - ) - - trigram = models.CharField( - max_length=3, - verbose_name=_("trigram"), - help_text=_("The trigram should be composed of 3 capitalize letters, that is a funny acronym for the team."), - ) - - tournament = models.ForeignKey( - Tournament, - on_delete=models.PROTECT, - related_name="_teams", - verbose_name=_("tournament"), - help_text=_("The tournament where the team is registered."), - ) - - inscription_date = models.DateTimeField( - auto_now_add=True, - verbose_name=_("inscription date"), - ) - - validation_status = models.CharField( - max_length=8, - choices=[ - ("0invalid", _("Registration not validated")), - ("1waiting", _("Waiting for validation")), - ("2valid", _("Registration validated")), - ], - verbose_name=_("validation status"), - ) - - selected_for_final = models.BooleanField( - default=False, - verbose_name=_("selected for final"), - ) - - access_code = models.CharField( - max_length=6, - unique=True, - verbose_name=_("access code"), - ) - - year = models.PositiveIntegerField( - default=os.getenv("TFJM_YEAR", timezone.now().year), - verbose_name=_("year"), - ) - - @property - def valid(self): - return self.validation_status == "2valid" - - @property - def waiting(self): - return self.validation_status == "1waiting" - - @property - def invalid(self): - return self.validation_status == "0invalid" - - @property - def coaches(self): - """ - Get all coaches of a team. - """ - return self.users.all().filter(role="2coach") - - @property - def linked_coaches(self): - """ - Get a list of the coaches of a team with html links to their pages. - """ - return [''.format(url=reverse_lazy("member:information", args=(user.pk,))) + str(user) + '' - for user in self.coaches] - - @property - def participants(self): - """ - Get all particpants of a team, coaches excluded. - """ - return self.users.all().filter(role="3participant") - - @property - def linked_participants(self): - """ - Get a list of the participants of a team with html links to their pages. - """ - return [''.format(url=reverse_lazy("member:information", args=(user.pk,))) + str(user) + '' - for user in self.participants] - - @property - def future_tournament(self): - """ - Get the last tournament where the team is registered. - Only matters if the team is selected for final: if this is the case, we return the final tournament. - Useful for deadlines. - """ - return Tournament.get_final() if self.selected_for_final else self.tournament - - @property - def can_validate(self): - """ - Check if a given team is able to ask for validation. - A team can validate if: - * All participants filled the photo consent - * Minor participants filled the parental consent - * Minor participants filled the sanitary plug - * Teams sent their motivation letter - * The team contains at least 4 participants - * The team contains at least 1 coach - """ - # TODO In a normal time, team needs a motivation letter and authorizations. - return self.coaches.exists() and self.participants.count() >= 4\ - and self.tournament.date_inscription <= timezone.now() - - class Meta: - verbose_name = _("team") - verbose_name_plural = _("teams") - unique_together = (('name', 'year',), ('trigram', 'year',),) - - def send_mail(self, template_name, subject="Contact TFJM²", **kwargs): - """ - Send a mail to all members of a team with a given template. - The template of the mail should be found either in templates/mail_templates/.html for the HTML - version and in templates/mail_templates/.txt for the plain text version. - The context of the template contains the team and the user. Extra context can be given through the kwargs. - """ - context = kwargs - context["team"] = self - for user in self.users.all(): - context["user"] = user - message = render_to_string("mail_templates/" + template_name + ".txt", context=context) - message_html = render_to_string("mail_templates/" + template_name + ".html", context=context) - send_mail(subject, message, "contact@tfjm.org", [user.email], html_message=message_html) - - def __str__(self): - return self.trigram + " — " + self.name - - -class Pool(models.Model): - """ - Store information of a pool. - A pool is only a list of accessible solutions to some teams and some juries. - TODO: check that the set of teams is equal to the set of the teams that have a solution in this set. - TODO: Moreover, a team should send only one solution. - """ - teams = models.ManyToManyField( - Team, - related_name="pools", - verbose_name=_("teams"), - ) - - solutions = models.ManyToManyField( - "member.Solution", - related_name="pools", - verbose_name=_("solutions"), - ) - - round = models.PositiveIntegerField( - choices=[ - (1, _("Round 1")), - (2, _("Round 2")), - ], - verbose_name=_("round"), - ) - - juries = models.ManyToManyField( - "member.TFJMUser", - related_name="pools", - verbose_name=_("juries"), - ) - - extra_access_token = models.CharField( - max_length=64, - default="", - verbose_name=_("extra access token"), - help_text=_("Let other users access to the pool data without logging in."), - ) - - @property - def problems(self): - """ - Get problem numbers of the sent solutions as a list of integers. - """ - return list(d["problem"] for d in self.solutions.values("problem").all()) - - @property - def tournament(self): - """ - Get the concerned tournament. - We assume that the pool is correct, so all solutions belong to the same tournament. - """ - return self.solutions.first().tournament - - @property - def syntheses(self): - """ - Get the syntheses of the teams that are in this pool, for the correct round. - """ - from member.models import Synthesis - return Synthesis.objects.filter(team__in=self.teams.all(), round=self.round, final=self.tournament.final) - - def save(self, **kwargs): - if not self.extra_access_token: - alphabet = "0123456789abcdefghijklmnopqrstuvwxyz0123456789" - code = "".join(random.choice(alphabet) for _ in range(64)) - self.extra_access_token = code - super().save(**kwargs) - - class Meta: - verbose_name = _("pool") - verbose_name_plural = _("pools") - - -class Payment(models.Model): - """ - Store some information about payments, to recover data. - TODO: handle it... - """ - user = models.OneToOneField( - 'member.TFJMUser', - on_delete=models.CASCADE, - related_name="payment", - verbose_name=_("user"), - ) - - team = models.ForeignKey( - Team, - on_delete=models.CASCADE, - related_name="payments", - verbose_name=_("team"), - ) - - method = models.CharField( - max_length=16, - choices=[ - ("not_paid", _("Not paid")), - ("credit_card", _("Credit card")), - ("check", _("Bank check")), - ("transfer", _("Bank transfer")), - ("cash", _("Cash")), - ("scholarship", _("Scholarship")), - ], - default="not_paid", - verbose_name=_("payment method"), - ) - - validation_status = models.CharField( - max_length=8, - choices=[ - ("0invalid", _("Registration not validated")), - ("1waiting", _("Waiting for validation")), - ("2valid", _("Registration validated")), - ], - verbose_name=_("validation status"), - ) - - class Meta: - verbose_name = _("payment") - verbose_name_plural = _("payments") - - def __str__(self): - return _("Payment of {user}").format(str(self.user)) diff --git a/apps/tournament/tables.py b/apps/tournament/tables.py deleted file mode 100644 index 33e39a1..0000000 --- a/apps/tournament/tables.py +++ /dev/null @@ -1,164 +0,0 @@ -import django_tables2 as tables -from django.urls import reverse_lazy -from django.utils.html import format_html -from django.utils.translation import gettext_lazy as _ -from django_tables2 import A - -from member.models import Solution, Synthesis -from .models import Tournament, Team, Pool - - -class TournamentTable(tables.Table): - """ - List all tournaments. - """ - - name = tables.LinkColumn( - "tournament:detail", - args=[A("pk")], - ) - - date_start = tables.Column( - verbose_name=_("dates").capitalize(), - ) - - def render_date_start(self, record): - return _("From {start:%b %d %Y} to {end:%b %d %Y}").format(start=record.date_start, end=record.date_end) - - class Meta: - model = Tournament - fields = ("name", "date_start", "date_inscription", "date_solutions", "size", ) - attrs = { - 'class': 'table table-condensed table-striped table-hover' - } - order_by = ('date_start', 'name',) - - -class TeamTable(tables.Table): - """ - Table of some teams. Can be filtered with a queryset (for example, teams of a tournament) - """ - - name = tables.LinkColumn( - "tournament:team_detail", - args=[A("pk")], - ) - - class Meta: - model = Team - fields = ("name", "trigram", "validation_status", ) - attrs = { - 'class': 'table table-condensed table-striped table-hover' - } - order_by = ('-validation_status', 'trigram',) - - -class SolutionTable(tables.Table): - """ - Display a table of some solutions. - """ - - team = tables.LinkColumn( - "tournament:team_detail", - args=[A("team.pk")], - ) - - tournament = tables.LinkColumn( - "tournament:detail", - args=[A("tournament.pk")], - accessor=A("tournament"), - order_by=("team__tournament__date_start", "team__tournament__name",), - verbose_name=_("Tournament"), - ) - - file = tables.LinkColumn( - "document", - args=[A("file")], - attrs={ - "a": { - "data-turbolinks": "false", - } - } - ) - - def render_file(self): - return _("Download") - - class Meta: - model = Solution - fields = ("team", "tournament", "problem", "uploaded_at", "file", ) - attrs = { - 'class': 'table table-condensed table-striped table-hover' - } - - -class SynthesisTable(tables.Table): - """ - Display a table of some syntheses. - """ - - team = tables.LinkColumn( - "tournament:team_detail", - args=[A("team.pk")], - ) - - tournament = tables.LinkColumn( - "tournament:detail", - args=[A("tournament.pk")], - accessor=A("tournament"), - order_by=("team__tournament__date_start", "team__tournament__name",), - verbose_name=_("tournament"), - ) - - file = tables.LinkColumn( - "document", - args=[A("file")], - attrs={ - "a": { - "data-turbolinks": "false", - } - } - ) - - def render_file(self): - return _("Download") - - class Meta: - model = Synthesis - fields = ("team", "tournament", "round", "source", "uploaded_at", "file", ) - attrs = { - 'class': 'table table-condensed table-striped table-hover' - } - - -class PoolTable(tables.Table): - """ - Display a table of some pools. - """ - - problems = tables.Column( - verbose_name=_("Problems"), - orderable=False, - ) - - tournament = tables.LinkColumn( - "tournament:detail", - args=[A("tournament.pk")], - verbose_name=_("Tournament"), - order_by=("teams__tournament__date_start", "teams__tournament__name",), - ) - - def render_teams(self, record, value): - return format_html('{trigrams}', - url=reverse_lazy('tournament:pool_detail', args=(record.pk,)), - trigrams=", ".join(team.trigram for team in value.all())) - - def render_problems(self, value): - return ", ".join([str(pb) for pb in value]) - - class Meta: - model = Pool - fields = ("teams", "tournament", "problems", "round", ) - attrs = { - 'class': 'table table-condensed table-striped table-hover' - } diff --git a/apps/tournament/urls.py b/apps/tournament/urls.py deleted file mode 100644 index 364f23d..0000000 --- a/apps/tournament/urls.py +++ /dev/null @@ -1,24 +0,0 @@ -from django.urls import path - -from .views import TournamentListView, TournamentCreateView, TournamentDetailView, TournamentUpdateView, \ - TeamDetailView, TeamUpdateView, AddOrganizerView, SolutionsView, SolutionsOrgaListView, SynthesesView, \ - SynthesesOrgaListView, PoolListView, PoolCreateView, PoolDetailView - -app_name = "tournament" - -urlpatterns = [ - path('list/', TournamentListView.as_view(), name="list"), - path("add/", TournamentCreateView.as_view(), name="add"), - path('/', TournamentDetailView.as_view(), name="detail"), - path('/update/', TournamentUpdateView.as_view(), name="update"), - path('team//', TeamDetailView.as_view(), name="team_detail"), - path('team//update/', TeamUpdateView.as_view(), name="team_update"), - path("add-organizer/", AddOrganizerView.as_view(), name="add_organizer"), - path("solutions/", SolutionsView.as_view(), name="solutions"), - path("all-solutions/", SolutionsOrgaListView.as_view(), name="all_solutions"), - path("syntheses/", SynthesesView.as_view(), name="syntheses"), - path("all_syntheses/", SynthesesOrgaListView.as_view(), name="all_syntheses"), - path("pools/", PoolListView.as_view(), name="pools"), - path("pool/add/", PoolCreateView.as_view(), name="create_pool"), - path("pool//", PoolDetailView.as_view(), name="pool_detail"), -] diff --git a/apps/tournament/views.py b/apps/tournament/views.py deleted file mode 100644 index 450351f..0000000 --- a/apps/tournament/views.py +++ /dev/null @@ -1,662 +0,0 @@ -import random -import zipfile -from datetime import timedelta -from io import BytesIO - -from django.contrib.auth.mixins import LoginRequiredMixin, AccessMixin -from django.core.exceptions import PermissionDenied -from django.core.mail import send_mail -from django.db.models import Q -from django.http import HttpResponse -from django.shortcuts import redirect -from django.template.loader import render_to_string -from django.urls import reverse_lazy -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ -from django.views.generic import DetailView, CreateView, UpdateView -from django.views.generic.edit import BaseFormView -from django_tables2.views import SingleTableView -from member.models import TFJMUser, Solution, Synthesis - -from .forms import TournamentForm, OrganizerForm, SolutionForm, SynthesisForm, TeamForm, PoolForm -from .models import Tournament, Team, Pool -from .tables import TournamentTable, TeamTable, SolutionTable, SynthesisTable, PoolTable - - -class AdminMixin(LoginRequiredMixin): - """ - If a view extends this mixin, then the view will be only accessible to administrators. - """ - - def dispatch(self, request, *args, **kwargs): - if not request.user.is_authenticated or not request.user.admin: - raise PermissionDenied - return super().dispatch(request, *args, **kwargs) - - -class OrgaMixin(AccessMixin): - """ - If a view extends this mixin, then the view will be only accessible to administrators or organizers. - """ - - def dispatch(self, request, *args, **kwargs): - if not request.user.is_authenticated and not request.session["extra_access_token"]: - return self.handle_no_permission() - elif request.user.is_authenticated and not request.user.organizes: - raise PermissionDenied - return super().dispatch(request, *args, **kwargs) - - -class TeamMixin(LoginRequiredMixin): - """ - If a view extends this mixin, then the view will be only accessible to users that are registered in a team. - """ - - def dispatch(self, request, *args, **kwargs): - if not request.user.is_authenticated or not request.user.team: - raise PermissionDenied - return super().dispatch(request, *args, **kwargs) - - -class TournamentListView(SingleTableView): - """ - Display the list of all tournaments, ordered by start date then name. - """ - - model = Tournament - table_class = TournamentTable - extra_context = dict(title=_("Tournaments list"),) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - team_users = TFJMUser.objects.filter(Q(team__isnull=False) | Q(role="admin") | Q(role="organizer"))\ - .order_by('-role') - valid_team_users = team_users.filter( - Q(team__validation_status="2valid") | Q(role="admin") | Q(role="organizer")) - - context["team_users_emails"] = [user.email for user in team_users] - context["valid_team_users_emails"] = [user.email for user in valid_team_users] - - return context - - -class TournamentCreateView(AdminMixin, CreateView): - """ - Create a tournament. Only accessible to admins. - """ - - model = Tournament - form_class = TournamentForm - extra_context = dict(title=_("Add tournament"),) - - def get_success_url(self): - return reverse_lazy('tournament:detail', args=(self.object.pk,)) - - -class TournamentDetailView(DetailView): - """ - Display the detail of a tournament. - Accessible to all, including not authenticated users. - """ - - model = Tournament - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - context["title"] = _("Tournament of {name}").format(name=self.object.name) - - if self.object.final: - team_users = TFJMUser.objects.filter(team__selected_for_final=True) - valid_team_users = team_users - else: - team_users = TFJMUser.objects.filter( - Q(team__tournament=self.object) - | Q(organized_tournaments=self.object)).order_by('role') - valid_team_users = team_users.filter( - Q(team__validation_status="2valid") - | Q(role="admin") - | Q(organized_tournaments=self.object)) - - context["team_users_emails"] = [user.email for user in team_users] - context["valid_team_users_emails"] = [user.email for user in valid_team_users] - - context["teams"] = TeamTable(self.object.teams.all()) - - return context - - -class TournamentUpdateView(OrgaMixin, UpdateView): - """ - Update the data of a tournament. - Reserved to admins and organizers of the tournament. - """ - - def dispatch(self, request, *args, **kwargs): - """ - Restrict the view to organizers of tournaments, then process the request. - """ - if self.request.user.role == "1volunteer" and self.request.user not in self.get_object().organizers.all(): - raise PermissionDenied - return super().dispatch(request, *args, **kwargs) - - model = Tournament - form_class = TournamentForm - extra_context = dict(title=_("Update tournament"),) - - def get_success_url(self): - return reverse_lazy('tournament:detail', args=(self.object.pk,)) - - -class TeamDetailView(LoginRequiredMixin, DetailView): - """ - View the detail of a team. - Restricted to this team, admins and organizers of its tournament. - """ - model = Team - - def dispatch(self, request, *args, **kwargs): - """ - Protect the page and process the request. - """ - if not request.user.is_authenticated or \ - (not request.user.admin and self.request.user not in self.get_object().tournament.organizers.all() - and not (self.get_object().selected_for_final - and request.user in Tournament.get_final().organizers.all()) - and self.get_object() != request.user.team): - raise PermissionDenied - return super().dispatch(request, *args, **kwargs) - - def post(self, request, *args, **kwargs): - """ - Process POST requests. Supported requests: - - get the solutions of the team as a ZIP archive - - a user leaves its team (if the composition is not validated yet) - - the team requests the validation - - Organizers can validate or invalidate the request - - Admins can delete teams - - Admins can select teams for the final tournament - """ - team = self.get_object() - if "zip" in request.POST: - solutions = team.solutions.all() - - out = BytesIO() - zf = zipfile.ZipFile(out, "w") - - for solution in solutions: - zf.write(solution.file.path, str(solution) + ".pdf") - - zf.close() - - resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed") - resp['Content-Disposition'] = 'attachment; filename={}'\ - .format(_("Solutions for team {team}.zip") - .format(team=str(team)).replace(" ", "%20")) - return resp - elif "leave" in request.POST and request.user.participates: - request.user.team = None - request.user.save() - if not team.users.exists(): - team.delete() - return redirect('tournament:detail', pk=team.tournament.pk) - elif "request_validation" in request.POST and request.user.participates and team.can_validate: - team.validation_status = "1waiting" - team.save() - team.tournament.send_mail_to_organizers("request_validation", "Demande de validation TFJM²", team=team) - return redirect('tournament:team_detail', pk=team.pk) - elif "validate" in request.POST and request.user.organizes: - team.validation_status = "2valid" - team.save() - team.send_mail("validate_team", "Équipe validée TFJM²") - return redirect('tournament:team_detail', pk=team.pk) - elif "invalidate" in request.POST and request.user.organizes: - team.validation_status = "0invalid" - team.save() - team.send_mail("unvalidate_team", "Équipe non validée TFJM²") - return redirect('tournament:team_detail', pk=team.pk) - elif "delete" in request.POST and request.user.organizes: - team.delete() - return redirect('tournament:detail', pk=team.tournament.pk) - elif "select_final" in request.POST and request.user.admin and not team.selected_for_final and team.pools: - # We copy all solutions for solutions for the final - for solution in team.solutions.all(): - alphabet = "0123456789abcdefghijklmnopqrstuvwxyz0123456789" - id = "" - for i in range(64): - id += random.choice(alphabet) - with solution.file.open("rb") as source: - with open("/code/media/" + id, "wb") as dest: - for chunk in source.chunks(): - dest.write(chunk) - new_sol = Solution( - file=id, - team=team, - problem=solution.problem, - final=True, - ) - new_sol.save() - team.selected_for_final = True - team.save() - team.send_mail("select_for_final", "Sélection pour la finale, félicitations ! - TFJM²", - final=Tournament.get_final()) - return redirect('tournament:team_detail', pk=team.pk) - - return self.get(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - context["title"] = _("Information about team") - context["ordered_solutions"] = self.object.solutions.order_by('final', 'problem',).all() - context["team_users_emails"] = [user.email for user in self.object.users.all()] - - return context - - -class TeamUpdateView(LoginRequiredMixin, UpdateView): - """ - Update the information about a team. - Team members, admins and organizers are allowed to do this. - """ - - model = Team - form_class = TeamForm - extra_context = dict(title=_("Update team"),) - - def dispatch(self, request, *args, **kwargs): - if not request.user.admin and self.request.user not in self.get_object().tournament.organizers.all() \ - and self.get_object() != self.request.user.team: - raise PermissionDenied - return super().dispatch(request, *args, **kwargs) - - -class AddOrganizerView(AdminMixin, CreateView): - """ - Add a new organizer account. No password is created, the user should reset its password using the link - sent by mail. Only name and email are requested. - Only admins are granted to do this. - """ - - model = TFJMUser - form_class = OrganizerForm - extra_context = dict(title=_("Add organizer"),) - template_name = "tournament/add_organizer.html" - - def form_valid(self, form): - user = form.instance - msg = render_to_string("mail_templates/add_organizer.txt", context=dict(user=user)) - msg_html = render_to_string("mail_templates/add_organizer.html", context=dict(user=user)) - send_mail('Organisateur du TFJM² 2020', msg, 'contact@tfjm.org', [user.email], html_message=msg_html) - return super().form_valid(form) - - def get_success_url(self): - return reverse_lazy('index') - - -class SolutionsView(TeamMixin, BaseFormView, SingleTableView): - """ - Upload and view solutions for a team. - """ - - model = Solution - table_class = SolutionTable - form_class = SolutionForm - template_name = "tournament/solutions_list.html" - extra_context = dict(title=_("Solutions")) - - def post(self, request, *args, **kwargs): - if "zip" in request.POST: - solutions = request.user.team.solutions - - out = BytesIO() - zf = zipfile.ZipFile(out, "w") - - for solution in solutions: - zf.write(solution.file.path, str(solution) + ".pdf") - - zf.close() - - resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed") - resp['Content-Disposition'] = 'attachment; filename={}'\ - .format(_("Solutions for team {team}.zip") - .format(team=str(request.user.team)).replace(" ", "%20")) - return resp - - return super().post(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - self.object_list = self.get_queryset() - context = super().get_context_data(**kwargs) - context["now"] = timezone.now() - context["real_deadline"] = self.request.user.team.future_tournament.date_solutions + timedelta(minutes=30) - return context - - def get_queryset(self): - qs = super().get_queryset().filter(team=self.request.user.team) - return qs.order_by('final', 'team__tournament__date_start', 'team__tournament__name', 'team__trigram', - 'problem',) - - def form_valid(self, form): - solution = form.instance - solution.team = self.request.user.team - solution.final = solution.team.selected_for_final - - if timezone.now() > solution.tournament.date_solutions + timedelta(minutes=30): - form.add_error('file', _("You can't publish your solution anymore. Deadline: {date:%m-%d-%Y %H:%M}.") - .format(date=timezone.localtime(solution.tournament.date_solutions))) - return super().form_invalid(form) - - prev_sol = Solution.objects.filter(problem=solution.problem, team=solution.team, final=solution.final) - for sol in prev_sol.all(): - sol.delete() - alphabet = "0123456789abcdefghijklmnopqrstuvwxyz0123456789" - id = "" - for i in range(64): - id += random.choice(alphabet) - solution.file.name = id - solution.save() - - return super().form_valid(form) - - def get_success_url(self): - return reverse_lazy("tournament:solutions") - - -class SolutionsOrgaListView(OrgaMixin, SingleTableView): - """ - View all solutions sent by teams for the organized tournaments. Juries can view solutions of their pools. - Organizers can download a ZIP archive for each organized tournament. - """ - - model = Solution - table_class = SolutionTable - template_name = "tournament/solutions_orga_list.html" - extra_context = dict(title=_("All solutions")) - - def post(self, request, *args, **kwargs): - if "tournament_zip" in request.POST: - tournament = Tournament.objects.get(pk=int(request.POST["tournament_zip"])) - solutions = tournament.solutions - if not request.user.admin and request.user not in tournament.organizers.all(): - raise PermissionDenied - - out = BytesIO() - zf = zipfile.ZipFile(out, "w") - - for solution in solutions: - zf.write(solution.file.path, str(solution) + ".pdf") - - zf.close() - - resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed") - resp['Content-Disposition'] = 'attachment; filename={}'\ - .format(_("Solutions for tournament {tournament}.zip") - .format(tournament=str(tournament)).replace(" ", "%20")) - return resp - - return self.get(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - if self.request.user.is_authenticated: - context["tournaments"] = \ - Tournament.objects if self.request.user.admin else self.request.user.organized_tournaments - - return context - - def get_queryset(self): - qs = super().get_queryset() - if self.request.user.is_authenticated and not self.request.user.admin: - if self.request.user in Tournament.get_final().organizers.all(): - qs = qs.filter(Q(team__tournament__organizers=self.request.user) | Q(pools__juries=self.request.user) - | Q(final=True)) - else: - qs = qs.filter(Q(team__tournament__organizers=self.request.user) | Q(pools__juries=self.request.user)) - elif not self.request.user.is_authenticated: - qs = qs.filter(pools__extra_access_token=self.request.session["extra_access_token"]) - return qs.order_by('final', 'team__tournament__date_start', 'team__tournament__name', 'team__trigram', - 'problem',).distinct() - - -class SynthesesView(TeamMixin, BaseFormView, SingleTableView): - """ - Upload and view syntheses for a team. - """ - model = Synthesis - table_class = SynthesisTable - form_class = SynthesisForm - template_name = "tournament/syntheses_list.html" - extra_context = dict(title=_("Syntheses")) - - def post(self, request, *args, **kwargs): - if "zip" in request.POST: - syntheses = request.user.team.syntheses - - out = BytesIO() - zf = zipfile.ZipFile(out, "w") - - for synthesis in syntheses: - zf.write(synthesis.file.path, str(synthesis) + ".pdf") - - zf.close() - - resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed") - resp['Content-Disposition'] = 'attachment; filename={}'\ - .format(_("Syntheses for team {team}.zip") - .format(team=str(request.user.team)).replace(" ", "%20")) - return resp - - return super().post(request, *args, **kwargs) - - def get_queryset(self): - qs = super().get_queryset().filter(team=self.request.user.team) - return qs.order_by('final', 'team__tournament__date_start', 'team__tournament__name', 'team__trigram', - 'round', 'source',) - - def get_context_data(self, **kwargs): - self.object_list = self.get_queryset() - context = super().get_context_data(**kwargs) - context["now"] = timezone.now() - context["real_deadline_1"] = self.request.user.team.future_tournament.date_syntheses + timedelta(minutes=30) - context["real_deadline_2"] = self.request.user.team.future_tournament.date_syntheses_2 + timedelta(minutes=30) - return context - - def form_valid(self, form): - synthesis = form.instance - synthesis.team = self.request.user.team - synthesis.final = synthesis.team.selected_for_final - - if synthesis.round == '1' and timezone.now() > (synthesis.tournament.date_syntheses + timedelta(minutes=30)): - form.add_error('file', _("You can't publish your synthesis anymore for the first round." - " Deadline: {date:%m-%d-%Y %H:%M}.") - .format(date=timezone.localtime(synthesis.tournament.date_syntheses))) - return super().form_invalid(form) - - if synthesis.round == '2' and timezone.now() > synthesis.tournament.date_syntheses_2 + timedelta(minutes=30): - form.add_error('file', _("You can't publish your synthesis anymore for the second round." - " Deadline: {date:%m-%d-%Y %H:%M}.") - .format(date=timezone.localtime(synthesis.tournament.date_syntheses_2))) - return super().form_invalid(form) - - prev_syn = Synthesis.objects.filter(team=synthesis.team, round=synthesis.round, source=synthesis.source, - final=synthesis.final) - for syn in prev_syn.all(): - syn.delete() - alphabet = "0123456789abcdefghijklmnopqrstuvwxyz0123456789" - id = "" - for i in range(64): - id += random.choice(alphabet) - synthesis.file.name = id - synthesis.save() - - return super().form_valid(form) - - def get_success_url(self): - return reverse_lazy("tournament:syntheses") - - -class SynthesesOrgaListView(OrgaMixin, SingleTableView): - """ - View all syntheses sent by teams for the organized tournaments. Juries can view syntheses of their pools. - Organizers can download a ZIP archive for each organized tournament. - """ - model = Synthesis - table_class = SynthesisTable - template_name = "tournament/syntheses_orga_list.html" - extra_context = dict(title=_("All syntheses")) - - def post(self, request, *args, **kwargs): - if "tournament_zip" in request.POST: - tournament = Tournament.objects.get(pk=request.POST["tournament_zip"]) - syntheses = tournament.syntheses - if not request.user.admin and request.user not in tournament.organizers.all(): - raise PermissionDenied - - out = BytesIO() - zf = zipfile.ZipFile(out, "w") - - for synthesis in syntheses: - zf.write(synthesis.file.path, str(synthesis) + ".pdf") - - zf.close() - - resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed") - resp['Content-Disposition'] = 'attachment; filename={}'\ - .format(_("Syntheses for tournament {tournament}.zip") - .format(tournament=str(tournament)).replace(" ", "%20")) - return resp - - return self.get(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - if self.request.user.is_authenticated: - context["tournaments"] = \ - Tournament.objects if self.request.user.admin else self.request.user.organized_tournaments - - return context - - def get_queryset(self): - qs = super().get_queryset() - if self.request.user.is_authenticated and not self.request.user.admin: - if self.request.user in Tournament.get_final().organizers.all(): - qs = qs.filter(Q(team__tournament__organizers=self.request.user) - | Q(team__pools__juries=self.request.user) - | Q(final=True)) - else: - qs = qs.filter(Q(team__tournament__organizers=self.request.user) - | Q(team__pools__juries=self.request.user)) - elif not self.request.user.is_authenticated: - pool = Pool.objects.filter(extra_access_token=self.request.session["extra_access_token"]) - if pool.exists(): - pool = pool.get() - qs = qs.filter(team__pools=pool, final=pool.tournament.final) - else: - qs = qs.none() - return qs.order_by('final', 'team__tournament__date_start', 'team__tournament__name', 'team__trigram', - 'round', 'source',).distinct() - - -class PoolListView(SingleTableView): - """ - View the list of visible pools. - Admins see all, juries see their own pools, organizers see the pools of their tournaments. - """ - model = Pool - table_class = PoolTable - extra_context = dict(title=_("Pools")) - - def get_queryset(self): - qs = super().get_queryset() - user = self.request.user - if user.is_authenticated: - if not user.admin and user.organizes: - qs = qs.filter(Q(juries=user) | Q(teams__tournament__organizers=user)) - elif user.participates: - qs = qs.filter(teams=user.team) - else: - qs = qs.filter(extra_access_token=self.request.session["extra_access_token"]) - qs = qs.distinct().order_by('id') - return qs - - -class PoolCreateView(AdminMixin, CreateView): - """ - Create a pool manually. - This page should not be used: prefer send automatically data from the drawing bot. - """ - model = Pool - form_class = PoolForm - extra_context = dict(title=_("Create pool")) - - def get_success_url(self): - return reverse_lazy("tournament:pools") - - -class PoolDetailView(DetailView): - """ - See the detail of a pool. - Teams and juries can download here defended solutions of the pool. - If this is the second round, teams can't download solutions of the other teams before the date when they - should be available. - Juries see also syntheses. They see of course solutions immediately. - This is also true for organizers and admins. - All can be downloaded as a ZIP archive. - """ - model = Pool - extra_context = dict(title=_("Pool detail")) - - def get_queryset(self): - qs = super().get_queryset() - user = self.request.user - if user.is_authenticated: - if not user.admin and user.organizes: - qs = qs.filter(Q(juries=user) | Q(teams__tournament__organizers=user)) - elif user.participates: - qs = qs.filter(teams=user.team) - else: - qs = qs.filter(extra_access_token=self.request.session["extra_access_token"]) - return qs.distinct() - - def post(self, request, *args, **kwargs): - user = request.user - pool = self.get_object() - - if "solutions_zip" in request.POST: - if user.is_authenticated and user.participates and pool.round == 2\ - and pool.tournament.date_solutions_2 > timezone.now(): - raise PermissionDenied - - out = BytesIO() - zf = zipfile.ZipFile(out, "w") - - for solution in pool.solutions.all(): - zf.write(solution.file.path, str(solution) + ".pdf") - - zf.close() - - resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed") - resp['Content-Disposition'] = 'attachment; filename={}' \ - .format(_("Solutions of a pool for the round {round} of the tournament {tournament}.zip") - .format(round=pool.round, tournament=str(pool.tournament)).replace(" ", "%20")) - return resp - elif "syntheses_zip" in request.POST and (not user.is_authenticated or user.organizes): - out = BytesIO() - zf = zipfile.ZipFile(out, "w") - - for synthesis in pool.syntheses.all(): - zf.write(synthesis.file.path, str(synthesis) + ".pdf") - - zf.close() - - resp = HttpResponse(out.getvalue(), content_type="application/x-zip-compressed") - resp['Content-Disposition'] = 'attachment; filename={}' \ - .format(_("Syntheses of a pool for the round {round} of the tournament {tournament}.zip") - .format(round=pool.round, tournament=str(pool.tournament)).replace(" ", "%20")) - return resp - - return self.get(request, *args, **kwargs) diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index ffb586e..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -python manage.py compilemessages -python manage.py migrate - -nginx - -if [ "$TFJM_STAGE" = "prod" ]; then - gunicorn -b 0.0.0.0:8000 --workers=2 --threads=4 --worker-class=gthread tfjm.wsgi --access-logfile '-' --error-logfile '-'; -else - ./manage.py runserver 0.0.0.0:8000; -fi diff --git a/index.html b/index.html deleted file mode 100644 index da8c197..0000000 --- a/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Erreur - - - -Le mode Rewrite n'est pas activé. - - \ No newline at end of file diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po deleted file mode 100644 index 7c2977f..0000000 --- a/locale/fr/LC_MESSAGES/django.po +++ /dev/null @@ -1,1221 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR yohann.danello@animath.fr, 2020. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: TFJM2\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-25 18:23+0200\n" -"PO-Revision-Date: 2020-04-29 02:30+0000\n" -"Last-Translator: Yohann D'ANELLO \n" -"Language-Team: fr \n" -"Language: fr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" - -#: apps/api/apps.py:10 -msgid "API" -msgstr "API" - -#: apps/member/apps.py:10 -msgid "member" -msgstr "membre" - -#: apps/member/forms.py:18 -msgid "Choose a role..." -msgstr "Choisir un rôle ..." - -#: apps/member/forms.py:19 apps/member/models.py:138 -msgid "Participant" -msgstr "Participant" - -#: apps/member/forms.py:20 apps/member/models.py:137 -msgid "Coach" -msgstr "Encadrant" - -#: apps/member/models.py:21 templates/member/tfjmuser_detail.html:35 -msgid "email" -msgstr "Adresse électronique" - -#: apps/member/models.py:22 -msgid "This should be valid and will be controlled." -msgstr "Elle doit être valide et sera contrôlée." - -#: apps/member/models.py:30 apps/member/models.py:244 apps/member/models.py:263 -#: apps/member/models.py:306 apps/tournament/models.py:286 -#: apps/tournament/models.py:400 templates/member/tfjmuser_detail.html:16 -msgid "team" -msgstr "équipe" - -#: apps/member/models.py:31 -msgid "Concerns only coaches and participants." -msgstr "Concerne uniquement les encadrants et participants." - -#: apps/member/models.py:37 templates/member/tfjmuser_detail.html:21 -msgid "birth date" -msgstr "date de naissance" - -#: apps/member/models.py:45 -msgid "Male" -msgstr "Homme" - -#: apps/member/models.py:46 -msgid "Female" -msgstr "Femme" - -#: apps/member/models.py:47 -msgid "Non binary" -msgstr "Non binaire" - -#: apps/member/models.py:49 templates/member/tfjmuser_detail.html:26 -msgid "gender" -msgstr "genre" - -#: apps/member/models.py:56 templates/member/tfjmuser_detail.html:31 -msgid "address" -msgstr "adresse" - -#: apps/member/models.py:62 -msgid "postal code" -msgstr "code postal" - -#: apps/member/models.py:69 -msgid "city" -msgstr "ville" - -#: apps/member/models.py:76 -msgid "country" -msgstr "pays" - -#: apps/member/models.py:84 templates/member/tfjmuser_detail.html:39 -msgid "phone number" -msgstr "numéro de téléphone" - -#: apps/member/models.py:91 templates/member/tfjmuser_detail.html:44 -msgid "school" -msgstr "école" - -#: apps/member/models.py:97 -msgid "Seconde or less" -msgstr "Seconde ou inférieur" - -#: apps/member/models.py:98 -msgid "Première" -msgstr "Première" - -#: apps/member/models.py:99 -msgid "Terminale" -msgstr "Terminale" - -#: apps/member/models.py:110 templates/member/tfjmuser_detail.html:51 -msgid "responsible name" -msgstr "nom du responsable" - -#: apps/member/models.py:117 templates/member/tfjmuser_detail.html:56 -msgid "responsible phone" -msgstr "téléphone du responsable" - -#: apps/member/models.py:123 templates/member/tfjmuser_detail.html:61 -msgid "responsible email" -msgstr "email du responsable" - -#: apps/member/models.py:129 apps/tournament/models.py:45 -#: templates/member/tfjmuser_detail.html:67 -#: templates/tournament/tournament_detail.html:42 -msgid "description" -msgstr "description" - -#: apps/member/models.py:135 -msgid "Admin" -msgstr "Administrateur" - -#: apps/member/models.py:136 -msgid "Organizer" -msgstr "Organisateur" - -#: apps/member/models.py:144 apps/tournament/models.py:90 -#: apps/tournament/models.py:215 -msgid "year" -msgstr "année" - -#: apps/member/models.py:171 apps/member/models.py:214 -#: apps/tournament/models.py:393 -msgid "user" -msgstr "utilisateur" - -#: apps/member/models.py:172 -msgid "users" -msgstr "utilisateurs" - -#: apps/member/models.py:189 -msgid "file" -msgstr "fichier" - -#: apps/member/models.py:194 -msgid "uploaded at" -msgstr "téléversé le" - -#: apps/member/models.py:198 -msgid "document" -msgstr "document" - -#: apps/member/models.py:199 -msgid "documents" -msgstr "documents" - -#: apps/member/models.py:220 -msgid "Parental consent" -msgstr "Autorisation parentale" - -#: apps/member/models.py:221 -msgid "Photo consent" -msgstr "Autorisation de droit à l'image" - -#: apps/member/models.py:222 -msgid "Sanitary plug" -msgstr "Fiche sanitaire" - -#: apps/member/models.py:223 apps/tournament/models.py:411 -msgid "Scholarship" -msgstr "Bourse" - -#: apps/member/models.py:225 -msgid "type" -msgstr "type" - -#: apps/member/models.py:229 -msgid "authorization" -msgstr "autorisation" - -#: apps/member/models.py:230 -msgid "authorizations" -msgstr "autorisations" - -#: apps/member/models.py:233 -#, python-brace-format -msgid "{authorization} for user {user}" -msgstr "{authorization} pour l'utilisateur {user}" - -#: apps/member/models.py:248 -msgid "motivation letter" -msgstr "lettre de motivation" - -#: apps/member/models.py:249 -msgid "motivation letters" -msgstr "lettres de motivation" - -#: apps/member/models.py:252 -#, python-brace-format -msgid "Motivation letter of team {team} ({trigram})" -msgstr "Lettre de motivation de l'équipe {team} ({trigram})" - -#: apps/member/models.py:267 -msgid "problem" -msgstr "problème" - -#: apps/member/models.py:272 -msgid "final solution" -msgstr "solution pour la finale" - -#: apps/member/models.py:285 -msgid "solution" -msgstr "solution" - -#: apps/member/models.py:286 apps/tournament/models.py:325 -msgid "solutions" -msgstr "solutions" - -#: apps/member/models.py:291 -#, python-brace-format -msgid "Solution of team {trigram} for problem {problem} for final" -msgstr "" -"Solution de l'équipe {trigram} pour le problème {problem} pour la finale" - -#: apps/member/models.py:294 -#, python-brace-format -msgid "Solution of team {trigram} for problem {problem}" -msgstr "Solution de l'équipe {trigram} pour le problème {problem}" - -#: apps/member/models.py:312 -msgid "Opponent" -msgstr "Opposant" - -#: apps/member/models.py:313 -msgid "Rapporteur" -msgstr "Rapporteur" - -#: apps/member/models.py:315 -msgid "source" -msgstr "source" - -#: apps/member/models.py:320 apps/tournament/models.py:330 -msgid "Round 1" -msgstr "Tour 1" - -#: apps/member/models.py:321 apps/tournament/models.py:331 -msgid "Round 2" -msgstr "Tour 2" - -#: apps/member/models.py:323 apps/tournament/models.py:333 -#: templates/tournament/pool_detail.html:18 -msgid "round" -msgstr "tour" - -#: apps/member/models.py:328 -msgid "final synthesis" -msgstr "synthèse pour la finale" - -#: apps/member/models.py:341 -msgid "synthesis" -msgstr "synthèse" - -#: apps/member/models.py:342 -msgid "syntheses" -msgstr "synthèses" - -#: apps/member/models.py:346 -#, python-brace-format -msgid "" -"Synthesis of team {trigram} that is {source} for the round {round} of " -"tournament {tournament}" -msgstr "" -"Synthèse de l'équipe {trigram} qui est {source} pour le tour {round} du " -"tournoi {tournament}" - -#: apps/member/models.py:358 -msgid "key" -msgstr "clé" - -#: apps/member/models.py:363 -msgid "value" -msgstr "valeur" - -#: apps/member/models.py:367 -msgid "configuration" -msgstr "configuration" - -#: apps/member/models.py:368 -msgid "configurations" -msgstr "configurations" - -#: apps/member/views.py:105 apps/member/views.py:145 -msgid "You can't organize and participate at the same time." -msgstr "Vous ne pouvez pas organiser et participer en même temps." - -#: apps/member/views.py:109 apps/member/views.py:149 -msgid "You are already in a team." -msgstr "Vous êtes déjà dans une équipe." - -#: apps/member/views.py:153 -msgid "This team is full of coachs." -msgstr "Cette équipe est pleine en encadrants." - -#: apps/member/views.py:157 -msgid "This team is full of participants." -msgstr "Cette équipe est pleine en participants." - -#: apps/member/views.py:161 -msgid "This team is already validated or waiting for validation." -msgstr "L'équipe est déjà en attente de validation." - -#: apps/member/views.py:194 -#, python-format -msgid "No %(verbose_name)s found matching the query" -msgstr "" - -#: apps/member/views.py:244 templates/base.html:81 -msgid "All profiles" -msgstr "Tous les profils" - -#: apps/member/views.py:256 templates/base.html:80 -msgid "Orphaned profiles" -msgstr "Profils orphelins" - -#: apps/member/views.py:268 apps/tournament/forms.py:23 templates/base.html:83 -msgid "Organizers" -msgstr "Organisateurs" - -#: apps/tournament/apps.py:10 apps/tournament/models.py:135 -#: apps/tournament/models.py:183 apps/tournament/tables.py:110 -#: templates/tournament/pool_detail.html:21 -#: templates/tournament/team_detail.html:21 -msgid "tournament" -msgstr "tournoi" - -#: apps/tournament/forms.py:31 -msgid "This tournament already exists." -msgstr "Ce tournoi existe déjà." - -#: apps/tournament/forms.py:33 -msgid "The final tournament was already defined." -msgstr "Le tournoi de la finale est déjà défini." - -#: apps/tournament/forms.py:65 -msgid "This organizer already exist." -msgstr "Cet organisateur existe déjà." - -#: apps/tournament/forms.py:94 -msgid "The trigram must be composed of three upcase letters." -msgstr "Le trigramme doit être composé de trois lettres en majuscules." - -#: apps/tournament/forms.py:98 -msgid "This trigram is already used." -msgstr "Ce trigramme est déjà utilisé." - -#: apps/tournament/forms.py:101 -msgid "This name is already used." -msgstr "Ce nom est déjà utilisé." - -#: apps/tournament/forms.py:104 -msgid "This tournament is already closed." -msgstr "Ce tournoi est déjà fermé." - -#: apps/tournament/forms.py:115 -msgid "Access code" -msgstr "Code d'accès" - -#: apps/tournament/forms.py:123 -msgid "The access code must be composed of 6 alphanumeric characters." -msgstr "Le code d'accès doit être composé de 6 caractères alphanumériques." - -#: apps/tournament/forms.py:127 -msgid "This access code is invalid." -msgstr "Ce code d'accès est invalide." - -#: apps/tournament/forms.py:130 -msgid "The team is already validated." -msgstr "L'équipe est déjà validée." - -#: apps/tournament/forms.py:142 -msgid "Problem" -msgstr "Problème" - -#: apps/tournament/forms.py:143 -#, python-format -msgid "Problem #%(problem)d" -msgstr "Problème n°%(problem)d" - -#: apps/tournament/forms.py:152 apps/tournament/forms.py:176 -#, python-format -msgid "" -"Please keep filesize under %(max_size)s. Current filesize %(current_size)s" -msgstr "" -"Merci de ne pas dépasser les %(max_size)s. Le fichier envoyé pèse " -"%(current_size)s." - -#: apps/tournament/forms.py:157 apps/tournament/forms.py:181 -msgid "The file should be a PDF file." -msgstr "Ce fichier doit être au format PDF." - -#: apps/tournament/forms.py:197 apps/tournament/forms.py:210 -#: apps/tournament/forms.py:223 -msgid "Choose a team..." -msgstr "Choisir une équipe ..." - -#: apps/tournament/forms.py:198 -msgid "Team 1" -msgstr "Équipe 1" - -#: apps/tournament/forms.py:205 -msgid "Problem defended by team 1" -msgstr "Problème défendu par l'équipe 1" - -#: apps/tournament/forms.py:211 -msgid "Team 2" -msgstr "Équipe 2" - -#: apps/tournament/forms.py:218 -msgid "Problem defended by team 2" -msgstr "Problème défendu par l'équipe 2" - -#: apps/tournament/forms.py:224 -msgid "Team 3" -msgstr "Équipe 3" - -#: apps/tournament/forms.py:231 -msgid "Problem defended by team 3" -msgstr "Problème défendu par l'équipe 3" - -#: apps/tournament/models.py:19 apps/tournament/models.py:170 -#: templates/tournament/team_detail.html:12 -msgid "name" -msgstr "nom" - -#: apps/tournament/models.py:25 templates/tournament/tournament_detail.html:12 -msgid "organizers" -msgstr "organisateurs" - -#: apps/tournament/models.py:26 -msgid "" -"List of all organizers that can see and manipulate data of the tournament " -"and the teams." -msgstr "" -"Liste des organisateurs qui peuvent manipuler les données du tournoi et des " -"équipes." - -#: apps/tournament/models.py:30 templates/tournament/tournament_detail.html:15 -msgid "size" -msgstr "taille" - -#: apps/tournament/models.py:31 -msgid "Number of teams that are allowed to join the tournament." -msgstr "Nombre d'équipes qui sont autorisées à rejoindre le tournoi." - -#: apps/tournament/models.py:36 templates/tournament/tournament_detail.html:18 -msgid "place" -msgstr "lieu" - -#: apps/tournament/models.py:40 templates/tournament/tournament_detail.html:21 -msgid "price" -msgstr "prix" - -#: apps/tournament/models.py:41 -msgid "Price asked to participants. Free with a scholarship." -msgstr "Prix demandé par participant. Gratuit pour les boursiers." - -#: apps/tournament/models.py:50 -msgid "date start" -msgstr "date de début" - -#: apps/tournament/models.py:55 -msgid "date end" -msgstr "date de fin" - -#: apps/tournament/models.py:60 templates/tournament/tournament_detail.html:27 -msgid "date of registration closing" -msgstr "date de clôture des inscriptions" - -#: apps/tournament/models.py:65 templates/tournament/tournament_detail.html:30 -msgid "date of maximal solution submission" -msgstr "date d'envoi maximal des solutions" - -#: apps/tournament/models.py:70 templates/tournament/tournament_detail.html:33 -msgid "date of maximal syntheses submission for the first round" -msgstr "date d'envoi maximal des notes de synthèses du premier tour" - -#: apps/tournament/models.py:75 templates/tournament/tournament_detail.html:36 -msgid "date when solutions of round 2 are available" -msgstr "date à partir de laquelle les solutions du tour 2 sont disponibles" - -#: apps/tournament/models.py:80 templates/tournament/tournament_detail.html:39 -msgid "date of maximal syntheses submission for the second round" -msgstr "date d'envoi maximal des notes de synthèses pour le second tour" - -#: apps/tournament/models.py:84 -msgid "final tournament" -msgstr "finale" - -#: apps/tournament/models.py:85 -msgid "It should be only one final tournament." -msgstr "Il ne doit y avoir qu'une seule finale." - -#: apps/tournament/models.py:136 -msgid "tournaments" -msgstr "tournois" - -#: apps/tournament/models.py:175 templates/tournament/team_detail.html:15 -msgid "trigram" -msgstr "trigramme" - -#: apps/tournament/models.py:176 -msgid "" -"The trigram should be composed of 3 capitalize letters, that is a funny " -"acronym for the team." -msgstr "" -"Le trigramme doit être composé de trois lettres en majuscule, qui doit être " -"un acronyme amusant représentant l'équipe." - -#: apps/tournament/models.py:184 -msgid "The tournament where the team is registered." -msgstr "Le tournoi où l'équipe est inscrite." - -#: apps/tournament/models.py:189 -msgid "inscription date" -msgstr "date d'inscription" - -#: apps/tournament/models.py:195 apps/tournament/models.py:420 -msgid "Registration not validated" -msgstr "Inscription non validée" - -#: apps/tournament/models.py:196 apps/tournament/models.py:421 -msgid "Waiting for validation" -msgstr "En attente de validation" - -#: apps/tournament/models.py:197 apps/tournament/models.py:422 -msgid "Registration validated" -msgstr "Inscription validée" - -#: apps/tournament/models.py:199 apps/tournament/models.py:424 -#: templates/tournament/team_detail.html:32 -msgid "validation status" -msgstr "statut de validation" - -#: apps/tournament/models.py:204 -msgid "selected for final" -msgstr "sélectionnée pour la finale" - -#: apps/tournament/models.py:210 templates/tournament/team_detail.html:18 -msgid "access code" -msgstr "code d'accès" - -#: apps/tournament/models.py:287 apps/tournament/models.py:319 -#: templates/tournament/pool_detail.html:15 -msgid "teams" -msgstr "équipes" - -#: apps/tournament/models.py:339 templates/tournament/pool_detail.html:12 -msgid "juries" -msgstr "jurys" - -#: apps/tournament/models.py:345 -msgid "extra access token" -msgstr "code d'accès spécial" - -#: apps/tournament/models.py:346 -msgid "Let other users access to the pool data without logging in." -msgstr "Permet à d'autres utilisateurs d'accéder au contenu de la poule sans connexion." - -#: apps/tournament/models.py:380 -msgid "pool" -msgstr "poule" - -#: apps/tournament/models.py:381 -msgid "pools" -msgstr "poules" - -#: apps/tournament/models.py:406 -msgid "Not paid" -msgstr "Non payé" - -#: apps/tournament/models.py:407 -msgid "Credit card" -msgstr "Carte bancaire" - -#: apps/tournament/models.py:408 -msgid "Bank check" -msgstr "Chèque bancaire" - -#: apps/tournament/models.py:409 -msgid "Bank transfer" -msgstr "Virement bancaire" - -#: apps/tournament/models.py:410 -msgid "Cash" -msgstr "Espèces" - -#: apps/tournament/models.py:414 -msgid "payment method" -msgstr "moyen de paiement" - -#: apps/tournament/models.py:428 -msgid "payment" -msgstr "paiement" - -#: apps/tournament/models.py:429 -msgid "payments" -msgstr "paiements" - -#: apps/tournament/models.py:432 -#, python-brace-format -msgid "Payment of {user}" -msgstr "Paiement de {user}" - -#: apps/tournament/tables.py:22 templates/tournament/tournament_detail.html:24 -msgid "dates" -msgstr "dates" - -#: apps/tournament/tables.py:26 -msgid "From {start:%b %d %Y} to {end:%b %d %Y}" -msgstr "Du {start: %d %b %Y} au {end:%d %b %Y}" - -#: apps/tournament/tables.py:71 apps/tournament/tables.py:147 -msgid "Tournament" -msgstr "Tournoi" - -#: apps/tournament/tables.py:85 apps/tournament/tables.py:124 -#: templates/tournament/team_detail.html:135 -#: templates/tournament/team_detail.html:144 -msgid "Download" -msgstr "Télécharger" - -#: apps/tournament/tables.py:140 -msgid "Problems" -msgstr "Problèmes" - -#: apps/tournament/views.py:68 -msgid "Tournaments list" -msgstr "Liste des tournois" - -#: apps/tournament/views.py:91 -msgid "Add tournament" -msgstr "Ajouter un tournoi" - -#: apps/tournament/views.py:108 -#, python-brace-format -msgid "Tournament of {name}" -msgstr "Tournoi de {name}" - -#: apps/tournament/views.py:146 -msgid "Update tournament" -msgstr "Modifier le tournoi" - -#: apps/tournament/views.py:195 apps/tournament/views.py:323 -#, python-brace-format -msgid "Solutions for team {team}.zip" -msgstr "Solutions pour l'équipe {team}.zip" - -#: apps/tournament/views.py:251 -msgid "Information about team" -msgstr "Informations sur l'équipe" - -#: apps/tournament/views.py:266 -msgid "Update team" -msgstr "Modifier l'équipe" - -#: apps/tournament/views.py:284 -msgid "Add organizer" -msgstr "Ajouter un organisateur" - -#: apps/tournament/views.py:307 templates/base.html:108 templates/base.html:118 -#: templates/base.html:132 templates/tournament/pool_detail.html:31 -msgid "Solutions" -msgstr "Solutions" - -#: apps/tournament/views.py:347 -msgid "" -"You can't publish your solution anymore. Deadline: {date:%m-%d-%Y %H:%M}." -msgstr "" -"Vous ne pouvez plus publier vos solutions. Deadline : {date:%d/%m/%Y %H:%M}." - -#: apps/tournament/views.py:376 -msgid "All solutions" -msgstr "Toutes les solutions" - -#: apps/tournament/views.py:395 -#, python-brace-format -msgid "Solutions for tournament {tournament}.zip" -msgstr "Solutions pour le tournoi {tournament}.zip" - -#: apps/tournament/views.py:432 templates/base.html:111 templates/base.html:121 -#: templates/base.html:135 templates/tournament/pool_detail.html:57 -msgid "Syntheses" -msgstr "Synthèses" - -#: apps/tournament/views.py:448 -#, python-brace-format -msgid "Syntheses for team {team}.zip" -msgstr "Notes de synthèse de l'équipe {team}.zip" - -#: apps/tournament/views.py:473 -msgid "" -"You can't publish your synthesis anymore for the first round. Deadline: " -"{date:%m-%d-%Y %H:%M}." -msgstr "" -"Vous ne pouvez plus envoyer vos notes de synthèse pour le premier tour. " -"Deadline : {date:%d/%m/%Y %h:%M}." - -#: apps/tournament/views.py:479 -msgid "" -"You can't publish your synthesis anymore for the second round. Deadline: " -"{date:%m-%d-%Y %H:%M}." -msgstr "" -"Vous ne pouvez plus envoyer vos notes de synthèse pour le second tour. " -"Deadline : {date:%d/%m/%Y %h:%M}." - -#: apps/tournament/views.py:509 -msgid "All syntheses" -msgstr "Toutes les notes de synthèses" - -#: apps/tournament/views.py:528 -#, python-brace-format -msgid "Syntheses for tournament {tournament}.zip" -msgstr "Notes de synthèse pour le tournoi {tournament}.zip" - -#: apps/tournament/views.py:571 templates/base.html:125 templates/base.html:138 -msgid "Pools" -msgstr "Poules" - -#: apps/tournament/views.py:594 -msgid "Create pool" -msgstr "Créer une poule" - -#: apps/tournament/views.py:611 -msgid "Pool detail" -msgstr "Détails d'une poule" - -#: apps/tournament/views.py:644 -#, python-brace-format -msgid "" -"Solutions of a pool for the round {round} of the tournament {tournament}.zip" -msgstr "Solutions d'une poule du tour {round} du tournoi {tournament}.zip" - -#: apps/tournament/views.py:658 -#, python-brace-format -msgid "" -"Syntheses of a pool for the round {round} of the tournament {tournament}.zip" -msgstr "Synthèse d'une poule du tour {round} du tournoi {tournament}.zip" - -#: templates/400.html:6 -msgid "Bad request" -msgstr "Requête invalide" - -#: templates/400.html:7 -msgid "" -"Sorry, your request was bad. Don't know what could be wrong. An email has " -"been sent to webmasters with the details of the error. You can now drink a " -"coke." -msgstr "" -"Désolé, votre requête comporte une erreur. Aucune idée de ce qui a pu se " -"passer. Un email a été envoyé au développeur avec les détails de l'erreur. " -"Vous pouvez désormais aller chercher un coca." - -#: templates/403.html:6 -msgid "Permission denied" -msgstr "Accès refusé" - -#: templates/403.html:7 -msgid "You don't have the right to perform this request." -msgstr "Vous n'avez pas la permission d'effectuer cette requête." - -#: templates/403.html:10 templates/404.html:10 -msgid "Exception message:" -msgstr "Message d'erreur :" - -#: templates/404.html:6 -msgid "Page not found" -msgstr "Page non trouvée" - -#: templates/404.html:7 -#, python-format -msgid "" -"The requested path %(request_path)s was not found on the server." -msgstr "" -"Le chemin demandé %(request_path)s n'a pas été trouvé sur le " -"serveur." - -#: templates/500.html:6 -msgid "Server error" -msgstr "Erreur du serveur" - -#: templates/500.html:7 -msgid "" -"Sorry, an error occurred when processing your request. An email has been " -"sent to webmasters with the detail of the error, and this will be fixed " -"soon. You can now drink a beer." -msgstr "" -"Désolé, votre requête comporte une erreur. Aucune idée de ce qui a pu se " -"passer. Un email a été envoyé au développeur avec les détails de l'erreur. " -"Vous pouvez désormais aller chercher une bière." - -#: templates/base.html:11 -msgid "The inscription site of the TFJM²." -msgstr "Le site d'inscription au TFJM²." - -#: templates/base.html:73 -msgid "Home" -msgstr "Accueil" - -#: templates/base.html:76 -msgid "Tournament list" -msgstr "Liste des tournois" - -#: templates/base.html:89 -msgid "My account" -msgstr "Mon compte" - -#: templates/base.html:94 -msgid "Add a team" -msgstr "Ajouter une équipe" - -#: templates/base.html:97 -msgid "Join a team" -msgstr "Rejoindre une équipe" - -#: templates/base.html:101 -msgid "My team" -msgstr "Mon équipe" - -#: templates/base.html:144 -msgid "Make a gift" -msgstr "Faire un don" - -#: templates/base.html:148 -msgid "Administration" -msgstr "Administration" - -#: templates/base.html:155 -msgid "Return to admin view" -msgstr "Retour à l'interface administrateur" - -#: templates/base.html:160 templates/registration/login.html:7 -#: templates/registration/login.html:8 templates/registration/login.html:22 -#: templates/registration/password_reset_complete.html:10 -msgid "Log in" -msgstr "Connexion" - -#: templates/base.html:163 templates/registration/signup.html:5 -#: templates/registration/signup.html:8 templates/registration/signup.html:14 -msgid "Sign up" -msgstr "S'inscrire" - -#: templates/base.html:167 -msgid "Log out" -msgstr "Déconnexion" - -#: templates/django_filters/rest_framework/crispy_form.html:4 -#: templates/django_filters/rest_framework/form.html:2 -msgid "Field filters" -msgstr "Filtres" - -#: templates/django_filters/rest_framework/form.html:5 -#: templates/member/my_account.html:9 templates/tournament/add_organizer.html:9 -#: templates/tournament/pool_form.html:9 -#: templates/tournament/solutions_list.html:24 -#: templates/tournament/syntheses_list.html:40 -#: templates/tournament/team_form.html:9 -#: templates/tournament/tournament_form.html:9 -msgid "Submit" -msgstr "Envoyer" - -#: templates/member/my_account.html:14 -msgid "Update my password" -msgstr "Changer mon mot de passe" - -#: templates/member/profile_list.html:9 -msgid "Add an organizer" -msgstr "Ajouter un organisateur" - -#: templates/member/tfjmuser_detail.html:12 -msgid "role" -msgstr "rôle" - -#: templates/member/tfjmuser_detail.html:47 -msgid "class" -msgstr "classe" - -#: templates/member/tfjmuser_detail.html:76 -#: templates/tournament/team_detail.html:129 -msgid "Documents" -msgstr "Documents" - -#: templates/member/tfjmuser_detail.html:84 -#, python-format -msgid "View site as %(tfjmuser)s" -msgstr "Voir le site en tant que %(tfjmuser)s" - -#: templates/registration/email_validation_complete.html:6 -msgid "Your email have successfully been validated." -msgstr "Votre adresse e-mail a bien été validée." - -#: templates/registration/email_validation_complete.html:8 -#, python-format -msgid "You can now log in." -msgstr "Vous pouvez désormais vous connecter" - -#: templates/registration/email_validation_complete.html:13 -msgid "" -"The link was invalid. The token may have expired. Please send us an email to " -"activate your account." -msgstr "" -"Le lien est invalide. Le jeton a du expirer. Merci de nous envoyer un mail " -"afin d'activer votre compte." - -#: templates/registration/logged_out.html:8 -msgid "Thanks for spending some quality time with the Web site today." -msgstr "Merci d'avoir utilisé la plateforme du TFJM²." - -#: templates/registration/logged_out.html:9 -msgid "Log in again" -msgstr "Se connecter à nouveau" - -#: templates/registration/login.html:13 -#, python-format -msgid "" -"You are authenticated as %(user)s, but are not authorized to access this " -"page. Would you like to login to a different account?" -msgstr "" -"Vous êtes déjà connecté sous le nom %(user)s, mais vous n'êtes pas autorisés " -"à accéder à cette page. Souhaitez-vous vous connecter sous un compte " -"différent ?" - -#: templates/registration/login.html:23 -msgid "Forgotten your password or username?" -msgstr "Mot de passe oublié ?" - -#: templates/registration/mails/email_validation_email.html:3 -msgid "Hi" -msgstr "Bonjour" - -#: templates/registration/mails/email_validation_email.html:9 -msgid "" -"This link is only valid for a couple of days, after that you will need to " -"contact us to validate your email." -msgstr "" - -#: templates/registration/mails/email_validation_email.html:13 -msgid "Thanks" -msgstr "Merci" - -#: templates/registration/password_change_done.html:8 -msgid "Your password was changed." -msgstr "Votre mot de passe a été changé" - -#: templates/registration/password_change_form.html:9 -msgid "" -"Please enter your old password, for security's sake, and then enter your new " -"password twice so we can verify you typed it in correctly." -msgstr "" -"Veuillez entrer votre ancien mot de passe, pour des raisons de sécurité, " -"puis entrer votre mot de passe deux fois afin de vérifier que vous l'avez " -"tapé correctement." - -#: templates/registration/password_change_form.html:11 -#: templates/registration/password_reset_confirm.html:12 -msgid "Change my password" -msgstr "Changer mon mot de passe" - -#: templates/registration/password_reset_complete.html:8 -msgid "Your password has been set. You may go ahead and log in now." -msgstr "Votre mot de passe a été changé. Vous pouvez désormais vous connecter." - -#: templates/registration/password_reset_confirm.html:9 -msgid "" -"Please enter your new password twice so we can verify you typed it in " -"correctly." -msgstr "" -"Veuillez taper votre nouveau mot de passe deux fois afin de s'assurer que " -"vous l'ayez tapé correctement." - -#: templates/registration/password_reset_confirm.html:15 -msgid "" -"The password reset link was invalid, possibly because it has already been " -"used. Please request a new password reset." -msgstr "" -"Le lien de réinitialisation du mot de passe est invalide, sans doute parce " -"qu'il a été déjà utilisé. Veuillez demander une nouvelle demande de " -"réinitialisation." - -#: templates/registration/password_reset_done.html:8 -msgid "" -"We've emailed you instructions for setting your password, if an account " -"exists with the email you entered. You should receive them shortly." -msgstr "" -"Nous vous avons envoyé des instructions pour réinitialiser votre mot de " -"passe, si un compte existe avec l'adresse email entrée. Vous devriez les " -"recevoir d'ici peu." - -#: templates/registration/password_reset_done.html:9 -msgid "" -"If you don't receive an email, please make sure you've entered the address " -"you registered with, and check your spam folder." -msgstr "" -"Si vous n'avez pas reçu d'email, merci de vérifier que vous avez entré " -"l'adresse avec laquelle vous êtes inscrits, et vérifier vos spams." - -#: templates/registration/password_reset_form.html:8 -msgid "" -"Forgotten your password? Enter your email address below, and we'll email " -"instructions for setting a new one." -msgstr "" -"Mot de passe oublié ? Entrez votre adresse email ci-dessous, et nous vous " -"enverrons des instructions pour en définir un nouveau." - -#: templates/registration/password_reset_form.html:11 -msgid "Reset my password" -msgstr "Réinitialiser mon mot de passe" - -#: templates/tournament/pool_detail.html:36 -msgid "Solutions will be available here for teams from:" -msgstr "Les solutions seront disponibles ici pour les équipes à partir du :" - -#: templates/tournament/pool_detail.html:49 -#: templates/tournament/pool_detail.html:73 -msgid "Download ZIP archive" -msgstr "Télécharger l'archive ZIP" - -#: templates/tournament/pool_detail.html:61 -#: templates/tournament/syntheses_list.html:7 -msgid "Templates for syntheses are available here:" -msgstr "Le modèle de note de synthèse est disponible ici :" - -#: templates/tournament/pool_detail.html:83 -msgid "Pool list" -msgstr "Liste des poules" - -#: templates/tournament/pool_detail.html:89 -msgid "" -"Give this link to juries to access this page (warning: should stay " -"confidential and only given to juries of this pool):" -msgstr "" -"Donnez ce lien aux jurys pour leur permettre d'accéder à cette page " -"(attention : ce lien doit rester confidentiel et ne doit être donné " -"exclusivement qu'à des jurys) :" - -#: templates/tournament/pool_list.html:10 -msgid "Add pool" -msgstr "Ajouter une poule" - -#: templates/tournament/solutions_list.html:9 -#, python-format -msgid "You can upload your solutions until %(deadline)s." -msgstr "Vous pouvez envoyer vos solutions jusqu'au %(deadline)s." - -#: templates/tournament/solutions_list.html:14 -msgid "" -"The deadline to send your solutions is reached. However, you have an extra " -"time of 30 minutes to send your papers, no panic :)" -msgstr "" -"La date limite pour envoyer vos solutions est dépassée. Toutefois, vous avez " -"droit à un délai supplémentaire de 30 minutes pour envoyer vos papiers, pas " -"de panique :)" - -#: templates/tournament/solutions_list.html:16 -msgid "You can't upload your solutions anymore." -msgstr "Vous ne pouvez plus publier vos solutions." - -#: templates/tournament/solutions_orga_list.html:14 -#: templates/tournament/syntheses_orga_list.html:14 -#, python-format -msgid "%(tournament)s — ZIP" -msgstr "%(tournament)s — ZIP" - -#: templates/tournament/syntheses_list.html:14 -#: templates/tournament/syntheses_list.html:26 -#, python-format -msgid "You can upload your syntheses for round %(round)s until %(deadline)s." -msgstr "" -"Vous pouvez envoyer vos notes de synthèses pour le tour %(round)s jusqu'au " -"%(deadline)s." - -#: templates/tournament/syntheses_list.html:18 -#: templates/tournament/syntheses_list.html:30 -#, python-format -msgid "" -"The deadline to send your syntheses for the round %(round)s is reached. " -"However, you have an extra time of 30 minutes to send your papers, no " -"panic :)" -msgstr "" -"La date limite pour envoyer vos notes de synthèses pour le tour %(round)s " -"est dépassée. Toutefois, vous avez droit à un délai supplémentaire de 30 " -"minutes pour envoyer vos papiers, pas de panique :)" - -#: templates/tournament/syntheses_list.html:22 -#: templates/tournament/syntheses_list.html:34 -#, python-format -msgid "You can't upload your syntheses for the round %(round)s anymore." -msgstr "" -"Vous ne pouvez plus publier vos notes de synthèses pour le tour %(round)s." - -#: templates/tournament/team_detail.html:8 -msgid "Team" -msgstr "Équipe" - -#: templates/tournament/team_detail.html:25 -msgid "coachs" -msgstr "encadrants" - -#: templates/tournament/team_detail.html:28 -msgid "participants" -msgstr "participants" - -#: templates/tournament/team_detail.html:39 -msgid "Send a mail to people in this team" -msgstr "Envoyer un mail à toutes les personnes de cette équipe" - -#: templates/tournament/team_detail.html:49 -msgid "Edit team" -msgstr "Modifier l'équipe" - -#: templates/tournament/team_detail.html:53 -msgid "Select for final" -msgstr "Sélectionner pour la finale" - -#: templates/tournament/team_detail.html:59 -msgid "Delete team" -msgstr "Supprimer l'équipe" - -#: templates/tournament/team_detail.html:61 -msgid "Leave this team" -msgstr "Quitter l'équipe" - -#: templates/tournament/team_detail.html:105 -msgid "The team is waiting about validation." -msgstr "L'équipe est en attente de validation" - -#: templates/tournament/team_detail.html:112 -msgid "Message addressed to the team:" -msgstr "Message adressé à l'équipe :" - -#: templates/tournament/team_detail.html:114 -msgid "Message..." -msgstr "Message ..." - -#: templates/tournament/team_detail.html:119 -msgid "Invalidate team" -msgstr "Invalider l'équipe" - -#: templates/tournament/team_detail.html:120 -msgid "Validate team" -msgstr "Valider l'équipe" - -#: templates/tournament/team_detail.html:133 -msgid "Motivation letter:" -msgstr "Lettre de motivation :" - -#: templates/tournament/team_detail.html:152 -msgid "Download solutions as ZIP" -msgstr "Télécharger les solutions en archive ZIP" - -#: templates/tournament/tournament_detail.html:22 -msgid "Free" -msgstr "Gratuit" - -#: templates/tournament/tournament_detail.html:25 -msgid "From" -msgstr "Du" - -#: templates/tournament/tournament_detail.html:25 -msgid "to" -msgstr "à" - -#: templates/tournament/tournament_detail.html:48 -msgid "Send a mail to all people in this tournament" -msgstr "Envoyer un mail à toutes les personnes du tournoi" - -#: templates/tournament/tournament_detail.html:49 -msgid "Send a mail to all people in this tournament that are in a valid team" -msgstr "" -"Envoyer un mail à toutes les personnes du tournoi dans une équipe valide" - -#: templates/tournament/tournament_detail.html:56 -msgid "Edit tournament" -msgstr "Modifier le tournoi" - -#: templates/tournament/tournament_detail.html:63 -msgid "Teams" -msgstr "Équipes" - -#: templates/tournament/tournament_list.html:8 -msgid "Send a mail to all people that are in a team" -msgstr "Envoyer un mail à toutes les personnes dans une équipe" - -#: templates/tournament/tournament_list.html:9 -msgid "Send a mail to all people that are in a valid team" -msgstr "Envoyer un mail à toutes les personnes dans une équipe validée" - -#: templates/tournament/tournament_list.html:15 -msgid "Add a tournament" -msgstr "Ajouter un tournoi" - -#: tfjm/settings.py:147 -msgid "English" -msgstr "Anglais" - -#: tfjm/settings.py:148 -msgid "French" -msgstr "Français" diff --git a/manage.py b/manage.py deleted file mode 100755 index 2bf0cbf..0000000 --- a/manage.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" -import os -import sys - - -def main(): - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tfjm.settings') - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) - - -if __name__ == '__main__': - main() diff --git a/nginx_tfjm.conf b/nginx_tfjm.conf deleted file mode 100644 index be143ce..0000000 --- a/nginx_tfjm.conf +++ /dev/null @@ -1,19 +0,0 @@ -upstream tfjm { - server 127.0.0.1:8000; -} - -server { - listen 80; - server_name tfjm; - - location / { - proxy_pass http://tfjm; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $host; - proxy_redirect off; - } - - location /static { - alias /code/static/; - } -} diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 093ea76..0000000 --- a/requirements.txt +++ /dev/null @@ -1,16 +0,0 @@ -bcrypt -Django~=3.0 -django-allauth -django-bootstrap-datepicker-plus -django-crispy-forms -django-extensions -django-filter -django-mailer -django-polymorphic -django-tables2 -djangorestframework -django-rest-polymorphic -mysqlclient -psycopg2-binary -ptpython -gunicorn \ No newline at end of file diff --git a/templates/400.html b/templates/400.html deleted file mode 100644 index 3560652..0000000 --- a/templates/400.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} - -{% block content %} -

{% trans "Bad request" %}

- {% blocktrans %}Sorry, your request was bad. Don't know what could be wrong. An email has been sent to webmasters with the details of the error. You can now drink a coke.{% endblocktrans %} -{% endblock %} \ No newline at end of file diff --git a/templates/403.html b/templates/403.html deleted file mode 100644 index 317865f..0000000 --- a/templates/403.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} - -{% block content %} -

{% trans "Permission denied" %}

- {% blocktrans %}You don't have the right to perform this request.{% endblocktrans %} - {% if exception %} -
- {% trans "Exception message:" %} {{ exception }} -
- {% endif %} -{% endblock %} \ No newline at end of file diff --git a/templates/404.html b/templates/404.html deleted file mode 100644 index 8477f91..0000000 --- a/templates/404.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} - -{% block content %} -

{% trans "Page not found" %}

- {% blocktrans %}The requested path {{ request_path }} was not found on the server.{% endblocktrans %} - {% if exception != "Resolver404" %} -
- {% trans "Exception message:" %} {{ exception }} -
- {% endif %} -{% endblock %} \ No newline at end of file diff --git a/templates/500.html b/templates/500.html deleted file mode 100644 index 7cc0063..0000000 --- a/templates/500.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} - -{% block content %} -

{% trans "Server error" %}

- {% blocktrans %}Sorry, an error occurred when processing your request. An email has been sent to webmasters with the detail of the error, and this will be fixed soon. You can now drink a beer.{% endblocktrans %} -{% endblock %} diff --git a/templates/amount_input.html b/templates/amount_input.html deleted file mode 100644 index 6ef4a53..0000000 --- a/templates/amount_input.html +++ /dev/null @@ -1,11 +0,0 @@ -
- -
- -
-
\ No newline at end of file diff --git a/templates/autocomplete_model.html b/templates/autocomplete_model.html deleted file mode 100644 index 2236c6e..0000000 --- a/templates/autocomplete_model.html +++ /dev/null @@ -1,9 +0,0 @@ - - -
    -
diff --git a/templates/base.html b/templates/base.html deleted file mode 100644 index 884633b..0000000 --- a/templates/base.html +++ /dev/null @@ -1,227 +0,0 @@ -{% load static i18n static getconfig %} - - - - - - - - {% block title %}{{ title }}{% endblock title %} - Inscription au TFJM² - - - - {# Favicon #} - - - {% if no_cache %} - - {% endif %} - - {# Bootstrap CSS #} - - - - - {# Custom CSS #} - - - {# JQuery, Bootstrap and Turbolinks JavaScript #} - - - - - - {# Si un formulaire requiert des données supplémentaires (notamment JS), les données sont chargées #} - {% if form.media %} - {{ form.media }} - {% endif %} - - - - {% block extracss %}{% endblock %} - - -
- -
- {% block contenttitle %}

{{ title }}

{% endblock %} -
- {% block content %} -

Default content...

- {% endblock content %} -
-
- -
-
-
-
-
- - 𝕋𝔽𝕁𝕄² — - Nous contacter — - - {% csrf_token %} - - -
-
- -
-
-
- - - -{% block extrajavascript %} -{% endblock extrajavascript %} - - diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index 9455dc1..0000000 --- a/templates/index.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} - -{% load getconfig %} - -{% block content %} - {% autoescape off %} - {{ "index_page"|get_config|safe }} - {% endautoescape %} -{% endblock %} diff --git a/templates/mail_templates/add_organizer.html b/templates/mail_templates/add_organizer.html deleted file mode 100644 index 06cc500..0000000 --- a/templates/mail_templates/add_organizer.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Organisateur du TFJM² - - -Bonjour {{ user }},
-
-Vous recevez ce message (envoyé automatiquement) car vous êtes organisateur d'un des tournois du TFJM2.

-Un compte organisateur vous a été créé par l'un des administrateurs. Avant de vous connecter, vous devez réinitialiser votre -mot de passe sur le lien suivant : https://inscription.tfjm.org{% url "password_reset" %}. -
-Une fois le mot de passe changé, vous pourrez vous connecter sur la plateforme.
-
-Merci beaucoup pour votre aide !
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/add_organizer.txt b/templates/mail_templates/add_organizer.txt deleted file mode 100644 index 2a4cde4..0000000 --- a/templates/mail_templates/add_organizer.txt +++ /dev/null @@ -1,12 +0,0 @@ -Bonjour {{ user }}, - -Vous recevez ce message (envoyé automatiquement) car vous êtes organisateur d'un des tournois du TFJM². - -Un compte organisateur vous a été créé par l'un des administrateurs. Avant de vous connecter, vous devez réinitialiser votre -mot de passe sur le lien suivant : https://inscription.tfjm.org{% url "password_reset" %}. - -Une fois le mot de passe changé, vous pourrez vous connecter sur la plateforme : https://inscription.tfjm.org{% url "login" %}. - -Merci beaucoup pour votre aide ! - -Le comité national d'organisation du TFJM² diff --git a/templates/mail_templates/add_organizer_for_tournament.html b/templates/mail_templates/add_organizer_for_tournament.html deleted file mode 100644 index ad9aa90..0000000 --- a/templates/mail_templates/add_organizer_for_tournament.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - Organisateur du tournoi de {TOURNAMENT_NAME} – TFJM² - - -Bonjour {FIRST_NAME} {SURNAME},
-
-Vous venez d'être promu organisateur du tournoi {TOURNAMENT_NAME} du TFJM2 {YEAR}.
-Ce message vous a été envoyé automatiquement. En cas de problème, merci de répondre à ce message. -
-Avec toute notre bienveillance,
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/add_team.html b/templates/mail_templates/add_team.html deleted file mode 100644 index bb69db0..0000000 --- a/templates/mail_templates/add_team.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Nouvelle équipe TFJM² {YEAR} - - -Bonjour {FIRST_NAME} {SURNAME},
-
-Vous venez de créer l'équipe « {TEAM_NAME} » ({TRIGRAM}) pour le TFJM2 de {TOURNAMENT_NAME} et nous vous en remercions.
-Afin de permettre aux autres membres de votre équipe de vous rejoindre, veuillez leur transmettre le code d'accès : -{ACCESS_CODE}
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/change_email_address.html b/templates/mail_templates/change_email_address.html deleted file mode 100644 index d04ed90..0000000 --- a/templates/mail_templates/change_email_address.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Changement d'adresse e-mail – TFJM² - - -Bonjour {FIRST_NAME} {SURNAME},
-
-Vous venez de changer votre adresse e-mail. Veuillez désormais la confirmer en cliquant ici : {URL_BASE}/confirmer_mail/{TOKEN}
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/change_password.html b/templates/mail_templates/change_password.html deleted file mode 100644 index 673e80f..0000000 --- a/templates/mail_templates/change_password.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Mot de passe changé – TFJM² - - -Bonjour {FIRST_NAME} {SURNAME},
-
-Nous vous informons que votre mot de passe vient d'être modifié. Si vous n'êtes pas à l'origine de cette manipulation, -veuillez immédiatement vérifier vos accès à votre boîte mail et changer votre mot de passe sur la plateforme -d'inscription.
-
-Avec toute notre bienveillance,
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/confirm_email.html b/templates/mail_templates/confirm_email.html deleted file mode 100644 index d247377..0000000 --- a/templates/mail_templates/confirm_email.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - Inscription au TFJM² {YEAR} - - -Bonjour {FIRST_NAME} {SURNAME},
-
-Vous êtes inscrit au TFJM2 {YEAR} et nous vous en remercions.
-Pour valider votre adresse e-mail, veuillez cliquer sur le lien : {URL_BASE}/confirmer_mail/{TOKEN}
-
-Avec toute notre bienveillance,
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/forgotten_password.html b/templates/mail_templates/forgotten_password.html deleted file mode 100644 index 717cc8c..0000000 --- a/templates/mail_templates/forgotten_password.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - Mot de passe oublié – TFJM² - - -Bonjour,
-
-Vous avez indiqué avoir oublié votre mot de passe. Veuillez cliquer ici pour le réinitialiser : {URL_BASE}/connexion/reinitialiser_mdp/{TOKEN}
-
-Si vous n'êtes pas à l'origine de cette manipulation, vous pouvez ignorer ce message.
-
-Avec toute notre bienveillance,
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/join_team.html b/templates/mail_templates/join_team.html deleted file mode 100644 index d5628c0..0000000 --- a/templates/mail_templates/join_team.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Équipe rejointe – TFJM² {YEAR} - - -Bonjour {FIRST_NAME} {SURNAME},
-
-Vous venez de rejoindre l'équipe « {TEAM_NAME} » ({TRIGRAM}) pour le TFJM² de {TOURNAMENT_NAME} et nous vous en -remercions.
-
-Avec toute notre bienveillance,
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/register.html b/templates/mail_templates/register.html deleted file mode 100644 index bc4123b..0000000 --- a/templates/mail_templates/register.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Inscription au TFJM² {YEAR} - - -Bonjour {FIRST_NAME} {SURNAME},
-
-Vous venez de vous inscrire au TFJM2 {YEAR} et nous vous en remercions.
-Pour valider votre adresse e-mail, veuillez cliquer sur le lien : {URL_BASE}/confirmer_mail/{TOKEN}
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/request_payment_validation.html b/templates/mail_templates/request_payment_validation.html deleted file mode 100644 index 913e490..0000000 --- a/templates/mail_templates/request_payment_validation.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - Demande de validation de paiement pour le TFJM² {YEAR} - - -Bonjour {FIRST_NAME} {SURNAME},
-
-{USER_FIRST_NAME} {USER_SURNAME} de l'équipe {TEAM_NAME} ({TRIGRAM}) annonce avoir réglé sa participation pour le tournoi {TOURNAMENT_NAME}. -Les informations suivantes ont été communiquées :

-Équipe : {TEAM_NAME} ({TRIGRAM})
-Tournoi : {TOURNAMENT_NAME}
-Moyen de paiement : {PAYMENT_METHOD}
-Montant : {AMOUNT} €
-Informations sur le paiement : {PAYMENT_INFOS}
-
-Vous pouvez désormais vérifier ces informations, puis valider (ou non) le paiement sur -la page associée à ce participant. -
-Avec toute notre bienveillance, -
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/request_validation.html b/templates/mail_templates/request_validation.html deleted file mode 100644 index 7a05122..0000000 --- a/templates/mail_templates/request_validation.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Demande de validation - TFJM² - - -Bonjour {{ user }},
-
-L'équipe « {{ team.name }} » ({{ team.trigram }}) vient de demander à valider son équipe pour participer au tournoi -{{ tournament }} du TFJM². Vous pouvez décider d'accepter ou de refuser l'équipe en vous rendant sur la page de l'équipe : -https://inscription.tfjm.org{% url "tournament:team_detail" pk=team.pk %}
-
-Avec toute notre bienveillance,
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/request_validation.txt b/templates/mail_templates/request_validation.txt deleted file mode 100644 index 88d463b..0000000 --- a/templates/mail_templates/request_validation.txt +++ /dev/null @@ -1,9 +0,0 @@ -Bonjour {{ user }}, - -L'équipe « {{ team.name }} » ({{ team.trigram }}) vient de demander à valider son équipe pour participer au tournoi -{{ tournament }} du TFJM². Vous pouvez décider d'accepter ou de refuser l'équipe en vous rendant sur la page de l'équipe : -https://inscription.tfjm.org{% url "tournament:team_detail" pk=team.pk %}. - -Avec toute notre bienveillance, - -Le comité national d'organisation du TFJM² diff --git a/templates/mail_templates/select_for_final.html b/templates/mail_templates/select_for_final.html deleted file mode 100644 index 383b527..0000000 --- a/templates/mail_templates/select_for_final.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - Sélection pour la finale - TFJM² - - -Bonjour {{ user }},
-
-Félicitations ! Votre équipe « {{ team.name }} » ({{ team.trigram }}) est sélectionnée pour la finale nationale !
-
-La finale aura lieu du {{ final.date_start }} au {{ final.date_end }}. Vous pouvez peaufiner vos solutions -si vous le souhaitez jusqu'au {{ final.date_solutions }}.
-
-Bravo encore !
-
-Avec toute notre bienveillance,
-
-Le comité national d'organisation du TFJM² - - \ No newline at end of file diff --git a/templates/mail_templates/select_for_final.txt b/templates/mail_templates/select_for_final.txt deleted file mode 100644 index a000c22..0000000 --- a/templates/mail_templates/select_for_final.txt +++ /dev/null @@ -1,12 +0,0 @@ -Bonjour {{ user }}, - -Félicitations ! Votre équipe « {{ team.name }} » ({{ team.trigram }}) est sélectionnée pour la finale nationale ! - -La finale aura lieu du {{ final.date_start }} au {{ final.date_end }}. Vous pouvez peaufiner vos solutions -si vous le souhaitez jusqu'au {{ final.date_solutions }}. - -Bravo encore ! - -Avec toute notre bienveillance, - -Le comité national d'organisation du TFJM² diff --git a/templates/mail_templates/unvalidate_payment.html b/templates/mail_templates/unvalidate_payment.html deleted file mode 100644 index 7273282..0000000 --- a/templates/mail_templates/unvalidate_payment.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - Non-validation du paiement pour le TFJM² {YEAR} - - -Bonjour {FIRST_NAME} {SURNAME},
-
-Votre paiement pour le TFJM² {YEAR} a malheureusement été rejeté. Pour rappel, vous aviez fourni ces informations :

-Équipe : {TEAM_NAME} ({TRIGRAM})
-Tournoi : {TOURNAMENT_NAME}
-Moyen de paiement : {PAYMENT_METHOD}
-Montant : {AMOUNT} €
-Informations sur le paiement : {PAYMENT_INFOS}
-
-{MESSAGE} -
-Avec toute notre bienveillance, -
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/unvalidate_team.html b/templates/mail_templates/unvalidate_team.html deleted file mode 100644 index 1ba3ce8..0000000 --- a/templates/mail_templates/unvalidate_team.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Équipe non validée – TFJM² - - -Bonjour {{ user }},
-
-Maleureusement, votre équipe « {{ team.name }} » ({{ team.trigram }}) n'a pas été validée. Veuillez vérifier que vos autorisations sont correctes. -{% if message %} -

- Le CNO vous adresse le message suivant : -

- {{ message }} -
-

-{% endif %} -
-N'hésitez pas à nous contacter à l'adresse contact@tfjm.org pour plus d'informations. -
-Avec toute notre bienveillance,
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/unvalidate_team.txt b/templates/mail_templates/unvalidate_team.txt deleted file mode 100644 index 88c1440..0000000 --- a/templates/mail_templates/unvalidate_team.txt +++ /dev/null @@ -1,15 +0,0 @@ -Bonjour {{ user }}, - -Maleureusement, votre équipe « {{ team.name }} » ({{ team.trigram }}) n'a pas été validée. Veuillez vérifier que vos autorisations sont correctes. - -{% if message %} -Le CNO vous adresse le message suivant : - -{{ message }} -{% endif %} - -N'hésitez pas à nous contacter à l'adresse contact@tfjm.org pour plus d'informations. - -Avec toute notre bienveillance, - -Le comité national d'organisation du TFJM² diff --git a/templates/mail_templates/validate_payment.html b/templates/mail_templates/validate_payment.html deleted file mode 100644 index 2743f17..0000000 --- a/templates/mail_templates/validate_payment.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - Validation du paiement pour le TFJM² {YEAR} - - -Bonjour {FIRST_NAME} {SURNAME},
-
-Votre paiement pour le TFJM² {YEAR} a bien été validé. Pour rappel, vous aviez fourni ces informations :

-Équipe : {TEAM_NAME} ({TRIGRAM})
-Tournoi : {TOURNAMENT_NAME}
-Moyen de paiement : {PAYMENT_METHOD}
-Montant : {AMOUNT} €
-Informations sur le paiement : {PAYMENT_INFOS}
-
-{MESSAGE} -
-Avec toute notre bienveillance, -
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/validate_team.html b/templates/mail_templates/validate_team.html deleted file mode 100644 index ef23e85..0000000 --- a/templates/mail_templates/validate_team.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - Équipe validée – TFJM² {YEAR} - - -Bonjour {{ user }},
-
-Félicitations ! Votre équipe « {{ team }} » ({{ team.trigram }}) est désormais validée ! Vous êtes désormais apte à travailler sur -vos problèmes et publier vos solutions sur la plateforme. -{% if message %} -

- Le CNO vous adresse le message suivant : -

- {{ message }} -
-

-{% endif %} -
-Avec toute notre bienveillance,
-
-Le comité national d'organisation du TFJM2 - - diff --git a/templates/mail_templates/validate_team.txt b/templates/mail_templates/validate_team.txt deleted file mode 100644 index 8b4c95e..0000000 --- a/templates/mail_templates/validate_team.txt +++ /dev/null @@ -1,13 +0,0 @@ -Bonjour {{ user }}, - -Félicitations ! Votre équipe « {{ team }} » ({{ team.trigram }}) est désormais validée ! Vous êtes désormais apte à travailler sur -vos problèmes et publier vos solutions sur la plateforme. - -{% if message %} -Le CNO vous adresse le message suivant : -{{ message }} -{% endif %} - -Avec toute notre bienveillance, - -Le comité national d'organisation du TFJM² diff --git a/templates/member/my_account.html b/templates/member/my_account.html deleted file mode 100644 index 884bf2b..0000000 --- a/templates/member/my_account.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "base.html" %} - -{% load i18n crispy_forms_filters %} - -{% block content %} -
- {% csrf_token %} - {{ form|crispy }} - -
- -
- - {% trans "Update my password" %} -{% endblock %} diff --git a/templates/member/profile_list.html b/templates/member/profile_list.html deleted file mode 100644 index 7a6f85f..0000000 --- a/templates/member/profile_list.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} - -{% load django_tables2 i18n %} - -{% block content %} - {% render_table table %} - {% if type == "organizers" and user.admin %} -
- {% trans "Add an organizer" %} - {% endif %} -{% endblock %} diff --git a/templates/member/tfjmuser_detail.html b/templates/member/tfjmuser_detail.html deleted file mode 100644 index 26e63b4..0000000 --- a/templates/member/tfjmuser_detail.html +++ /dev/null @@ -1,87 +0,0 @@ -{% extends "base.html" %} - -{% load getconfig i18n django_tables2 static %} - -{% block content %} -
-
-

{{ tfjmuser }}

-
-
-
-
{% trans 'role'|capfirst %}
-
{{ tfjmuser.get_role_display }}
- - {% if tfjmuser.team %} -
{% trans 'team'|capfirst %}
-
{{ tfjmuser.team }}
- {% endif %} - - {% if tfjmuser.birth_date %} -
{% trans 'birth date'|capfirst %}
-
{{ tfjmuser.birth_date }}
- {% endif %} - - {% if tfjmuser.participates %} -
{% trans 'gender'|capfirst %}
-
{{ tfjmuser.get_gender_display }}
- {% endif %} - - {% if tfjmuser.address %} -
{% trans 'address'|capfirst %}
-
{{ tfjmuser.address }}, {{ tfjmuser.postal_code }}, {{ tfjmuser.city }}{% if tfjmuser.country != "France" %}, {{ tfjmuser.country }}{% endif %}
- {% endif %} - -
{% trans 'email'|capfirst %}
-
{{ tfjmuser.email }}
- - {% if tfjmuser.phone_number %} -
{% trans 'phone number'|capfirst %}
-
{{ tfjmuser.phone_number }}
- {% endif %} - - {% if tfjmuser.role == '3participant' %} -
{% trans 'school'|capfirst %}
-
{{ tfjmuser.school }}
- -
{% trans 'class'|capfirst %}
-
{{ tfjmuser.get_student_class_display }}
- - {% if tfjmuser.responsible_name %} -
{% trans 'responsible name'|capfirst %}
-
{{ tfjmuser.responsible_name }}
- {% endif %} - - {% if tfjmuser.responsible_phone %} -
{% trans 'responsible phone'|capfirst %}
-
{{ tfjmuser.responsible_phone }}
- {% endif %} - - {% if tfjmuser.responsible_email %} -
{% trans 'responsible email'|capfirst %}
-
{{ tfjmuser.responsible_email }}
- {% endif %} - {% endif %} - - {% if tfjmuser.role != '3participant' %} -
{% trans 'description'|capfirst %}
-
{{ tfjmuser.description|default_if_none:"" }}
- {% endif %} -
-
-
- -
- -

{% trans "Documents" %}

- - {# TODO Display documents #} - - {% if request.user.is_superuser %} -
-
- {% csrf_token %} - -
- {% endif %} -{% endblock %} diff --git a/templates/registration/email_validation_complete.html b/templates/registration/email_validation_complete.html deleted file mode 100644 index f58a7e5..0000000 --- a/templates/registration/email_validation_complete.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} -{% load i18n %} - -{% block content %} -
-

- {{ title }} -

-
- {% if validlink %} -

- {% trans "Your email have successfully been validated." %} -

-

- {% blocktrans %}You can now log in.{% endblocktrans %} -

- {% else %} -

- {% trans "The link was invalid. The token may have expired. Please send us an email to activate your account." %} -

- {% endif %} -
-
-{% endblock %} diff --git a/templates/registration/email_validation_email_sent.html b/templates/registration/email_validation_email_sent.html deleted file mode 100644 index adc0c02..0000000 --- a/templates/registration/email_validation_email_sent.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} -{% load i18n %} - -{% block content %} -
-

- {% trans "Account activation" %} -

-
-

- {% trans "An email has been sent. Please click on the link to activate your account." %} -

-
-
-{% endblock %} diff --git a/templates/registration/logged_out.html b/templates/registration/logged_out.html deleted file mode 100644 index 3b044b7..0000000 --- a/templates/registration/logged_out.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} -{% load i18n %} - -{% block content %} -

{% trans "Thanks for spending some quality time with the Web site today." %}

-

{% trans 'Log in again' %}

-{% endblock %} diff --git a/templates/registration/login.html b/templates/registration/login.html deleted file mode 100644 index 64c5c26..0000000 --- a/templates/registration/login.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-2.0-or-later -{% endcomment %} -{% load i18n crispy_forms_filters %} - -{% block title %}{% trans "Log in" %}{% endblock %} -{% block contenttitle %}

{% trans "Log in" %}

{% endblock %} - -{% block content %} - {% if user.is_authenticated %} -

- {% blocktrans trimmed %} - You are authenticated as {{ user }}, but are not authorized to - access this page. Would you like to login to a different account? - {% endblocktrans %} -

- {% endif %} -
- {% csrf_token %} - {{ form | crispy }} - - {% trans 'Forgotten your password or username?' %} -
-{% endblock %} diff --git a/templates/registration/mails/email_validation_email.html b/templates/registration/mails/email_validation_email.html deleted file mode 100644 index 9d002d6..0000000 --- a/templates/registration/mails/email_validation_email.html +++ /dev/null @@ -1,36 +0,0 @@ -{% load i18n %} - - - - - - - - - -

- {% trans "Hi" %} {{ user.username }}, -

- -

- {% trans "You recently registered on the TFJM² platform. Please click on the link below to confirm your registration." %} -

- -

- - https://{{ domain }}{% url 'member:email_validation' uidb64=uid token=token %} - -

- -

- {% trans "This link is only valid for a couple of days, after that you will need to contact us to validate your email." %} -

- -

- {% trans "Thanks" %}, -

- --- -

- {% trans "The CNO." %}
-

diff --git a/templates/registration/mails/email_validation_email.txt b/templates/registration/mails/email_validation_email.txt deleted file mode 100644 index 2022294..0000000 --- a/templates/registration/mails/email_validation_email.txt +++ /dev/null @@ -1,13 +0,0 @@ -{% load i18n %} - -{% trans "Hi" %} {{ user.username }}, - -{% trans "You recently registered on the TFJM² platform. Please click on the link below to confirm your registration." %} - -https://{{ domain }}{% url 'member:email_validation' uidb64=uid token=token %} - -{% trans "This link is only valid for a couple of days, after that you will need to contact us to validate your email." %} - -{% trans "Thanks" %}, - -{% trans "The CNO." %} diff --git a/templates/registration/password_change_done.html b/templates/registration/password_change_done.html deleted file mode 100644 index 150a00e..0000000 --- a/templates/registration/password_change_done.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} -{% load i18n %} - -{% block content %} -

{% trans 'Your password was changed.' %}

-{% endblock %} diff --git a/templates/registration/password_change_form.html b/templates/registration/password_change_form.html deleted file mode 100644 index 01133e4..0000000 --- a/templates/registration/password_change_form.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} -{% load i18n crispy_forms_tags %} - -{% block content %} -
{% csrf_token %} -

{% trans "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." %}

- {{ form | crispy }} - -
-{% endblock %} \ No newline at end of file diff --git a/templates/registration/password_reset_complete.html b/templates/registration/password_reset_complete.html deleted file mode 100644 index bb91a3c..0000000 --- a/templates/registration/password_reset_complete.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} -{% load i18n %} - -{% block content %} -

{% trans "Your password has been set. You may go ahead and log in now." %}

-

- {% trans 'Log in' %} -

-{% endblock %} diff --git a/templates/registration/password_reset_confirm.html b/templates/registration/password_reset_confirm.html deleted file mode 100644 index 5db0e81..0000000 --- a/templates/registration/password_reset_confirm.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} -{% load i18n crispy_forms_tags %} - -{% block content %} - {% if validlink %} -

{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}

-
{% csrf_token %} - {{ form | crispy }} - -
- {% else %} -

{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}

- {% endif %} -{% endblock %} diff --git a/templates/registration/password_reset_done.html b/templates/registration/password_reset_done.html deleted file mode 100644 index a215ab9..0000000 --- a/templates/registration/password_reset_done.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} -{% load i18n %} - -{% block content %} -

{% trans "We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly." %}

-

{% trans "If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder." %}

-{% endblock %} diff --git a/templates/registration/password_reset_form.html b/templates/registration/password_reset_form.html deleted file mode 100644 index 61adaa9..0000000 --- a/templates/registration/password_reset_form.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} -{% load i18n crispy_forms_tags %} - -{% block content %} -

{% trans "Forgotten your password? Enter your email address below, and we'll email instructions for setting a new one." %}

-
{% csrf_token %} - {{ form | crispy }} - -
-{% endblock %} diff --git a/templates/registration/signup.html b/templates/registration/signup.html deleted file mode 100644 index ed100d0..0000000 --- a/templates/registration/signup.html +++ /dev/null @@ -1,17 +0,0 @@ - -{% extends 'base.html' %} -{% load crispy_forms_filters %} -{% load i18n %} -{% block title %}{% trans "Sign up" %}{% endblock %} - -{% block content %} -

{% trans "Sign up" %}

- -
- {% csrf_token %} - {{ form|crispy }} - -
-{% endblock %} diff --git a/templates/tournament/add_organizer.html b/templates/tournament/add_organizer.html deleted file mode 100644 index 21daee8..0000000 --- a/templates/tournament/add_organizer.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} - -{% load i18n crispy_forms_filters %} - -{% block content %} -
- {% csrf_token %} - {{ form|crispy }} - -
-{% endblock %} diff --git a/templates/tournament/pool_detail.html b/templates/tournament/pool_detail.html deleted file mode 100644 index d20c2b8..0000000 --- a/templates/tournament/pool_detail.html +++ /dev/null @@ -1,94 +0,0 @@ -{% extends "base.html" %} - -{% load getconfig i18n django_tables2 static %} - -{% block content %} -
-
-

{{ title }}

-
-
-
-
{% trans 'juries'|capfirst %}
-
{{ pool.juries.all|join:", " }}
- -
{% trans 'teams'|capfirst %}
-
{{ pool.teams.all|join:", " }}
- -
{% trans 'round'|capfirst %}
-
{{ pool.round }}
- -
{% trans 'tournament'|capfirst %}
-
{{ pool.tournament }}
-
-
-
- -
- -
-
-

{% trans "Solutions" %}

-
-
- {% if pool.round == 2 %} -
- {% trans "Solutions will be available here for teams from:" %} {{ pool.tournament.date_solutions_2 }} -
- {% endif %} - -
    - {% for solution in pool.solutions.all %} -
  • {{ solution }}
  • - {% endfor %} -
-
- -
-
- -
-
-

{% trans "Syntheses" %}

-
-
-
- {% trans "Templates for syntheses are available here:" %} - PDFTEX -
- {% if user.organizes or not user.is_authenticated %} -
    - {% for synthesis in pool.syntheses.all %} -
  • {{ synthesis }}
  • - {% endfor %} -
- - {% endif %} -
-
- -
- - - - {% if user.organizes or not user.is_authenticated %} -
-
- {% trans "Give this link to juries to access this page (warning: should stay confidential and only given to juries of this pool):" %}
- - https://{{ request.get_host }}{% url "tournament:pool_detail" pk=pool.pk %}?extra_access_token={{ pool.extra_access_token }} -
- {% endif %} -{% endblock %} diff --git a/templates/tournament/pool_form.html b/templates/tournament/pool_form.html deleted file mode 100644 index 21daee8..0000000 --- a/templates/tournament/pool_form.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} - -{% load i18n crispy_forms_filters %} - -{% block content %} -
- {% csrf_token %} - {{ form|crispy }} - -
-{% endblock %} diff --git a/templates/tournament/pool_list.html b/templates/tournament/pool_list.html deleted file mode 100644 index 9a15348..0000000 --- a/templates/tournament/pool_list.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} - -{% load i18n django_tables2 %} - -{% block content %} - {% render_table table %} - - {% if user.admin %} -
- - {% endif %} -{% endblock %} diff --git a/templates/tournament/solutions_list.html b/templates/tournament/solutions_list.html deleted file mode 100644 index 6005d11..0000000 --- a/templates/tournament/solutions_list.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "base.html" %} - -{% load i18n crispy_forms_filters django_tables2 %} - -{% block content %} - {% if form %} - {% if now < user.team.future_tournament.date_solutions %} -
- {% blocktrans with deadline=user.team.future_tournament.date_solutions %}You can upload your solutions until {{ deadline }}.{% endblocktrans %} -
- {% else %} -
- {% if now < real_deadline %} - {% trans "The deadline to send your solutions is reached. However, you have an extra time of 30 minutes to send your papers, no panic :)" %} - {% else %} - {% trans "You can't upload your solutions anymore." %} - {% endif %} -
- {% endif %} - -
- {% csrf_token %} - {{ form|crispy }} - -
-
- {% endif %} - {% render_table table %} -{% endblock %} diff --git a/templates/tournament/solutions_orga_list.html b/templates/tournament/solutions_orga_list.html deleted file mode 100644 index fe83b4f..0000000 --- a/templates/tournament/solutions_orga_list.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "base.html" %} - -{% load i18n django_tables2 %} - -{% block content %} - {% render_table table %} - -
- -
- {% csrf_token %} -
- {% for tournament in tournaments.all %} - - {% endfor %} -
-
-{% endblock %} diff --git a/templates/tournament/syntheses_list.html b/templates/tournament/syntheses_list.html deleted file mode 100644 index b1cf355..0000000 --- a/templates/tournament/syntheses_list.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "base.html" %} - -{% load i18n crispy_forms_filters django_tables2 static %} - -{% block content %} -
- {% trans "Templates for syntheses are available here:" %} - PDFTEX -
- - {% if form %} - {% if now < user.team.future_tournament.date_syntheses %} -
- {% blocktrans with deadline=user.team.future_tournament.date_syntheses round=1 %}You can upload your syntheses for round {{ round }} until {{ deadline }}.{% endblocktrans %} -
- {% elif now < real_deadline_1 %} -
- {% blocktrans with round=1 %}The deadline to send your syntheses for the round {{ round }} is reached. However, you have an extra time of 30 minutes to send your papers, no panic :){% endblocktrans %} -
- {% elif now < user.team.future_tournament.date_solutions_2 %} -
- {% blocktrans with round=1 %}You can't upload your syntheses for the round {{ round }} anymore.{% endblocktrans %} -
- {% elif now < user.team.future_tournament.date_syntheses_2 %} -
- {% blocktrans with deadline=user.team.future_tournament.date_syntheses_2 round=2 %}You can upload your syntheses for round {{ round }} until {{ deadline }}.{% endblocktrans %} -
- {% elif now < real_deadline_2 %} -
- {% blocktrans with round=2 %}The deadline to send your syntheses for the round {{ round }} is reached. However, you have an extra time of 30 minutes to send your papers, no panic :){% endblocktrans %} -
- {% else %} -
- {% blocktrans with round=2 %}You can't upload your syntheses for the round {{ round }} anymore.{% endblocktrans %} -
- {% endif %} -
- {% csrf_token %} - {{ form|crispy }} - -
-
- {% endif %} - {% render_table table %} -{% endblock %} diff --git a/templates/tournament/syntheses_orga_list.html b/templates/tournament/syntheses_orga_list.html deleted file mode 100644 index fe83b4f..0000000 --- a/templates/tournament/syntheses_orga_list.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "base.html" %} - -{% load i18n django_tables2 %} - -{% block content %} - {% render_table table %} - -
- -
- {% csrf_token %} -
- {% for tournament in tournaments.all %} - - {% endfor %} -
-
-{% endblock %} diff --git a/templates/tournament/team_detail.html b/templates/tournament/team_detail.html deleted file mode 100644 index 21dbada..0000000 --- a/templates/tournament/team_detail.html +++ /dev/null @@ -1,156 +0,0 @@ -{% extends "base.html" %} - -{% load getconfig i18n django_tables2 static %} - -{% block content %} -
-
-

{% trans "Team" %} {{ team.name }}

-
-
-
-
{% trans 'name'|capfirst %}
-
{{ team.name }}
- -
{% trans 'trigram'|capfirst %}
-
{{ team.trigram }}
- -
{% trans 'access code'|capfirst %}
-
{{ team.access_code }}
- -
{% trans 'tournament'|capfirst %}
-
{{ team.tournament }}
- -
{% trans 'coachs'|capfirst %}
-
{% autoescape off %}{{ team.linked_coaches|join:", " }}{% endautoescape %}
- -
{% trans 'participants'|capfirst %}
-
- {% autoescape off %}{{ team.linked_participants|join:", " }}{% endautoescape %}
- -
{% trans 'validation status'|capfirst %}
-
{{ team.get_validation_status_display }}
-
-
- - {% if user.is_authenticated and user.admin %} - - {% endif %} - - {% if user.admin or user in team.tournament.organizers.all or team == user.team %} - - {% endif %} -
- - {% if user.participates and team.invalid %} -
- {% if team.can_validate %} -
- {% csrf_token %} - - -
- Attention ! Une fois votre équipe validée, vous ne pourrez plus modifier le nom - de l'équipe, le trigramme ou la composition de l'équipe. -
- -
- {% else %} -
- Pour demander à valider votre équipe, vous devez avoir au moins un encadrant, quatre participants - et soumis une autorisation de droit à l'image, une fiche sanitaire et une autorisation - parentale (si besoin) par participant, ainsi qu'une lettre de motivation à transmettre aux - organisateurs. - Les encadrants doivent également fournir une autorisation de droit à l'image. -
- {% endif %} -
-
- En raison du changement de format du TFJM² 2020, il n'y a plus de document obligatoire à envoyer. Les - autorisations - précédemment envoyées ont été détruites. Seules les lettres de motivation ont été conservées, mais leur - envoi - n'est plus obligatoire. -
- {% endif %} - - {% if team.waiting %} -
-
- {% trans "The team is waiting about validation." %} -
- - {% if user.admin %} -
- {% csrf_token %} -
- - -
- -
-
- - -
-
-
- {% endif %} - {% endif %} - -
- -

{% trans "Documents" %}

- - {% if team.motivation_letters.count %} -
- {% blocktrans %}Motivation letter:{% endblocktrans %} - {% trans "Download" %} -
- {% endif %} - - {% if team.solutions.count %} -
- -
-
-
- {% csrf_token %} - -
-
- {% endif %} -{% endblock %} diff --git a/templates/tournament/team_form.html b/templates/tournament/team_form.html deleted file mode 100644 index 21daee8..0000000 --- a/templates/tournament/team_form.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} - -{% load i18n crispy_forms_filters %} - -{% block content %} -
- {% csrf_token %} - {{ form|crispy }} - -
-{% endblock %} diff --git a/templates/tournament/tournament_detail.html b/templates/tournament/tournament_detail.html deleted file mode 100644 index 2c3b617..0000000 --- a/templates/tournament/tournament_detail.html +++ /dev/null @@ -1,67 +0,0 @@ -{% extends "base.html" %} - -{% load getconfig i18n django_tables2 %} - -{% block content %} -
-
-

{{ title }}

-
-
-
-
{% trans 'organizers'|capfirst %}
-
{% autoescape off %}{{ tournament.linked_organizers|join:", " }}{% endautoescape %}
- -
{% trans 'size'|capfirst %}
-
{{ tournament.size }}
- -
{% trans 'place'|capfirst %}
-
{{ tournament.place }}
- -
{% trans 'price'|capfirst %}
-
{% if tournament.price %}{{ tournament.price }} €{% else %}{% trans "Free" %}{% endif %}
- -
{% trans 'dates'|capfirst %}
-
{% trans "From" %} {{ tournament.date_start }} {% trans "to" %} {{ tournament.date_end }}
- -
{% trans 'date of registration closing'|capfirst %}
-
{{ tournament.date_inscription }}
- -
{% trans 'date of maximal solution submission'|capfirst %}
-
{{ tournament.date_solutions }}
- -
{% trans 'date of maximal syntheses submission for the first round'|capfirst %}
-
{{ tournament.date_syntheses }}
- -
{% trans 'date when solutions of round 2 are available'|capfirst %}
-
{{ tournament.date_solutions_2 }}
- -
{% trans 'date of maximal syntheses submission for the second round'|capfirst %}
-
{{ tournament.date_syntheses_2 }}
- -
{% trans 'description'|capfirst %}
-
{{ tournament.description }}
-
- - {% if user.is_authenticated and user.admin %} - - {% endif %} -
- - {% if user.admin or user in tournament.organizers.all %} - - {% endif %} -
- -
- -

{% trans "Teams" %}

-
- {% render_table teams %} -
-{% endblock %} diff --git a/templates/tournament/tournament_form.html b/templates/tournament/tournament_form.html deleted file mode 100644 index 21daee8..0000000 --- a/templates/tournament/tournament_form.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} - -{% load i18n crispy_forms_filters %} - -{% block content %} -
- {% csrf_token %} - {{ form|crispy }} - -
-{% endblock %} diff --git a/templates/tournament/tournament_list.html b/templates/tournament/tournament_list.html deleted file mode 100644 index 6a3479a..0000000 --- a/templates/tournament/tournament_list.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "base.html" %} - -{% load django_tables2 getconfig i18n %} - -{% block content %} - {% if user.is_authenticated and user.admin %} - - {% endif %} - {% render_table table %} - {% if user.is_authenticated and user.admin %} -
- {% trans "Add a tournament" %} - {% endif %} -{% endblock %} diff --git a/tfjm.cron b/tfjm.cron deleted file mode 100644 index 113ed0d..0000000 --- a/tfjm.cron +++ /dev/null @@ -1,5 +0,0 @@ -# m h dom mon dow user command -# Envoyer les mails en attente - * * * * * root cd /code && python manage.py send_mail -c 1 - * * * * * root cd /code && python manage.py retry_deferred -c 1 - 00 0 * * * root cd /code && python manage.py purge_mail_log 7 -c 1 diff --git a/tfjm/__init__.py b/tfjm/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tfjm/asgi.py b/tfjm/asgi.py deleted file mode 100644 index a75729d..0000000 --- a/tfjm/asgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -ASGI config for tfjm project. - -It exposes the ASGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ -""" - -import os - -from django.core.asgi import get_asgi_application - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tfjm.settings') - -application = get_asgi_application() diff --git a/tfjm/inputs.py b/tfjm/inputs.py deleted file mode 100644 index 67838fc..0000000 --- a/tfjm/inputs.py +++ /dev/null @@ -1,322 +0,0 @@ -from json import dumps as json_dumps - -from django.forms.widgets import DateTimeBaseInput, NumberInput, TextInput, Widget - - -class AmountInput(NumberInput): - """ - This input type lets the user type amounts in euros, but forms receive data in cents - """ - template_name = "amount_input.html" - - def format_value(self, value): - return None if value is None or value == "" else "{:.02f}".format(int(value) / 100, ) - - def value_from_datadict(self, data, files, name): - val = super().value_from_datadict(data, files, name) - return str(int(100 * float(val))) if val else val - - -class Autocomplete(TextInput): - template_name = "autocomplete_model.html" - - def __init__(self, model, attrs=None): - super().__init__(attrs) - - self.model = model - self.model_pk = None - - class Media: - """JS/CSS resources needed to render the date-picker calendar.""" - - js = ('js/autocomplete_model.js', ) - - def format_value(self, value): - if value: - self.attrs["model_pk"] = int(value) - return str(self.model.objects.get(pk=int(value))) - return "" - - -class ColorWidget(Widget): - """ - Pulled from django-colorfield. - Select a color. - """ - template_name = 'colorfield/color.html' - - class Media: - js = [ - 'colorfield/jscolor/jscolor.min.js', - 'colorfield/colorfield.js', - ] - - def format_value(self, value): - if value is None: - value = 0xFFFFFF - return "#{:06X}".format(value) - - def value_from_datadict(self, data, files, name): - val = super().value_from_datadict(data, files, name) - return int(val[1:], 16) - - -""" -The remaining of this file comes from the project `django-bootstrap-datepicker-plus` available on Github: -https://github.com/monim67/django-bootstrap-datepicker-plus -This is distributed under Apache License 2.0. - -This adds datetime pickers with bootstrap. -""" - -"""Contains Base Date-Picker input class for widgets of this package.""" - - -class DatePickerDictionary: - """Keeps track of all date-picker input classes.""" - - _i = 0 - items = dict() - - @classmethod - def generate_id(cls): - """Return a unique ID for each date-picker input class.""" - cls._i += 1 - return 'dp_%s' % cls._i - - -class BasePickerInput(DateTimeBaseInput): - """Base Date-Picker input class for widgets of this package.""" - - template_name = 'bootstrap_datepicker_plus/date_picker.html' - picker_type = 'DATE' - format = '%Y-%m-%d' - config = {} - _default_config = { - 'id': None, - 'picker_type': None, - 'linked_to': None, - 'options': {} # final merged options - } - options = {} # options extended by user - options_param = {} # options passed as parameter - _default_options = { - 'showClose': True, - 'showClear': True, - 'showTodayButton': True, - "locale": "fr", - } - - # source: https://github.com/tutorcruncher/django-bootstrap3-datetimepicker - # file: /blob/31fbb09/bootstrap3_datetime/widgets.py#L33 - format_map = ( - ('DDD', r'%j'), - ('DD', r'%d'), - ('MMMM', r'%B'), - ('MMM', r'%b'), - ('MM', r'%m'), - ('YYYY', r'%Y'), - ('YY', r'%y'), - ('HH', r'%H'), - ('hh', r'%I'), - ('mm', r'%M'), - ('ss', r'%S'), - ('a', r'%p'), - ('ZZ', r'%z'), - ) - - class Media: - """JS/CSS resources needed to render the date-picker calendar.""" - - js = ( - 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/' - 'moment-with-locales.min.js', - 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/' - '4.17.47/js/bootstrap-datetimepicker.min.js', - 'bootstrap_datepicker_plus/js/datepicker-widget.js' - ) - css = {'all': ( - 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/' - '4.17.47/css/bootstrap-datetimepicker.css', - 'bootstrap_datepicker_plus/css/datepicker-widget.css' - ), } - - @classmethod - def format_py2js(cls, datetime_format): - """Convert python datetime format to moment datetime format.""" - for js_format, py_format in cls.format_map: - datetime_format = datetime_format.replace(py_format, js_format) - return datetime_format - - @classmethod - def format_js2py(cls, datetime_format): - """Convert moment datetime format to python datetime format.""" - for js_format, py_format in cls.format_map: - datetime_format = datetime_format.replace(js_format, py_format) - return datetime_format - - def __init__(self, attrs=None, format=None, options=None): - """Initialize the Date-picker widget.""" - self.format_param = format - self.options_param = options if options else {} - self.config = self._default_config.copy() - self.config['id'] = DatePickerDictionary.generate_id() - self.config['picker_type'] = self.picker_type - self.config['options'] = self._calculate_options() - attrs = attrs if attrs else {} - if 'class' not in attrs: - attrs['class'] = 'form-control' - super().__init__(attrs, self._calculate_format()) - - def _calculate_options(self): - """Calculate and Return the options.""" - _options = self._default_options.copy() - _options.update(self.options) - if self.options_param: - _options.update(self.options_param) - return _options - - def _calculate_format(self): - """Calculate and Return the datetime format.""" - _format = self.format_param if self.format_param else self.format - if self.config['options'].get('format'): - _format = self.format_js2py(self.config['options'].get('format')) - else: - self.config['options']['format'] = self.format_py2js(_format) - return _format - - def get_context(self, name, value, attrs): - """Return widget context dictionary.""" - context = super().get_context( - name, value, attrs) - context['widget']['attrs']['dp_config'] = json_dumps(self.config) - return context - - def start_of(self, event_id): - """ - Set Date-Picker as the start-date of a date-range. - - Args: - - event_id (string): User-defined unique id for linking two fields - """ - DatePickerDictionary.items[str(event_id)] = self - return self - - def end_of(self, event_id, import_options=True): - """ - Set Date-Picker as the end-date of a date-range. - - Args: - - event_id (string): User-defined unique id for linking two fields - - import_options (bool): inherit options from start-date input, - default: TRUE - """ - event_id = str(event_id) - if event_id in DatePickerDictionary.items: - linked_picker = DatePickerDictionary.items[event_id] - self.config['linked_to'] = linked_picker.config['id'] - if import_options: - backup_moment_format = self.config['options']['format'] - self.config['options'].update(linked_picker.config['options']) - self.config['options'].update(self.options_param) - if self.format_param or 'format' in self.options_param: - self.config['options']['format'] = backup_moment_format - else: - self.format = linked_picker.format - # Setting useCurrent is necessary, see following issue - # https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1075 - self.config['options']['useCurrent'] = False - self._link_to(linked_picker) - else: - raise KeyError( - 'start-date not specified for event_id "%s"' % event_id) - return self - - def _link_to(self, linked_picker): - """ - Executed when two date-inputs are linked together. - - This method for sub-classes to override to customize the linking. - """ - pass - - -class DatePickerInput(BasePickerInput): - """ - Widget to display a Date-Picker Calendar on a DateField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'DATE' - format = '%Y-%m-%d' - format_key = 'DATE_INPUT_FORMATS' - - -class TimePickerInput(BasePickerInput): - """ - Widget to display a Time-Picker Calendar on a TimeField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'TIME' - format = '%H:%M' - format_key = 'TIME_INPUT_FORMATS' - template_name = 'bootstrap_datepicker_plus/time_picker.html' - - -class DateTimePickerInput(BasePickerInput): - """ - Widget to display a DateTime-Picker Calendar on a DateTimeField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'DATETIME' - format = '%Y-%m-%d %H:%M' - format_key = 'DATETIME_INPUT_FORMATS' - - -class MonthPickerInput(BasePickerInput): - """ - Widget to display a Month-Picker Calendar on a DateField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'MONTH' - format = '01/%m/%Y' - format_key = 'DATE_INPUT_FORMATS' - - -class YearPickerInput(BasePickerInput): - """ - Widget to display a Year-Picker Calendar on a DateField property. - - Args: - - attrs (dict): HTML attributes of rendered HTML input - - format (string): Python DateTime format eg. "%Y-%m-%d" - - options (dict): Options to customize the widget, see README - """ - - picker_type = 'YEAR' - format = '01/01/%Y' - format_key = 'DATE_INPUT_FORMATS' - - def _link_to(self, linked_picker): - """Customize the options when linked with other date-time input""" - yformat = self.config['options']['format'].replace('-01-01', '-12-31') - self.config['options']['format'] = yformat diff --git a/tfjm/middlewares.py b/tfjm/middlewares.py deleted file mode 100644 index c5b8987..0000000 --- a/tfjm/middlewares.py +++ /dev/null @@ -1,118 +0,0 @@ -from django.conf import settings -from django.contrib.auth.models import AnonymousUser - -from threading import local - -from django.contrib.sessions.backends.db import SessionStore - -from member.models import TFJMUser -from tournament.models import Pool - -USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user') -SESSION_ATTR_NAME = getattr(settings, 'LOCAL_SESSION_ATTR_NAME', '_current_session') -IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip') - -_thread_locals = local() - - -def _set_current_user_and_ip(user=None, session=None, ip=None): - setattr(_thread_locals, USER_ATTR_NAME, user) - setattr(_thread_locals, SESSION_ATTR_NAME, session) - setattr(_thread_locals, IP_ATTR_NAME, ip) - - -def get_current_user() -> TFJMUser: - return getattr(_thread_locals, USER_ATTR_NAME, None) - - -def get_current_session() -> SessionStore: - return getattr(_thread_locals, SESSION_ATTR_NAME, None) - - -def get_current_ip() -> str: - return getattr(_thread_locals, IP_ATTR_NAME, None) - - -def get_current_authenticated_user(): - current_user = get_current_user() - if isinstance(current_user, AnonymousUser): - return None - return current_user - - -class SessionMiddleware(object): - """ - This middleware get the current user with his or her IP address on each request. - """ - - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - if "_fake_user_id" in request.session: - request.user = TFJMUser.objects.get(pk=request.session["_fake_user_id"]) - - user = request.user - if 'HTTP_X_FORWARDED_FOR' in request.META: - ip = request.META.get('HTTP_X_FORWARDED_FOR') - else: - ip = request.META.get('REMOTE_ADDR') - - _set_current_user_and_ip(user, request.session, ip) - response = self.get_response(request) - _set_current_user_and_ip(None, None, None) - - return response - - -class ExtraAccessMiddleware(object): - """ - This middleware allows some non authenticated people to access to pool data. - """ - - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - if "extra_access_token" in request.GET: - request.session["extra_access_token"] = request.GET["extra_access_token"] - if request.user.is_authenticated: - pool = Pool.objects.filter(extra_access_token=request.GET["extra_access_token"]) - if pool.exists(): - pool = pool.get() - pool.juries.add(request.user) - pool.save() - else: - request.session.setdefault("extra_access_token", "") - return self.get_response(request) - - -class TurbolinksMiddleware(object): - """ - Send the `Turbolinks-Location` header in response to a visit that was redirected, - and Turbolinks will replace the browser's topmost history entry. - """ - - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - response = self.get_response(request) - - is_turbolinks = request.META.get('HTTP_TURBOLINKS_REFERRER') - is_response_redirect = response.has_header('Location') - - if is_turbolinks: - if is_response_redirect: - location = response['Location'] - prev_location = request.session.pop('_turbolinks_redirect_to', None) - if prev_location is not None: - # relative subsequent redirect - if location.startswith('.'): - location = prev_location.split('?')[0] + location - request.session['_turbolinks_redirect_to'] = location - else: - if request.session.get('_turbolinks_redirect_to'): - location = request.session.pop('_turbolinks_redirect_to') - response['Turbolinks-Location'] = location - return response diff --git a/tfjm/settings.py b/tfjm/settings.py deleted file mode 100644 index ae7f788..0000000 --- a/tfjm/settings.py +++ /dev/null @@ -1,210 +0,0 @@ -""" -Django settings for tfjm project. - -Generated by 'django-admin startproject' using Django 3.0.5. - -For more information on this file, see -https://docs.djangoproject.com/en/3.0/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/3.0/ref/settings/ -""" - -import os -import sys - -from django.utils.translation import gettext_lazy as _ - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) -APPS_DIR = os.path.realpath(os.path.join(BASE_DIR, "apps")) -sys.path.append(APPS_DIR) - -ADMINS = [("Yohann D'ANELLO", "yohann.danello@animath.fr")] - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '6$wl1=ehfoiymin3m3i-wyx5d3t=1h7g4(j2izn*my)*yiq#he' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -SITE_ID = 1 - -ALLOWED_HOSTS = ['*'] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.forms', - - 'bootstrap_datepicker_plus', - 'crispy_forms', - 'django_extensions', - 'django_tables2', - 'mailer', - 'polymorphic', - 'rest_framework', - 'rest_framework.authtoken', - - 'member', - 'tournament', -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.contrib.sites.middleware.CurrentSiteMiddleware', - 'tfjm.middlewares.SessionMiddleware', - 'tfjm.middlewares.ExtraAccessMiddleware', - 'tfjm.middlewares.TurbolinksMiddleware', -] - -ROOT_URLCONF = 'tfjm.urls' - -LOGIN_REDIRECT_URL = "index" - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')] - , - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' - -WSGI_APPLICATION = 'tfjm.wsgi.application' - - -# Password validation -# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - -AUTH_USER_MODEL = 'member.TFJMUser' - -PASSWORD_HASHERS = [ - 'django.contrib.auth.hashers.PBKDF2PasswordHasher', - 'django.contrib.auth.hashers.BCryptPasswordHasher', -] - -REST_FRAMEWORK = { - 'DEFAULT_PERMISSION_CLASSES': [ - 'rest_framework.permissions.IsAdminUser' - ], - 'DEFAULT_AUTHENTICATION_CLASSES': [ - 'rest_framework.authentication.SessionAuthentication', - 'rest_framework.authentication.TokenAuthentication', - ], - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', - 'PAGE_SIZE': 50, -} - -# Internationalization -# https://docs.djangoproject.com/en/3.0/topics/i18n/ - -LANGUAGE_CODE = 'en' - -LANGUAGES = [ - ('en', _('English')), - ('fr', _('French')), -] - -TIME_ZONE = 'Europe/Paris' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - -LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")] - -FIXTURE_DIRS = [os.path.join(BASE_DIR, "tfjm/fixtures")] - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/3.0/howto/static-files/ - -STATIC_URL = '/static/' - -STATICFILES_DIRS = [ - os.path.join(BASE_DIR, "tfjm/static"), -] - -STATIC_ROOT = os.path.join(BASE_DIR, "static") - -MEDIA_URL = '/media/' - -MEDIA_ROOT = os.path.join(BASE_DIR, "media") - -CRISPY_TEMPLATE_PACK = 'bootstrap4' - -DJANGO_TABLES2_TEMPLATE = 'django_tables2/bootstrap4.html' - -_db_type = os.getenv('DJANGO_DB_TYPE', 'sqlite').lower() - -if _db_type == 'mysql' or _db_type.startswith('postgres') or _db_type == 'psql': - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql' if _db_type == 'mysql' else 'django.db.backends.postgresql_psycopg2', - 'NAME': os.environ.get('DJANGO_DB_NAME', 'tfjm'), - 'USER': os.environ.get('DJANGO_DB_USER', 'tfjm'), - 'PASSWORD': os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS'), - 'HOST': os.environ.get('DJANGO_DB_HOST', 'localhost'), - 'PORT': os.environ.get('DJANGO_DB_PORT', ''), # Use default port - } - } -else: - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, os.getenv('DJANGO_DB_HOST', 'db.sqlite3')), - } - } - -if os.getenv("TFJM_STAGE", "dev") == "prod": - from .settings_prod import * -else: - from .settings_dev import * diff --git a/tfjm/settings_dev.py b/tfjm/settings_dev.py deleted file mode 100644 index a52990e..0000000 --- a/tfjm/settings_dev.py +++ /dev/null @@ -1,5 +0,0 @@ -# Database -# https://docs.djangoproject.com/en/3.0/ref/settings/#databases - -EMAIL_BACKEND = 'mailer.backend.DbBackend' -MAILER_EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' diff --git a/tfjm/settings_prod.py b/tfjm/settings_prod.py deleted file mode 100644 index 3d6a742..0000000 --- a/tfjm/settings_prod.py +++ /dev/null @@ -1,30 +0,0 @@ -import os - -# Break it, fix it! -DEBUG = False - -# Mandatory ! -ALLOWED_HOSTS = ['inscription.tfjm.org', 'plateforme.tfjm.org'] - -SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS') - -# Emails -EMAIL_BACKEND = 'mailer.backend.DbBackend' -MAILER_EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_USE_SSL = True -EMAIL_HOST = os.getenv("SMTP_HOST") -EMAIL_PORT = os.getenv("SMTP_PORT") -EMAIL_HOST_USER = os.getenv("SMTP_HOST_USER") -EMAIL_HOST_PASSWORD = os.getenv("SMTP_HOST_PASSWORD") - -DEFAULT_FROM_EMAIL = os.getenv('FROM_EMAIL', 'Contact TFJM² ') -SERVER_EMAIL = os.getenv('SERVER_EMAIL', 'contact@tfjm.org') - -# Security settings -SECURE_CONTENT_TYPE_NOSNIFF = False -SECURE_BROWSER_XSS_FILTER = False -SESSION_COOKIE_SECURE = False -CSRF_COOKIE_SECURE = False -CSRF_COOKIE_HTTPONLY = False -X_FRAME_OPTIONS = 'DENY' -SESSION_COOKIE_AGE = 60 * 60 * 3 diff --git a/tfjm/static/Autorisation_droit_image_majeur.tex b/tfjm/static/Autorisation_droit_image_majeur.tex deleted file mode 100644 index 7cb1727..0000000 --- a/tfjm/static/Autorisation_droit_image_majeur.tex +++ /dev/null @@ -1,113 +0,0 @@ -\documentclass[a4paper,french,11pt]{article} - -\usepackage[T1]{fontenc} -\usepackage[utf8]{inputenc} -\usepackage{lmodern} -\usepackage[frenchb]{babel} - -\usepackage{fancyhdr} -\usepackage{graphicx} -\usepackage{amsmath} -\usepackage{amssymb} -%\usepackage{anyfontsize} -\usepackage{fancybox} -\usepackage{eso-pic,graphicx} -\usepackage{xcolor} - - -% Specials -\newcommand{\writingsep}{\vrule height 4ex width 0pt} - -% Page formating -\hoffset -1in -\voffset -1in -\textwidth 180 mm -\textheight 250 mm -\oddsidemargin 15mm -\evensidemargin 15mm -\pagestyle{fancy} - -% Headers and footers -\fancyfoot{} -\lhead{} -\rhead{} -\renewcommand{\headrulewidth}{0pt} -\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018} -\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.} - -\begin{document} - -\includegraphics[height=2cm]{assets/logo_animath.png}\hfill{\fontsize{55pt}{55pt}{$\mathbb{TFJM}^2$}} - -\vfill - -\begin{center} - - -\LARGE -Autorisation d'enregistrement et de diffusion de l'image ({TOURNAMENT_NAME}) -\end{center} -\normalsize - - -\thispagestyle{empty} - -\bigskip - - - -Je soussign\'e {PARTICIPANT_NAME}\\ -demeurant au {ADDRESS} - -\medskip -Cochez la/les cases correspondantes.\\ -\medskip - - \fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$ du {START_DATE} au {END_DATE} {YEAR} à : {PLACE}, \`a me photographier ou \`a me filmer et \`a diffuser les photos et/ou les vid\'eos r\'ealis\'ees \`a cette occasion sur son site et sur les sites partenaires. D\'eclare c\'eder \`a titre gracieux \`a Animath le droit d’utiliser mon image sur tous ses supports d'information : brochures, sites web, r\'eseaux sociaux. Animath devient, par la pr\'esente, cessionnaire des droits pendant toute la dur\'ee pour laquelle ont \'et\'e acquis les droits d'auteur de ces photographies.\\ - -\medskip -Animath s'engage, conform\'ement aux dispositions l\'egales en vigueur relatives au droit \`a l'image, \`a ce que la publication et la diffusion de l'image ainsi que des commentaires l'accompagnant ne portent pas atteinte \`a la vie priv\'ee, \`a la dignit\'e et \`a la r\'eputation de la personne photographiée.\\ - -\medskip - \fbox{\textcolor{white}{A}} Autorise la diffusion dans les medias (Presse, T\'el\'evision, Internet) de photographies prises \`a l'occasion d’une \'eventuelle m\'ediatisation de cet événement.\\ - - \medskip - -Conform\'ement \`a la loi informatique et libert\'es du 6 janvier 1978, vous disposez d'un droit de libre acc\`es, de rectification, de modification et de suppression des donn\'ees qui vous concernent. -Cette autorisation est donc r\'evocable \`a tout moment sur volont\'e express\'ement manifest\'ee par lettre recommand\'ee avec accus\'e de r\'eception adress\'ee \`a Animath, IHP, 11 rue Pierre et Marie Curie, 75231 Paris cedex 05.\\ - -\medskip - \fbox{\textcolor{white}{A}} Autorise Animath à conserver mes données personnelles, dans le cadre défini par la loi n 78-17 du 6 janvier 1978 relative à l'informatique, aux fichiers et aux libertés et les textes la modifiant, pendant une durée de quatre ans à compter de ma dernière participation à un événement organisé par Animath.\\ - - \medskip - \fbox{\textcolor{white}{A}} J'accepte d'être tenu informé d'autres activités organisées par l'association et ses partenaires. - -\bigskip - -Signature pr\'ec\'ed\'ee de la mention \og lu et approuv\'e \fg{} - -\medskip - - - -\begin{minipage}[c]{0.5\textwidth} - -\underline{L'\'el\`eve :}\\ - -Fait \`a :\\ -le -\end{minipage} - - -\vfill -\vfill -\begin{minipage}[c]{0.5\textwidth} -\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018 -\end{minipage} -\begin{minipage}[c]{0.5\textwidth} -\footnotesize -\begin{flushright} -Association agréée par\\le Ministère de l'éducation nationale. -\end{flushright} -\end{minipage} -\end{document} diff --git a/tfjm/static/Autorisation_droit_image_mineur.tex b/tfjm/static/Autorisation_droit_image_mineur.tex deleted file mode 100644 index 4f14a43..0000000 --- a/tfjm/static/Autorisation_droit_image_mineur.tex +++ /dev/null @@ -1,122 +0,0 @@ -\documentclass[a4paper,french,11pt]{article} - -\usepackage[T1]{fontenc} -\usepackage[utf8]{inputenc} -\usepackage{lmodern} -\usepackage[frenchb]{babel} - -\usepackage{fancyhdr} -\usepackage{graphicx} -\usepackage{amsmath} -\usepackage{amssymb} -%\usepackage{anyfontsize} -\usepackage{fancybox} -\usepackage{eso-pic,graphicx} -\usepackage{xcolor} - - -% Specials -\newcommand{\writingsep}{\vrule height 4ex width 0pt} - -% Page formating -\hoffset -1in -\voffset -1in -\textwidth 180 mm -\textheight 250 mm -\oddsidemargin 15mm -\evensidemargin 15mm -\pagestyle{fancy} - -% Headers and footers -\fancyfoot{} -\lhead{} -\rhead{} -\renewcommand{\headrulewidth}{0pt} -\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018} -\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.} - -\begin{document} - -\includegraphics[height=2cm]{assets/logo_animath.png}\hfill{\fontsize{55pt}{55pt}{$\mathbb{TFJM}^2$}} - -\vfill - -\begin{center} - - -\LARGE -Autorisation d'enregistrement et de diffusion de l'image -({TOURNAMENT_NAME}) -\end{center} -\normalsize - - -\thispagestyle{empty} - -\bigskip - - - -Je soussign\'e \dotfill (p\`ere, m\`ere, responsable l\'egal) \\ -agissant en qualit\'e de repr\'esentant de {PARTICIPANT_NAME}\\ -demeurant au {ADDRESS} - -\medskip -Cochez la/les cases correspondantes.\\ -\medskip - - \fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$ du {START_DATE} au {END_DATE} {YEAR} à : {PLACE}, \`a photographier ou \`a filmer l'enfant et \`a diffuser les photos et/ou les vid\'eos r\'ealis\'ees \`a cette occasion sur son site et sur les sites partenaires. D\'eclare c\'eder \`a titre gracieux \`a Animath le droit d’utiliser l'image de l'enfant sur tous ses supports d'information : brochures, sites web, r\'eseaux sociaux. Animath devient, par la pr\'esente, cessionnaire des droits pendant toute la dur\'ee pour laquelle ont \'et\'e acquis les droits d'auteur de ces photographies.\\ - -\medskip -Animath s'engage, conform\'ement aux dispositions l\'egales en vigueur relatives au droit \`a l'image, \`a ce que la publication et la diffusion de l'image de l'enfant ainsi que des commentaires l'accompagnant ne portent pas atteinte \`a la vie priv\'ee, \`a la dignit\'e et \`a la r\'eputation de l’enfant.\\ - -\medskip - \fbox{\textcolor{white}{A}} Autorise la diffusion dans les medias (Presse, T\'el\'evision, Internet) de photographies de mon enfant prises \`a l'occasion d’une \'eventuelle m\'ediatisation de cet événement.\\ - - \medskip - -Conform\'ement \`a la loi informatique et libert\'es du 6 janvier 1978, vous disposez d'un droit de libre acc\`es, de rectification, de modification et de suppression des donn\'ees qui vous concernent. -Cette autorisation est donc r\'evocable \`a tout moment sur volont\'e express\'ement manifest\'ee par lettre recommand\'ee avec accus\'e de r\'eception adress\'ee \`a Animath, IHP, 11 rue Pierre et Marie Curie, 75231 Paris cedex 05.\\ - -\medskip - \fbox{\textcolor{white}{A}} Autorise Animath à conserver mes données personnelles, dans le cadre défini par la loi n 78-17 du 6 janvier 1978 relative à l'informatique, aux fichiers et aux libertés et les textes la modifiant, pendant une durée de quatre ans à compter de ma dernière participation à un événement organisé par Animath.\\ - - \medskip - \fbox{\textcolor{white}{A}} J'accepte d'être tenu informé d'autres activités organisées par l'association et ses partenaires. - - \bigskip - -Signatures pr\'ec\'ed\'ees de la mention \og lu et approuv\'e \fg{} - -\medskip - - -\begin{minipage}[c]{0.5\textwidth} - -\underline{Le responsable l\'egal :}\\ - -Fait \`a :\\ -le : - -\end{minipage} -\begin{minipage}[c]{0.5\textwidth} - -\underline{L'\'el\`eve :}\\ - -Fait \`a :\\ -le -\end{minipage} - - -\vfill -\vfill -\begin{minipage}[c]{0.5\textwidth} -\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018 -\end{minipage} -\begin{minipage}[c]{0.5\textwidth} -\footnotesize -\begin{flushright} -Association agréée par\\le Ministère de l'éducation nationale. -\end{flushright} -\end{minipage} -\end{document} diff --git a/tfjm/static/Autorisation_parentale.tex b/tfjm/static/Autorisation_parentale.tex deleted file mode 100644 index 6c56ac4..0000000 --- a/tfjm/static/Autorisation_parentale.tex +++ /dev/null @@ -1,66 +0,0 @@ -\documentclass[a4paper,french,11pt]{article} - -\usepackage[T1]{fontenc} -\usepackage[utf8]{inputenc} -\usepackage{lmodern} -\usepackage[french]{babel} - -\usepackage{fancyhdr} -\usepackage{graphicx} -\usepackage{amsmath} -\usepackage{amssymb} -%\usepackage{anyfontsize} -\usepackage{fancybox} -\usepackage{eso-pic,graphicx} -\usepackage{xcolor} - - -% Specials -\newcommand{\writingsep}{\vrule height 4ex width 0pt} - -% Page formating -\hoffset -1in -\voffset -1in -\textwidth 180 mm -\textheight 250 mm -\oddsidemargin 15mm -\evensidemargin 15mm -\pagestyle{fancy} - -% Headers and footers -\fancyfoot{} -\lhead{} -\rhead{} -\renewcommand{\headrulewidth}{0pt} -\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018} -\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.} - -\begin{document} - -\includegraphics[height=2cm]{assets/logo_animath.png}\hfill{\fontsize{55pt}{55pt}{$\mathbb{TFJM}^2$}} - -\vfill - -\begin{center} -\Large \bf Autorisation parentale pour les mineurs ({TOURNAMENT_NAME}) -\end{center} - -Je soussigné(e) \hrulefill,\\ -responsable légal, demeurant \writingsep\hrulefill\\ -\writingsep\hrulefill,\\ -\writingsep autorise {PARTICIPANT_NAME},\\ -né(e) le {BIRTHDAY}, -à participer au Tournoi Français des Jeunes Mathématiciennes et Mathématiciens ($\mathbb{TFJM}^2$) organisé \`a : {PLACE}, du {START_DATE} au {END_DATE} {YEAR}. - -{PRONOUN} se rendra au lieu indiqu\'e ci-dessus le vendredi matin et quittera les lieux l'après-midi du dimanche par ses propres moyens et sous la responsabilité du représentant légal. - - - -\vspace{8ex} - -Fait à \vrule width 10cm height 0pt depth 0.4pt, le \phantom{232323}/\phantom{XXX}/{YEAR}, - -\vfill -\vfill - -\end{document} diff --git a/tfjm/static/Fiche synthèse.pdf b/tfjm/static/Fiche synthèse.pdf deleted file mode 100644 index af8ed1c09bcb31004a2cc47160405bbcd5031e1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71958 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

~{D|}{2p(v{ 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 P;F^kphJ5)0i>LnwUcrUQ diff --git a/tfjm/static/style.css b/tfjm/static/style.css deleted file mode 100644 index 5c8d3ff..0000000 --- a/tfjm/static/style.css +++ /dev/null @@ -1,47 +0,0 @@ -html, body { - height: 100%; - margin: 0; -} - -:root { - --navbar-height: 32px; -} - -.container { - min-height: 78%; -} - -.inner { - margin: 20px; -} - -.alert { - text-align: justify; -} - - -footer .alert { - text-align: center; -} - -#navbar-logo { - height: var(--navbar-height); - display: block; -} - -ul .deroule { - display: none; - position: absolute; - background: #f8f9fa !important; - list-style-type: none; - padding: 20px; - z-index: 42; -} - -li:hover ul.deroule { - display:block; -} - -a.nav-link:hover { - background-color: #d8d9da; -} diff --git a/tfjm/urls.py b/tfjm/urls.py deleted file mode 100644 index 6e38c02..0000000 --- a/tfjm/urls.py +++ /dev/null @@ -1,54 +0,0 @@ -"""tfjm URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/3.0/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" -import re - -from django.conf import settings -from django.contrib import admin -from django.contrib.staticfiles.views import serve -from django.urls import path, include, re_path -from django.views.defaults import bad_request, permission_denied, page_not_found, server_error -from django.views.generic import TemplateView, RedirectView - -from member.views import DocumentView - -urlpatterns = [ - path('', TemplateView.as_view(template_name="index.html"), name="index"), - path('i18n/', include('django.conf.urls.i18n')), - path('admin/doc/', include('django.contrib.admindocs.urls')), - path('admin/', admin.site.urls, name="admin"), - path('accounts/', include('django.contrib.auth.urls')), - - path('member/', include('member.urls')), - path('tournament/', include('tournament.urls')), - - path("media//", DocumentView.as_view(), name="document"), - - path('api/', include('api.urls')), - - # Supporting old paths - path('inscription/', RedirectView.as_view(pattern_name="member:signup")), - path('connexion/', RedirectView.as_view(pattern_name="login")), - path('tournois/', RedirectView.as_view(pattern_name="tournament:list")), - path('mon-compte/', RedirectView.as_view(pattern_name="member:my_account")), - path('mon-equipe/', RedirectView.as_view(pattern_name="member:my_team")), - path('solutions/', RedirectView.as_view(pattern_name="tournament:solutions")), - path('syntheses/', RedirectView.as_view(pattern_name="tournament:syntheses")), -] - -handler400 = bad_request -handler403 = permission_denied -handler404 = page_not_found -handler500 = server_error diff --git a/tfjm/wsgi.py b/tfjm/wsgi.py deleted file mode 100644 index 7fd654c..0000000 --- a/tfjm/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for tfjm project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tfjm.settings') - -application = get_wsgi_application()

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�FYRkCZlZE&@3ur!j=l3Ok zuH)SVps}NLSZXIBRhm+@-oj?hSU{@-wmOcQe1>T8Bi;sLd zio*+I@kf*0zELRP7dw+=20v>@jIJ24WdSF{H52-_L=VIjcIdoX)lLj zG-e3V!LyIDyfiy<<6CTyh&`Lhsu|pQAbnnw4|E2e?Tn_>NpgvUaX$|gsrtmB89?${ zpj@aaPcl{eNZ3ups(3r;u!(qvvC>s;>z(Pdz3M?dcfUnmA-+X}CzU8sca)jS!gn^c ztx5deI(V-#jhC9xaKN)kRY+AU?>1YGM0d8^VIq+}0 zZlz4lz2_E*+TDWd<*~Pw*kRN^)XS&1>Y+MZ`q?=3P1)>>TGbK7%_(^cW8Z#!&X^Er zpVG~IU^8f9t}q$66?UxhDVn8LUEMspnqNqL(DLWXnVi2#V)KXbf|ftl1nu>yxJMR2 zSuN{UKLK13wve&b)xzQMQ6T$*r}`i7{!-Rx|9-L7J7@b45<$)Ak0`o+kwBrTte#Au zQIAv%W0U0xeEs0y|L3q+x<5a zLr-fD4|}^Ceu}lLmpwpC&rlU9s^;zLYGdv0{#Pu9j!s^noA$4mgh9sko?bUwgP@|~ zFmbpvL;@}X5taBm)_+C_(sJ^#2SLE%(gGkC4{MC0m%TN}=l}d9L~lF={zA0ayW62{ zoB`thE=ywQ=V1>L)3tFnz_8jPBgrxykUfr$V81_FsoLZtuKI>`>C8=6`A z_SKi_gWh{aUHfQ+p#Zv}H43Kf=~Thb(PXDAMV(u7GV+Q4 z+6GXhkvkx1wfu|UT91jlu_fb(25F(-{JcgjHHStAfG{zo1{hvjL@O(Ti{!MwkG_uy z6Nnkc(gK-o4U_G4#ifB{hG0R$F#1)$;-9NFn(D;`q$RhF{D*nI=Mo=rY7>6pf=)i9 zPk+K*7bzQfgj0T*klTO*5JHJ%3!H!KHJz^g*62Sq9Th`eJd!OR(1+Vn>g6<_`bo14 zmvnfN&?e-g7Aap|{^V|>&-J)HukJk_wq=kWeKop!>~e&|E_ldxq| zMBVzoH*5X9^y!SqpXKNG+)KcpTd;30Ahy#sW?Pp1$~8jcA`V(N+qvzO6sM%)Y|5?6 zul(@BRc6m3g|011mhxV#RoW}MwzB%bY*MBSl?T5&p^4o7^hd+kD(26pg~5_;V@w7ZRJP8|V}C$VB6DjaD7nqt-1wwNB(s+=nXUNInJp1q9B`xtP8VYlB#08> z(&KKEfq8q+JE@JeJ{isig9G7}Rh5rXKZoEoE`rFD8iK?{i4!QO$c-NOYtzs2Ker3F(?2h2=Bcg(9&0-!`XL`?dItER z0&oy+BC{3dBKmXkidWR-hN}@X8GerdLKffoY?u3Ae@S$&;u_d&WnrXl= z@fLq`5SKosjLnm8k>A4eRc-o2X`*!|Y@96)1}nvNzNp3bF;{|RdSpfo#^l2@PG{L0 zZ(S6|P4j7&1BOtu#ZPR-sR*G;7Da1}l}!7A_pngM*fg_9nPTESNi%p{b@Qov!9t8& zQHa4W<;w^6h$d-&$RH{zRg-BQc-M?Gnyj4r1Lf0-ew*85e%RKa@2~io?(dvTlIItS zs)?{9&7yNGVPnHG>F`KAGc~v`VhM(F<@i z7~lsz_J%{GE7+Z;oXd^nw-M9cJ9c?nWD{Gvy2SlzcS3kd(zsSLAVl*w|94~SU2M-c zvOY@__*41{uSBqJvPG?ydLQF>0wRx0>!;i7h^+)7P#6%;{PZkk_6w<;iI&!+5$Se@ z=Y`L<-VJGukO|K$*-6s0ld`7OFJi!DpT{v`QX=e+z8@{x%S)^vNY13?{3nYOUjZh^u1uIChjK(ErLNMfiX}Ydo&&I{v zzCha-yk~;1EfRwOC)>a5u3rrj8N1@c5n&xYiRJ<7(Ow8vn>_Of1ZRM!tu*?f+{uW(+&-}n(A@Y=o?vJWr%Ug00FTRsg zc?BZ*_%rhTUj|p%In5ZDEV_i~s-Cy)!s3xJq`lfV5<4ix2fKakhV5lMlt|p;+gI5!mPyvG_lq8{w z@EcJuHA&b_L9RULznk3bqGj*?7~=>M|2s$j50Eytfi`MV0 z(EGXgZqL0zMp1@T5UAjzMKQniEU0=?&>O?XTnaXDAA+VaidQ%{ln!U@Ftu^&28zZB zve(j$M6w^{Mc@Ogeld3r%m(Hsd7r9HOkOg=>$0m$YmXPzwRbOQ;E`2XI(3|k&xVUZaP$2Bt%*e1eKCTS`cXj1Vp+!1eF%) zZ!M5>JnFsY-21-I_v?jcG1sgy$BdD4EEv^f=Dpa@aH{(@&JqVGdx}x@6WhEd10_$+VR17ex4hYpP%cO zzWjm$zu*kx;`#+=0pMW$jEeyD*EJ9jfd0HL+}u2{pXbKS%l*q*aSQNp|BN%Y052Cn zl;F=FXB;RK_!$>q`F`0W9vF|nFX!NdbpHyE3(5!m;hef4jIC_p2!IFRKr{jBr{&|r zfhgLWJJ3RbQE4>*!q#wbaG`|)uupPo2)G+a>D&ONLlkB7M9fXi%*^>ie|S089U4O#p^{vXtNgo~$DRZVznp_l1OLz?~gj5vJhU0;lw3=cOI&U4S;ufJgv2 z9Z<#%5Ezi~oOa?lJt8320t*2m01{qM$NEsng3q8ddA}2r#(#T1vAT9Ul6d-Z{Mev-?0|&@JX?ae2 zf@cPzbIrsWZh8t|(GE(>ciQQsPS(RkUh@QhaCCW1C@sjVOa#;^ zbiU)k3j+t1wlqe7+r|PxK%BvuL$o1qV^dcbI7HF{VeAHnI76%;$`D6m;DFk~&0Qca z5FLohX&wPi2x1Fyg{VPPA=e;M5G{y4jvYi7q7IRUm;$S54{>sJaDh8pfv7+nAg3TT zAaW2Hh#ABbaypVML=z$pF^0e)77$BMM@zUpMAibKj{^>W9U^<$ND(3lv1dJ98-SI- zd1!f056NFP1}ysHOrDb1$-x8ItE-923CV$_ppGSHg>ZHO=TFN6Dp1PC-*bQ#0%UJk znYmb=5}N=m4?tYt1L_IjbFvpO;AC`=ZDeq1E(HLVL&53;52{__zpg2Sb%N7asgmuuv`E@`ve2<<*X9K1gr#s zoy_F~G6)@{D{!KK00vVK4GUMJ64 zCD0f|tC?jzyJLX;QJJSY5dt7T;J1CPZ0-mXB%-g zDPRr2xtwKxOwXGC2@tG31#$Xz)&d7y@IRaV`1WVRA4`9J$N71lU^kF=%i06-6reI% zh>EePhJ&54{U0Sb5GiA4_z4yKo?C!bKX9%S=Jr=II}19V1FUdw8CCsow$IM_ z$&Q}j4OE_`ClmahKSKfR0KWf7RVOHbe?UqBCw0;h$b*f4M^z{7zSA|>7Cc`ki*RDdEP0yi*E zFam4D_MXZf!~qE4}$AotK5R1;``54Zoqi~>fAiEf`Fey5HME2>4B8{ zr^@~76#j+I4OoId=-h&!runDNeX4OQLljQbXDvW!K2?oDg&5Fx|Elet8g@UjiWBbf z8&>g;*5qHCAt&lEINdW71UTG3m>__~_Un55$m4#$60pB41hBY2E`*D_1H=xn8h*4E zPHlxh4G#^#esHlwz~MkExU-8DU|cwUHy|{>+avloCl-k@#2m1mAx;o;s}l=C6=G_I zFm<&vw}pEEMhc+l!x2`_Hb0m=-)$Xm2TTBq#}qJQzMCixc6P>q&2wVA0hW&}V5G>O z87ZLMV-B$gw(7c>6&wMWInEGA0JMV{#O=(Aved`~BRHZhU^O||!vXv2 zyQTEka|2N5FSG<&$GPC-84egg2saB=~AV*vP%i~;5V`r|kO zSr0JdpHmQJ6=N5K)rq%_lNZVj_^&v*0k<%a0Jk@QZ;6wK54;25gaLkGK=I>*LIDv0 zBm&?~2qzb)D}5i=fEIK>;TW9xpg>r^BjSI?{9yoUzZHnTFn=gNFUbABJ3Rz|Tbp0G zzogQcKnLB6u66+M-wS~g`S*th3hoExRQ(kX$kV>>!8ep*z4o`Rc?NFp#I>#J8?h$FY`a(NQ1oXOtS*hKlmR|_XlqT z%K3ne2Nfr<>_n^jt~~xD75hvL1A&}9rz-Y$OAb`W{#M2QuC@vS_i|?{Hb`TqDmGXN zWWeVisn{oIfLbHSeZYx>DEyHEP{4_T(7^ID8VB>>`~N`2hJ9DD0gK@Kih_&tzoQ|a zE%Seqo?vix5Pk;$T-D!|pOxYKI3KX^KUcATLyj>-!xlzY5E%mD$RW!X+s6ar1#C`fp42s=1zZ7-yJ)ZE)TDIeh2iWe)5Tdfr@S zw!J`op!e;D|88SIf;ht)rqo-(0jZDYmWF7(oDfAK=v^`ul*IPIZ6bU5cLN|Xkq9p&TPw%gCD=c308d8aqA!GzPHS93n$eL8!948!o6W7op5;>Ei%!%l*G8EKR* zl`iCpFWQ+p8jWvV@?R#UGo#&7dtVQU?Z?_B91*%TZe;~vw& zcst%(cQx~QXogz2^!s={^At~<`r(u*uNg-Yd36G9q$H%E^Y@<639g*?VsG7l*iO)PtB;*7sQJ<}VlIehj>WZM&H! zYiiJE;>BNm*K%EHx$t_@)4B^0dL-Q9@gY~FKX%Y zmM$R&hu;GZ$CdFZaWt=decn(<{qcH6%SQw|c+b;Vz^n%*i5& z;HJLal|#xFV)vyn<&BUISqWW%M#Lh@AyLk@)6LI{#&+|b$;HvEh3kreJRQ!|GO_K) zFciL=#0Shf4~G>QI83g(YV_XSmpX6Ad3meEeyb9@_G$&& zE1d44!gKGOo;y}{K6~K1wd$sMFNo*!YX?|H0$!IhQ~qa_N%3tvk&0YBU7yVPnXf{3 z(yXd0YrUr?po<4Q?{D*T$tuB836`$u7TB*2B&^KO*i$teMzuYR2%AUTevN+EP;Pfg zdobbL2B+yEmiTK{Es;`1ZM4SgCVA#9F1;%Lp=2YXR9=n9HpvakYCe4PmojdA?i}~& z-S$IG3~Q+3aoo=<7Law!StroOJ&@;8~Yp*a>gwd(*bvPHnNf9MmLlWAXge%sHJ>=oEIVAC2tMvLSG*8zILx}v&ZoGVtai$eAzcshsYV!ZI{VW$ww48KMcw88yYdn z62layC%fyA-EMArSW>NEc8|{rJLdD}e^{Ix{hBUmW7j{gQrgk_b!~5UbasB{tIsem zzV7x@{;j(7_fR!8;<_j=Zd`q}yLau0CFQkPguJy@M|V0s-`6h~t__4~p-4?F_0tHN zYVExxLnkoNzjUCrnV%z*ms8*CA;339 zDt^Yt7&G*wNhf$}07s5p4pon42I(DNG7*mhgZ8qOkMG-p_m0nAC}mCyCs|H?E8Mm^ z!e?st6htJw>%&sfJwI4XOUY%18Ag=Pn%KO}zQpVC#-7VxQr_1mspI~2iIA2Z_Lm74 z8@r@1GBEuKz43E7xC*Y(`03n~d3l6BpVU6~Zlv`5Q|aOXf4A)gw4S7BQw(?BOT9(# z0J?}BBl@IXBh`100^__Xhm{7l_n7lpSt%k!aoUPDmN_WGEfutT2x$zCZTOvelqS2I z9t4Nb#&>S=`W)k0i6AuWlGJg$;LT3o?W3%#y|LKRqT!7X z3kH@C2*P6QWbT%l4;n3Kt6m}~cei8x{MN7I5OzP~if8A$alP^cDE-^v&D~kDb8iI` z9X<&4PHXZz=XC1Jm5y{T8cdDsVU~p#i?AOy_w;_Cf%+^T6szaXQH8voW(&4SF_avu z+`NIakRska+mx%l!J*9QF=kv45h;v?gLT(+uV!uWg>t?w^`!5dTSaj?q4gv)V-{h{ z?Kjq3EBi6R-0nF+K4HD1ndv42YAkQnTA{_V|F2R=Yp@qn*DnAdCvTAP%v=j%m_J&J@_ZO!AbXD4!?HC14cgVL;t;d zx(y1{gspC$I0#K(=qguzB*G{pdKI{^?xdOuSA4Mx#9x-3oP9YvSRAwY?aS^XC6xh1 znTXc5T#VFf(o$a+yIXnf<=j4ycM7Ezw%#-KIvC)MB6)fBLm9>9E`{!F_!|fWZMKN- z_)S3M>H~*VJcshG2X7AQ8%NDHuYTU$UfQL=t@S!?+%HVM8)kMiO2)%SbHqepZeIYW z=clb^NPPF!%qwQIaFaY{#Fx&qJK)x8^d)(rSd4`f%M}(EPQ@kVpe|x7F^5GRtu-b^ z>Hyr8cHK8_Xi(F1O| z=oy4qGagI)h16M_6&_kb^ptNqrF6WSvj;9dn}=vZ;c&MthA)80r$Jm%-L!r{J5t0gW=dR)5iW5 zJWgw$9z?lz?->$SHc0eg5Crlg#>H5$LZimS*c;1RD0op0)hyccpwZ9nN3dZe^FD9P zu0fbnUxeb9Sde1%=$U++M@lwQx?gPpw`k&3gB2)2cCg4eUJk$_(l8j`H6U>gh&bk# z;rPU14RF=5%f28;4`a@(B8(a8Y|wZ=n0;A({xf;fTz_6o!k7u=3~Nsi7%Ik-M_@)_>tF&z#Dj)$#@%^goccSnvEiJ}Y4dFg zPmX*|n>_W#82mY+17ZT+-dtaU4k!(H4^opjN8~6q+g0>1cGkQd2x4=-?)|=qKf6fV zj;|UPQ`3*L%x7JzIO6`Wgba;_ROu51T~i^S-g7-p3YNmC=qGtna!@Mue1g2jX?~*7 zyr2-*Skj^FPgd$wq+bFCb#ikl&1HEM`s{h7dNG=#?>Su{-Nd>lI~Kl|R%|Vg7WPcC zPCC@R;|fcxbN=T>4bv}HN>{|EIBPjHUbZq{TZ%)~M4gAePb67Q>w|T_(=bI$x5a&{ zwa7`W8?GfO4CuZI+j%|c0w0ej&Qy=P`>C^I{VK^bH&)(TM0oKtA(2}zo^$C*QNpzH zH3s)Bw)R@Gmt6`YTLj6LTsu)Fi1CY%Mex5W>`FYG}9=#|am&w0;Zk zXeM3r*)-V3;T^%)N@i0Gz1vl;s7)Ov)6kT=z7&&(b`dL+EYUtXib~jVfUEpCyw>=B zKU|HkIsUriqX1bRl9?#Y?qkZ7MHOiR0ghJ z?=9P@oM@M8gz#ftiixkLskdW)Z2Q=!GCuoD-<&RkrfYJs9%p!$>L@Cs-)cDPmok&q zkbKO%eLI4}q?c=RY!j$4Jj+IkQ&8BOucLI18Po=EY^8Yw>7Q0%jT+)33D|7?_00OLMn;xJ3I-(@1yK zMzKn&v#Rl3UTU3dcOm;_k=+{)nY)AGt@4c}m34*Z*&f<8Z)}ed>-6pUZue0d4L22R z;qQK3I{e%tR1DK^_f1x_yF8U4gjBVOSsX}^NG2AUisiGv$${o4P2E2^TO1Nu=pGOj zUhk+NW`f3ENwF4nh$Tg9YNcG&x$gI&TD~g#i2lXM+`>T&oaaSEk z^+zlHY>}hf4%CpL^Yv**7c}q# zv&4X>_C~l2U&@D0Ccb(VynX{xMfSH2oE78pUAxVn^NkJ$F;h{Jd@WwPg*jEGX%}Cg z)!)Lmy4d|nc`_$uZuTk*f3ss zIQEPZi%mZDkyK%swE}FV_WIT11JC}3PWJhIbd#I!xjU@}k@oDY^a)-|7ZQ3*R~>wM zc+c#*5appz_Tw!7r6QK9(7Vh1dLD)XIn|NGYHE)#B*OKU~3bCklm620toPY*7Aah6H5 zi~UfiH~j=BqF#>-A5s-(;Z}(HRap2fp^jJZRtD!wXBsYp`DYvc48|EpK9G3)nUr@m zUKWG=)~`Irv7ch)SzgO(%+ZILH-*RE7hEpdvb7&Gv0-0Um><4x51-L0$0h?Mo3CZnqwFJex?uS`F(rtkHfq(KJy`Pd zg@g=vZIuNq=oQ~Yy1|B=hD&pD@wz>ySTfsrHhUQf>6~>b;SJGb^ALAw~W|r4+$MPDZDD}%x#@4Sn zs&kbUcP^Xs_^htUcxds7B!23##_I03!ZfktgNVme+&br#L-YDBKysaN3sGy?vKnw( zy*|n(VN*=U%95!`9(82Tya`bE@8^AW|A?*bTqEMn!{4yff8kOGywv}j_hSDSJ$T|? z1{}rz|Dp%a+|K`P`oD8229AC*_wUZ{pRxIO7h@+F{Le4Oeu)+S$u0bw=s)n|j#IZV za7p#2rg7#hK8fl6H?jRE-G4Uy-reZXnAOHVJbxJA7DY<9bExD08D9e%|9Lz6BleIE zp4r%Xqgf@m=)RA$s(S}2G7s_#wKx;H)Z9BeA8Vhn*M!;ezEW;4FEC4IznpT2tKAY8 zGjr5b%9?(Qp8Ekm*1!{|BCF@dGL54wgac5wE=*FCl?u$8%9PAZG2P*=#6h<6ugmiu z>h|wz-4{vMe#{_np@k`!gEV5yMnHwIYFqKDcE%3o#)A@mp=c3RM2HU|yVM#bG$t^B z8CiMQtA)-c@ykGd98t9__WCjM{?JAVP$B4U>= z`!Lr0u}pGz1`|RuIjVYXtOo^y=<5D2wJ`~j<5+A*>cui6Rc>HK~L>6QLP&)##WJv)DvOCd_lf zL0r7*CVEtbyE5`lHu^@k;pWlb@+x#ZmZAvu>-K$p7*RW9@D#Y5V%soZ_}ILN7buPw zNu$@on0rjI`-y^*aifosP=S_CQ74?($TlUd&IkPwg3qH9IEUOWm9kP0`ntY7A?7-(RIXyNyGKFCwcKZZWEn=r zQb*a^tjy|00;Lli5Y5 zj4CCek)ZV1w&jvDX~(*y4-cnUaUmidS=x^VLm-MHjeDL%w@i4$8_HLI#?Zsk2x#-_k3 zq%6EUsnYtvAVB4K#3!fOXC#?3t7O;5>9uuB*mf4{0|sVxyo3*p$uhkP$`*VaB07&a z3aoA0bPl&!ST?MVWM>yx*rT@->=y6=7oX|H=C9OaV zPtl`2rR1QjHZ`Qi#%<%QO_GYA+9mt48xFy|x56RsLr&{MvFjY&a%idZ=&lDlX(YVt za(H(I%a(x9oi52VJUsoCYoZ5hebRN}=#rm3zRCL{o%3C+F!_z-Kl^S#KmfQIKeMII z+`WIx;lSQMbGRDqPQ(Zo{+;u00_>yk&<}MrAk7_wTMy|}?jM6vmAKCDUu1IG6LrpT5c}UtT<4@FBtCfqdS$#ep zP>oUT<5A+7Ji^QjX->^~{ZMacX(y(%obnQ0bjhu3ly@cgHRK}7a!#-BiYb4HQLnjm z>t3wF+dO@_N}5Qig&K=Cs}Fb?fe!O<)Eit1KI#qcMf*dZeiHM=r?*C7c$A{$d5rLM zzm^X>hc;mSW>R50{VSKwJ!cKH0h&yTz~SIZ-gpJO=cG---2@4wX=(c961SXie7`9W z-$G8n9&IZRcI24S=n9F|cM#dpt;{5NDwQ3)O?qCSW2V#vMLxAn3908@dyDY_Qd#Bw z3k3Qh!Sgt8=iy~_4>G?HRXTLWV4(yUaojJLco!6;6KB_wrs(p~dFm4}H>-20K zY4Tnr%L}x(etQt~%3$qBuWn@<#HH0oc@v}s#CAmAKgLbBYb`d{xk`^ciQH1Jers%! zl&#b!sX(Y#eoHGYK*_IOBEooo(J|l#){RF5wJ8M~H*wqEERJPm{7$1 zW-nBA+lNf~mPvmZSN7cD&BG^T?hpD}FLF@jYu8itdpx947#du8CUszZR4PH!_`r`W zc6zs$_Y;>iMGZ@HjYle>hoXcl4hv43)mNs$Jn!zrgY464H8W#I7#XIjGw3X6xYnWF|RVbOQ4;qrvd0eM=$iF1w zM;iBhEN*e5sL#p61Ku=L^640z>s+`+{rTTe_7B%HINv|I167{?M5pKG{o79${!gd> z|F6@7HxmEq7Yx7n^B2^!|ID4e{Sotjrjnmk0P);F(8Xzh&S_W+m;-_QsDFbo@&7`P zKWqLcz*7v)^!?LvF!u)ze>VNI^5?SO<^KZUbd)~d<$xz@!Z6GKOh{Xdx z760v5k^gcgKSri;01-)masMBD9{K&_NFdPnk1ryvfcW5(+}Zb#W)3d4aA#*L;KN8a zAac#*hsZU39Pl$p@Y6@DAA)>Ozi@;Dk!%(~)bbA%Ky>k6g4w|TKk)OigGDDnJ6yoG zzh}D3$%nuH1}5ST~#W#Wqtu?;6zbKcN5^5giXHVtgAtObL`^0_|3Lu>Xoz4 z+r_Udc=z(g4q%Jl`pyL$yq?v(>=DwkdtcfM5}P$YG)wmXjsQT8YyH|Li7 zfJj$LMVI`&`tw}AQP^79btN#nvO|mc18lxmn?bkJ#VYu}wvLKD%P8}RGu?7T?;@Yi zyaye_Xn9e1^PH4bdzswxIA@XNK8y7)8Nz8#V0L=0G)a3oO-<9)W?e&M8Zf%Jk&90R zQR@b6_Y&p|Hg{evPv!XcciJJH>v1W{;w1G}swD*beJY zVU?C<6i8FH!VlbY{K~1oTQ~F$#+SFZ6?ttN|5}W=$YYBUcjb zqq>NoYn;rbXNtH5=#~=nkX8}srS)iH)tNzLb%G|8^mXhOb;=Y54<5GYRC~jgMH+7% zuY6!>GYN?vP|1f-7 zrS6gwcTz3KOeuMao^@b3O3JGS)Gm(8^mFL6k4Y`o>z?3;VsdxRFsNk34DWoL9sjak z6eXC|pp0%)tE>z z3@>`|@u!Iwq3Wa;7I@HzuR|WnaUrUfvJ4D0*qOSl6e8WIk(?&1Y88-JXy4G4_h5r?*23{$--6dk1@qN z-XFbs`aE9x**h5|bmTOfwqm)7TLaJ zRb8>K1!R+5e)3yG%@K*wxl76eZoev2;J-UL;WvC>Fzj@q-!TLp8ISGE!$+pZsNYa233?s^&s=@qOU zyM49ga(H(`dJO{`CH=z9P%pf>InuN+T54XB6*y$J8zd@?mFd9f*wv57ukXPTaf+0WRWoRt)}I>wpeQ1#81&gC*( z7w41*^X!?i8^oPEm$g(M!o{Rg9+k$IwujEK7UnlzjuyWnF(Q%Q%?(K!1mN6GydizCZk=&)=yG*V zsj%iPf42ELg!v1)2~_Jt4VRKm*V+q%dx%?7HXC+nQ9js9?LHeEl~F!s%6&>l7cCjY zv)-ILPChVQZ_RyufP9E=bZ)B3Ddjgu_RN9#w}w0~4ERSSfA#fC{}j-wrG=qJY(e?i zTtIhes-+nEwNmss_LZ0T-L^IS6D*bk+}R!lECCOK8!6^^S+KEr#zjSuvF~9ggeY(o z*?YKxrW4q{T7r$a@lXuX2sQeLo?wl42gBZ?9&F_1qPM^?qSyl8aoB-9*K>tuimuqj1#CLV_pE+XwT< zn+yu`_(bSZh4h&ZWzr!~X<2sq5JoW>Y{@(g0w;#zFj|2Otw6k|^x;kS=B{k`U3+;g zODSVyX=T>C>Dv6_TI%#92|lS~V3N{JIj+lJA5@Z(6M6`b~>iexgpKSQ*5TUF>5)02U*10@}ImHpH>J|eQB`t^a5_fC|7C3AwryltB>#Np;-q8T=n_wkhx z+o#?Q68;Lc_4C4SjGhQwOZT6RV-i;@-4-82t)M7=p=jIVa>oSnZS96={~I+E4uS*Q zs6CZ^MO9l{{AIq10=>{z!NvAZ_fYs>cqC8{qqH^Xl}?b*O^36x2NSPgHVQ;)vGUR6 zmU~*h-j;I8`t}I3h4@;qyC)NdU!)d3`ZbF1ie@tU7e=`mjrXW*T}vmv%C4*EbV)r# z_E(|}>0s?D$;za5ocpk+5l(`s)_ui^>!7ki4}*9^=lZ>A<%CVCk7&2Gjs;r<*H&k| zt93$RX-l!sSK8`5=uCdXIM1@jbag2}?34bb7>=H?`XX1)(L5!1-r+<7Pa93`jrw>o zR8GtrV^i85H_KmViYQhM7U#=QY&fw$m~Pc`Bx>ggM-bs2#3$BK_KY6te#EZaZVn!wQy%^gT)|y-?SW-C-Lr&G`qq z7PsR@3$z{`XOdb!sEfTy?nrdG@M)&oji489!t;bF&w4-37?jq&>>uyjYhxt$CTTe! zl1eX)5sxHxZS2r|(j$`R(x;Z1JdoAp?>O99=XG#Z zVM={k#(mCh9mh z=)ZO3PZdTFdfwo+Y+V)4 zJlBh#S6{c%Cw3b#yc|#@Y!xSZ5@M*SVeovZ$A`<&=-T7*woemrZ&3}^V#llkW~oD? zpWb_TtVOV4$LgGhildgoD{)&3DTxUker?f}T(@drT(O@#R`_wZb{{+)Q#C+a%8->e zwE4ap_Dd7m8ywdaI?E#5WK&SgL$8UOM(*LMzP_|et@`wVVuVJcab&)vQ!QQcwm(Yn<=ueH{_GOI5WJIB1k57PIVKv6w zVYz^GKZIlR=*vZns?f-}b~$9OnLK6ts^;nqOABEa(~K!b&n3?#6NxrQ;0j%SPB1te9@ z2;4GDE6z|hj+iMZ37bWtA0QRJMZ%F`jgs?-o4#C#g!nyz1qp$F9s!RT|3v6_fzY0i zunQrJyUt$zj)mIJVvXEduUH`~1u@hGc}HdOYCUzQ`|(c7r1$F1zR#O}9y6rd_q*CS z=laA}a-Vs`I+AvOpew8iV89fK9Ede1gsKl5Ib^4+cOtK~Qc*nAYPh1os>Uo&H;cGE zaD!G{E;#AJ4fO}jcvbupYpeVF*(5LTPC_Gaj6R30DUIn2yKJ&LIWVjnnMVng1n#MQ z3BviLZJSWoJo~DVj7Hd7ctnx?b8NBnfa1{O@D?T|i=7YgB)upy+_R4|ZAHEbt~mu= zuD<_CF*H{|`jb#pbex>kvkP=n>IDLvoF6@`r4C=~Z6mK0M{J1}kh|Hrdttu`l#Iz* z*@DdQuomaY%&_QAphBnI7co5U2x}8hrzub9i#ot|LN8(%lAB_bvUxn`Iyc0yf!x*< zu@}rAns02wrYBn~{_0$GNrs?`&R!r8!Co$1P^55dw-mnp4^@ zwsMtD`G#^yn-Jg^U!-sTe3RoN#mx_;F1dX@=5|(> z4ObxZ?IPwDA{GX#q3Vn=H(#ma^CJediR|34(5#uKkT242na&qYmq}ik^Ry4(y#T$* z9$g*9M!frRr{SKmINC>Eh15cw0G0X>o7ABm!{i}}#O@VXO7o75_>~Bz+}$g;b6>aH zNhdKUiPOxucc2u7+Kw~6h)AD5Unqm*K}=*wEH$t}dfOoXCL8U&OV3-s3~k}71&w~3 zz)G1@L%f^EmO@B)En&Ovm?VFujl$0j5s@{1e&~}>o1|qGM_I+5dIJOg%ph-u=zu5X zB{oV-gil1RHohG``HtGoCf~)!y6^FBdbd4^chiJT2j{HBI89&S={5536HW3nhhnmZ zaSF3f^}OSQ>5YAs*@-o{8y?9ub$w6;w@g6Ko^a~sCCYT8_b;DGw7+}2#hcicU_~Jf`=&W{rSRTOOxU|`jZ<{Shn}8Uf;V=)Uj{iy)Rn8 zS@#;{TIJ_!_#-`N4?Nv9KEAd9evGs^Z3ci zmDs6hqMPf2WS-8XiDND=%F@eyao^{Jz0tlZ+uX9+r9vPve(-r(-|V&yPn<5rs0Z4c z4?R#-lM>Qu?S2J8vtagH^QsQJ$OW#*_ZyF}?%FeF^6eef$r(zZ*azv)Q*3t1tdc2BWVH5i7wY3uIZDp3+d_fEEF5bSA zDR|9PC{OXNsQDFkj`#PPKFXFnqE32eD)^MCgy}|UIWhbu!WqHSS>@GnEOPLuH*EhH z&RXBZc#|#4G9%hZnUFpz&?GwHH3M5ygF+1>w_=bP>eZ&q*E2Pj6bF^AriV>bzkag? z-SY`1C7G!mzlm*CBBhMc!dVda5@~U{`cWos!fWcma!)T2yTLiKs<2N%J;v7LgHJkF zm6r6Ff&b-c8jOni5lO!qYv3{kc92AB&6u`aTtXZIUg;*RB>lLyl_*Ms)9C$k-X5lQ zEwwt*sS!`ET^0#9gznl{Yl|eafj&fEZ6gJT=<{lud_|CO7wW@~m9tls0;k2Si{$#cruFhxXwyz)l!2#*_pbZsGk{QP4>`x-T6%2vng(d zJSJ}|?zv6iJX1CDg}9z0$liHYvTz`H)IFV{Sg~>->fNde-Owts&(|I+zK?VG;thDX zcxow?U$;p1x=5txT70y~$MlFQ|Ek_1+9M&84k7Eg4JQtV?5he)#Mu=k9_lyzMS~Ss z9k~iG7+wlNU1MZp4dCc7nrl+E9jklh>nbA@5UToG8CL+c%J1m}BX<8?IPJGx!mLOX z7J|`_e8q7t?K=Cy1H>CxN9yCuM4IK1jvW2g4i?GEJfmYlXh1X-`E$*l+b(x$kepv$ zeZY*vV^+(N3b?3EHy!N^+{T}zT%FfF|Min5-I3;tH5{!gjZdD66rna=*>&>1|2jQ8 z1VStfjaQU*x)5rFtDSb?WeQoMbMmze_JVjMCru`1lA4Cg~z)08X}1>$pzOX9^Lj;@l_pq$smDukQRt2 z#hGKl>!%|O3LVMyzk!cSDsax*h=MmtL7r6|LB1h%wBmn!{$tTRXw-y`v-3&nm~m|XPU zLw&JnVt!j$d>oP`vK8@$NTjjk;?-Vu44a43QV6~B=R#|u@?`FwW`U&41crt)-1=;* zxH3;AcE@bLEmN{Hzi+5eD#%E*QXfw)rZ*(chU4ByF&UyPqc2+~GTy75R6&>Mc=eR3 zuV?Xq>_D7MBS?~Qa9m_qjD7W;;^*;nDJp40va3_B7OP~#*2m%j^zd|%Lo4;?@+;6*e=aKl`JK@WXWo58zs*2U(B1H5f_!noI zheYl#dSk4Y^H}uBX6KThak>sDU>*{`E`4&9giWpOIv0sDg@JZ38lTsfmozy#fjl7+ zcLEzelw~{9g<(zD9@&LKmYv#_#>4c+v)^Gr3+_cAEDO*uHXD~);Dd(#ogIZcV+~HE zH`vPy36Js*3eO>*DV(($pmc<)oN0-E7$JFwW2aeAiL>?W3)vC=SY0`pmXy)=C>Kfd@UR8 zQq|n&A=;(mFSMP;v`6js4MeI-ypi3qTwWcOIe$4^=n`Dq?^<=QUEMJ_zb=X|iT|vh ztyeoUD>#oMSB-2jkTJo&)BPI6G$SNMzGzcaC#kh-x`cb#{py-u>#J7dC#3=3D%yDM zSRqT>K{N;V7Vd|fn^VL$+wbvSH~JYpGm>6vsHn z^Q`su{`rlIomZ_Vy%}_TdDpzoI!>($6 z*~j&PoPpYr^99)K$8N)0Nc5F{<^&2M4~d_moG-obl*@rQ@;whavJdPvo|S4f~!5OR7s zZQ%{=2DaEP!X@Pxp#sdxZ#_Jv!<~-0yTevN0 z^|*PFX6V4xeMq?>L#OEY!X>M_hSJh+@;E*pYROZ6G!tT1&FXaw(O{5be}Ym#o=xx6 zAg|X2JE%DqP{7S~x zp%{-Ke$JC&rrZebC0j}N#;1+Z_6yjXbe;c?w08im?CaLOgO1fPJ4wg3*|BZgwv&$S zj&0kvZQHi7Z~Fh9_nvdVd#diK`c~~#wQH|A$HK0)YOFb*@q3t9Q#tcl(`kzwcCc=k z?e2rdoz@(U9hf%^+CRmrR!;sfF{f=ML_L`Vjs*}d9IbF3h2n-7pi*-^>TDBEZvIhB zry0;zbw#$%fi#wRcXVq$4~(xx^DStrDQXFKG%msMiP`0EXZh~3jjqR|?030H+;;b8 zeAlzhAS)h)KX-E=suhy;tqMPk)!((P;4ClvH5li{>H9!}IOh3aY+kc?A%oGWPKFr6 zG53ubGUP->{edp_oiEC9n>bGJoj`)Cm^G%s;wYAS%flQ}jwUboqJ%7ia73T}=JPn{ z&&SQ_$-Y0m%ZBR~_Q^Qon`q)M#{9jLNBM($a35YDho3wn4fK6c77YG<%|rE5^Ol=; zJOz^LDb^7zae0N5Z6YVq*{2UnhQioQJ$mj*X6CtnjDl1Ru3H=07C+EyWhYTLCP;tz zdBf$Gm2FgK$)7h{wZnUkR4)NeBt_H34ZFV=p#r%nhabn9>N5mgAZfHc+4*y&#x?rH zqSy?XsRm%*kc135iK$II+ty!P1)AheSYHN$NLX+lPklh!7AhYnicZ zU|~@uA3#-gN9`TbB_Dx2cRk@`%Rw9yEgBf9Z)(C`BM$@z3Ev*g$_XlGp2xDVaFV1h z5bY^-O~0msmk#R25S0Q)RU4{UTv%*Tk+7RU(~S4G5N#V6T()v1T+(~wAI61r`W2!< zIyc<;60vcOd4=659Km{W&Eo|%@jy7p(y?XRZwsDFY*0v>Opn!T2TQfP`N9@tSunPZtZp)m>BR9PFApVhg=9TQ38@`J(eSj9bicX{P zYHvQal+Ucm#5s7M53+%s<^zR$ujiI$7Ryy#%DMbfu*RQSa-^1o_;4*?8PgE(71gKc zGbmllAi;Css$t#AQ`M1;eTB)*r=aruVsojYUH)rGsM2V>Zk&*~5OAME@ji>^$vM%> zT~Y1Jz{e9FbTBacDt~KAiuF3t zRO8kBEiQlD%%cZhr@6MF@V4Be0X=L+rH2ROJB@p-yQOh0tPo_UWet))GG|=~#(_<} zYul8my6Ck6yNAAmpi&SclYU9+3i->Vbck~d<$*hUY=X5sycS^8t({zF`#M`ON>HbD zTkD!hLe^&(CG7-Y3(-%%dP}hDmz5y-;t*cnzTR4u<93q8sl$gLhxZIeQ;I@2cAe%O zlg)CXeOmH9>&JB&8uoHtzsMpnTD4Q)0i;#(_;HW@1? zt14)tYS=t$xpJ7AES2~{4u1ac0Wu;#sAzongB8s<>_6SNXppzU-w;C*!4N6u`AQk8 zmvi7zo60K7*e1_gCc8FPcpW*`HZY4AGdNvHo({*kw_c{6GDHe5i84}BMhv0zi#j6C z+C@>`*>44~W8%xv9lKfE%YjHK)Lhb~rFkZaVDiW5No*5%e*~RjtwPXnZub?i_muX& zs=(tc0{eyNUOxnHW4I<&&61lXh>s9KYZT-%g zy0KLPv;G7+;rX_M$Zq4s(r$?(oi(K`%favWQ4{DssMnuvi@5F=4-$$rR0`vFF!0`g z48I>pePF+_+-ThVnOR-UwLll$Gugl=0}^b?;s#?KgX>HjoH5R1G6$O#;EB~hcD>}XSeKSbsNMH;fxu}B(fT^s<6 z^C3~=E8Iv1-9q^K<31CnaNBCpd9V;svjzSFBWj-XZu z+eGIc;AeI?-jq2Y1s#Uf+QaxewkQVHq?lP-71#~J_?CG9Pd?9?BCQ~9voCM4uKx%E z`xyx!q9VD3e$T88g2+F;%wZi1b_h85{=nd)6PN$8Qr(K=OWX@8lxVTbqgCq<9V97#l zjVO0z4$yB@IIL(myEmrG0JR=Pgz^Kq$P5mgOIxxHYo1)V0RxKl(&4RMb0&rWafd12 z(bU7?nTeL^tWLn5p~rLA6HVx@IxA-WxzA(RiZSP!vt7Me$Nzyh;E6y3v{FIk19sik zzqQhzOG8dUYC#cADa`ljV!w{J$O+Hl4+dtXsXK)Gl%S?5&*4QzBEF1#J+G=MMl)59 z#y1)@%oEijJjfwiY{wyXFD5YY-r!9&{5KZT&$}<@`(E@aUp*Ej4LuMxV*vd%cO(Uf%7BLoh_d<_4Sp2$} zb+FsikuM~`x+aaoo{SFm`)${DsE0GP)6M`A<@Yhx%{I-?V>WaGdIz5~%x{l)a8X5r z3%7S310L`=a1t@o&Jr2AP^WA`EpUD5f&N{7{do9Tmf=RQ>0Zq&xzVU#=isdf`Be4>t%|IFpzxS* z*iu6TS4LULpk^4!<4sx|r=}`Fpqf+b8Az0<7phkS*sxlxU-jnx#G9gsh=iGgnxk*P zwL3e4*hrWr-e7p{==3hk{_dN#3#{hM?xAPBKIaL~cjvAldF2g3t=$8Yqf_aSJcn_7 zOnN`58;r-!Uk+vpa^#i31>g!P-Ec>ydPA^hDV_cLl*|l3s7O)~E6dBKi_D*0Uo$Mw z%IdZBcU?ml2{^5+{BAcq%Zxs>8LtJ($)JNF%OSM6Aasn=qca~N7sgt8GB{tj%mDn@DQ-)WWa4PBGRGq>B$9RQZy6bT?=u< z{%&QyWeeR`MrLk|D=|jWiii7zmQh9DuEDaPARQdRu&-O-c6b5ZHkl{rH+7AV3wY+S zg=me6Q_1Jq?dK(*L#TyC?wLmHF^_1zK*Qp`{(RezTUCZSNB~4#ASr($)c@v%|G$(x z{kvM?KmBvQl+t|oj`siP!4;(b@=3oGfV7HVPC8_ApE~6z4-qGGuE?o z_^U4eA4K~{k^etYG_x>%8N>WP#v(lbjzyTD0cd_A&9J60zvcS=W8nKpNUaI~|M zIbO=x2zN38*f~#*vomguUvYUmcpnkQG@%nf^4Hs++!j!x%Y=^&i*Homesw3?ha%uY z?%-k?B7At9?*09AU7`d3&JZcFgbbjec%6WGk>y77zIK6_k<;Io`tG+XtRg75e;*Q} zCQ*;M(mMZxTrG?oFPMTL@36F2_#=IZ$9!Hyh(bTEevLWoVI@T^bt5G#;RMgZ2~$0T zLn5Aun}k}b#u_E7M73yS{`j%Rrpu{VbV9KhRgJ?b&bUdvNg_u|*)t}!a$Xsv&LK6jfvCZt2oj37aHW%GM}NzUTvW*S~eT@euc)L%bGWm%YF}U0!+c z>I@?ko};D@Nbkm;uu%rWIx#stEbp4x_s%~JJ`lo(;x`RxUUc7cZ(ia8<0ygse(yr< zkzkO(LKH%LQc1-$j`16Y8#UCjtl%?8sS99|Ys3(kipvU4w`V73qY}&vgM7GkGZ)qOFAa6Y&C9 zp%Uz9VFpwMm&E@TiGO&tyoBNEgn!$$Wy!*Q3(`dkIqLT67IME~b``faP#|%`BH|Ai zm}-7a|3XBV%2xJq z;Yw9n__FguxG_&g&w+FYJZLXy0!f#Hz7P2TB>ET>x2P)6il#zO#=t;8z+f*ksJ@=t zW7bqoKgywhO^oTOht&(`dmkuNIX8lVFGTxYa_`tR101$%auG0e;*12rSudrro8Tg zeXFVZ-t|w9*8ZX=77W=<4U_g|V&?^>MzQKNOUGtf zEX6BP@}{6>I{emy9}R~a2qIXd0{6GAU63=V$RAcgup!Z_nqH_;*xd5mkt}TQHk?7v z$G*#do6;J;EX4PGCV%cp{(c0V)*c}9xvcDV^_qQNaH?-RzL0o9>~jl`R;7orG|ACK*Nm=2Pm z*R>7F`%#wWW`z;}i{Sr^Hv_s%DxKs(C-B*tK`PC5in8VUu7`JissTv$bn_<$i^kR* zwLV%#|M(7>I}M{TdMwYw{+tw#&cSK)wCiDue>fmG%vdN13V= zRKh~jBtn}GbXZy^m33}v8g*jT{*kfvpmsxzAC?8Pf`WooB_ajyJEqJ-wk#WdGoVaS zUu;n|PJu}h8tmI2dm)xQ7!EDlSz_E%(7U~ZsbLiXSrWMv;}qi==`*##6D|u!=mh-V zp-Z~Z*JwI}2IBj$S@-R1GfoDg;Oixk=Ral0SM?;tR(kRw6B<_<)hN`qu86npY*wrp zn@lY(#15n9JA5r8=uA#0ZQb~5^8-aXK~8>wd-TgXrkjf}7%YrTY^An#CWNlmX%Pa` z^m6xO-r&Y1Tdq4!)CSOneXs{i@eN^N695()SS}*k18NqMwAqvixihM(wS{)LT{$v; zKd2&D2K=DKZ9SD96RI9QzWtqsaeCK}V5b{sl9QiwQnOKbA+>aIJ~#!o!{G^}vZrQY zC%P-F5vpu`%JTF$oC_ixDmz2HXk1uOu(I5XM33Ca>(8;w1NVOO^QG|XVNxg+I!@wj zy>Prv5O{;6>FmFUq!D$;OT$@yAt@r#)^fr7C&@z*kk zM{Z`DFdIyefZx&!F$|$!G@pXU84T@vfRTx_aqJMyHt3(=MgM8$RzXDO&VlUmFswKv zA<6xaLczJsgZ0PyE-*36sh-Tt4}+I9gR}3rU&HiB08{ z*_=~Vt#z1+5aN?}Zv&vHSkHm$_;~yB zwXnHA&C1eqXGbZi@Py2j!)ElEio)Sm8-EYjok;m7mu$<`3+88BHjSI-nCt|N*b-!16v=iK-C^(Zo z;qcfX4_kV1dBXz#^rF5Hs@%c<0EqT~af}~-CFOa1qRlhVRUaV2ZgWb~!78S9&g01L z_FDzWR@q+mp1YrQHC8ogCZ5qQ!6c1vf9M}72*0Eb)$StuvejXFQD1^Bv^k=-R=&7D zgjQ0xgSsNi&pc8$lVC|K>2fLQ)*vdc1b`$c`+Y*-cVKvqd3?HY|TF zx6D54;-|hH3cO0Ye^fq#la!QhzX3=;Als~wUvN^$I*q*I31qU*&R8?Tfyt4`z2{d% zu%!ME&=MjUi9at^iWauE(1$kB`|fTQglH$CXrAk(FKM1)wGQ{z*L~k1d$#&8kSfrR z-AfKQ#JZW$S25Q^N@oJpk+ruMzv&x{L}R!3+}iLI&4Y&o+g+m_^F{Bc(OC+}nA|_d z(4O1&NY{cl2Gf?M)rsfiYH|%bcb8K@{yt$7gWp?9 zc7rCt^A>JbPA*iz5y3lK81gVe_If(ky_#8i#FqZYR7e(U%C-)RZUqQ(TY7vhApfZI{w&0CvbkhlN3RsBk_t zkZce^Pfu?@KDB+BqwH0u1DtMFxOPI@^_w-h93!6RD_PXhYP-Zn8}xcYX@@ky$Hg{5 zQj#)}-stqQ?Kvf=>}I^OwKn4;SHk?!;D8%0P&g?F7F{y^j5;|dse{1~o7Z)(=HBTH zvvo1FP{fTNnfz9tl-o_tZbW+I8Ixf5HScdu2xlH&(XalR?D-834{t@JpusYJZrsdx zmwHZdv*ifakW-Ca6dL*z+|DXV`Rb;4fVoR~=N0|kpRWN^W!rt-Fz6Qsbe@AQnOOwN z?COY^JA0_?W8XQ{vQ5biLJpSZJPR%X{xdllzU-kPMhXP5+l3;>O^w;eh&9PkIYXB} z43qWP*K@QA~(MMOmLY>{HVn`~8 zMX%TqaVkJthE%%qT5Q-ovF)=at zq{a18#qCjlkP|+OJ@HB~m$RyiV(zVS(zs1HXq{P{wXxmo_m2q0%+6!O!=E4rC@x@K zy-12be68Q#F1j{9Zw7#TiXxM7IegEp%bgmQ-2b$mq811r=7WLko48a${`i)};wRBG z(-dbS<&Gbe?@@PyP3%VP^GToX=;&eSpDx1a>`wnhkM}tV$1DM6;kQt8ymCxxRlH{{T&E?$kZ7#%=LWCKVw4<5k!7 znqtrSesg@TAwC|RCadzM4a=?K#`Gl=8K)wAVza3;sDf;4jC}WrypEwUjd4ZkFfaJI zZKcWD0(ZRQc``RzKDU>x6|FX8MZ3dog;ZkMXpFMzJ0}}epPG2soNP*kLm-E4miC4% zOjP}h6AiW~EG#rIm_0Cz0cajuk3tY1nrga7(5g8DB)C5xON=>WVk~A=PmA~}o%Syh z#y@L&VoMj)D=PAh!C@tAO^47hT>dT^tww06ICX%ACpa;gc_8W`IE@C>iagR69jl#JEX{Tad< zJCy$1uuX&@&kJ)t-Ze%HJyN)D=ubQ{0m=9?N1f1>rCdjoJN$<)w2o(1(b+U0SS>?F zTFdE=Z7r!cv_Gcn#d2PhND+u^B(B`DkKph*R`PlU%r3>x?Ag3lxG)Wrg=*iPpIaXO zjUp=288fp0QYjy|LzWO_?bmYQ&xj5?f$uiA@;QpC#lS+gobY{eorHGK5MnX?T$GRS z+XJYvy$sGm`BWAAHkZ{dJbIxUwqR>NFN4H%N>zB9g;=MhWY?HeN~+J39vvM0(_6Gg z4GnUeELZz!)ltI3Qc@?d3#7{b{4`2toWRrPbC3JyEO+kKOOI#jTurMozNQF(78~WxlSZFT#sKV#sL9ds%Xf#nsr7V^$(JdgNtez${3^<-CCLYt2h}r# zgQNLve0&Ga{k6>0oIg@#Q>IHL`65C3q%82glc5{VyB-L;d?Ricg#OFPzD(-0B)FcL zf0_u*C>uKH3oLj6yB^WSR0E3%^Q29z-BhYP(0;*7tU7|1LQFSgJUz*#l62yY7z+zS z#fgXRjFFTb(%`-HYjbxc=uOO{n8KoLhmL5qU_(ww`m=e}^CwOva~8cjIdl2PO=I&X zuy+YKYx+n^<20TzaV?!Q7?Yt8aJDr8nm0zrSD;9=n=Mu?*Av$#%Q9wEZZ|qP`Au7m zMqMzxkA2)0L-AQ{@$VUM=`a@qm`Ar2KOCKVnx@4#C<*5=M@~=8xhGrO%iQsYN=(q! z@+gZ?wC}O(etF06gjDuoC@U$AgdfXbQWc4WS$&{POK_@`uP4{j_%23=Uo!qmt(;u} zC!n20PB~7?9$!<3~iyLmX9j^x1ZOrbfDxG}13ge%O$y4Sf@l?;kRE^%a?) zFXXnel9IBr@<^3ecsds=*ob|(Ry;pjWsU1h1p|hFju`Z8y&wBo_+;B=FsQlD2X>Np1olaKQj>2j>$6LXQcg)>@yxiP=~7=K!ZL$|8peN% zhDJ18mQGRjd^)WL`y+#HZr&#dWT(`6YrBE7FP`4|v5b)FXYKU_xdqaJJ8V?NAX?$V ziRa{5j!y#nvXjZ~ch(j#+vN8wWC1}(nLO7@*VQ>>))(0P7Zk@v;7dkQWvwred>|hi zY=~^kwvZ-R<};E?>^E8hl)#zz%`NFQNu?V)@JeMM^N1Sgi_&^{> zYe%#pY7Y>oc;joq;T+;o9kEb7w2V7&cy!NM^#*u}eo=x~f`m(8S(Ysb1gh+I=FeGL z;YV|i3q*EnDauRQD$2EWqA3xaKsS5cnNlVlM%u&7Kox>J_#w|`%g*P6cI?Pxsf!LS z^I=u@dz9b_Y74n>o3(wp^0^^o!Fty4E9Pw4>H^3@#HpZzAGOWN{Ry1=?s+k$RxHU9 z?sU8gXUyhsSKq7!vxF2z^!LrLX_q4|wk3_)zpR*m&p^x02H|!p@k8 z(+(2rW-k$iz=$FQ6|d_TUqMS|Ne+!?)vQPvAq?>`Iv6J%)P3?FKxIbV3tzrTaUJvA zD%q#B^Y<$z)g1&A8HpQh7_Zn6^m)~g-9lVo&_queB&5+*XyMtIDUEH|4Z7Ae&-;;t zdh{Rbw8+JUvVK)d68CdIED6IFH`i=8>JL+M#hkHZnVB)ok3q+(WV(Dm@L_ngcJMgq zAz7uOnKnGGuRVCZ!G4nHP9u*;&O59F|Db=7$Uj6MH22cf%uydqH<(1tO4<*%?R+%8 z4k7)~1ZqIqI@A-M?!kV$Vstjslh@3Q?4bZh{))mirNoeskl@+v=oP3;`A~OLbAlOF z4X#rcBxeAQW1w1*eVx3km0T89gv{uYUzubjQ(?ADImMOF!ywL0?wm|w%Irg$YWWGX z=f3x4sStZhiOKV*=}0>0d<@j3?wk=irhi zQe895h(#@rpVV;OLxSJrTSSCyF*P~9yFwSs669$$Yy$M6>wX;Ilw?dJ4uop!7l9pt zw{C~&up>Dh$mmAHb$`;$a8@e{VE)|sjI-zJ=p@+PAmk=I5_adG3`&QOokv?>XlQ77 zFGsZJwPd*Md`R;>6EH{y*`wF*N-XNuAly|RDj!6%JCVFlXkoIi0a;EEA$hib2)*?x zXz^RJ?C}qXvbe&DH+`9EP!~R9xq=~5%5K4qaRnWZ@_TCXMad$E8Da~c+HHi4$#pP^ zN$bzh0MPtSQtq@NL z5W0j)^w>r54~!0EHhM@fF6C3I9GUEm|6XNfeuJ;7%hmNRus-x6V=FItsG214UbT&6Lh=#RXT5=i+v!AU zxjaQnH6CruZN<&aq9G$`Xb20qUp95Ye&*YzBU`s3i|^=rtWn4;4?lpa_6NZb0ja(J zUA?TRHBV+?F4zF_l#JQ-@vZH;H|=RJwqcZ|C6wHnj9X5bwM`9ix>_-4b_R*-zC;$p zGsL(DD`DMhQUMYtuvn!HM5e478{~?Q%G=;M_zt06sO_pOOm59>K^ZAqp1Y`W0VYPWXr94 z@JB=NJG)^IWA=bMqS|<=k(eY=A(B*qr`6}`#AAd7vGqW{d089a^z;+()cMr$)XnI|r_jFnV6J}zGWp*0{*zZg06Iq6&QW2HnIFC0fT z#Z;ZB^rZ&?A(LLY|6!U>S(9QBeo-YH3uN8W`(*Rb+Ttokmp+8N_yD_pe{=J2dvl{v zHacCFz=@)6to_azadm4HzSU46adF1**1FzE^JMgntY{Z1S*)EWB{@fjJ-erL3Y(x_ zDJ6NWj{%rhBFs5v3cK%bGPoh(#W zsL_DA0OK9Po#;s$P6)OuCl-#RkO3H-=M#6wI3EyU&u=E$mOo?5g14yt2W%6G+N5xmCvlrtT0D8uhpd1fK*Cgisw7Qi}S zbHZ&D<{HPP;U3~Q^*`*`XXI^OxVsJY3#rnyHW0cXf6=RU{Sg~qTR~Cipja_aWZpbrnLQ4rtFd6})t*2XzvL0gIh2P!^ z;j&6S+xLt$?m4y;^sHaIS3H3sgSQWxlQFAD78sxL#Rfmam$Ji!g?3v)vO#&Yp%xKG z>QDh2qv_J2CIfw+#3LP~SQ#0Pg$;xD$Rgn}ShTVNh}bweq$nxd*N@Ip9DMS&#?*-m zM_l+!KE??x5>TRVnb)cd;c~`>#TBf$$D1WZG*2WmrJzNkh@tsx-^~0JQYwttCLyR( z+lr-Rg6y?N0(_SsRw|R(p9Tj`B4wlG(L-)9XM!!XCCG39M)Rtv0A)`$)jv<~g4!~k zRb)~af=^1CIS>hp66L#IX|VZB+(^_UtV_1reKfzXOyE*WIzLOxKuIe#b!XFFSRh1p z7{8kDVNxZ-3l1 zN5{-Omy6zl2T51u5l!IvSiPAK8E&QI=+w~Y>_o0^V1OE?nksSKo4prlTgT8lvZK;U zPxXvC6RKtdUvhMOYBypez1X>4m)z%$-dKahFqcg|Z(5HwLCJLE$GQusz3km?xjja` zPh`EoL`>(lw{p#<*Tb}@ryACHb0`n`?Dd`W-K|?<&*+|?4tL;%RI&~&3TCPyZ4_s< zQVAlp+w2j0L{&}$tJt-T>InuwOm6j0TMg+>Nvz$SwyhY^4z$1>vN!%h3kCUA3#{4w zal)tyedpE5uPy4QiI(%g>Q@tF#-{S8Js6859xClJ(^`x%3={{i^fD!SW% zH3uI{?FvY;7?MucVqpaAU6$Zwzm~-2XExWUZZ~b&-l~dttOi+XfN{(BR{1-%F zY}|9dmwD@>VbjONhWjWeT#SjBoQsNO);QC;$isuyDFEL^JzX;q4gjK(S1Cu@GFihu zCH@DQ`uoUC$-(iwc>~&o&8!%#3*=@m?tyo z#-Q>X8~`$8+Wk1Gi{MC(5rHTtzq}W6bVt%7FaLq2r>q6m5xcnCk|`P zL6zv*OpdmEOM_NqSzxzyO{1n@8H*{QA*)X`5QRq za*|PE>lPAeTdG@y+w;xy1Ydm&}E{jg{3k#16x5fFNhmtQ7 zvDazw{#OulPbytiiBQ~b2>1JrVXOk3TnGxYMibFJ;nNx+D(0o07e(7}lOn6t)}zsOj&8Xn_~Rb}+ejSL+B zOEM;ArDyWBLB-6_(G;JB4WCx@AOG<${3JdtznP=M7dl(O#>)1~_4^BC`UR1s6|k|i zu~)FwGx+KyXyj~WU?gI%=Y~%!X=H8k6@r0@jRpUI+rbD5C<+=G*ccl9A7mrIGT;&j zp3l%m-w4R?H_+DtSOXFO0{;KG0J?!-Ac44m1c8AGfB+jn-+;gX{Xnx{;lV*bf&cF2 zUkd~zIM_ERP-qZfAeiq!Ai$vCfI&e)K)+USC?{YL&~J!fNZ>yqkO_bw=~4I?2vMQr z_|f!;7}4eJd@=N6i3M_Mu1T2ep)q@Am`T5E*8Fbbas?F~goIf|49Jv}Rj_J%XLoN6 z{T<^2jPmOG=Jv?hjPIN%*f~VS#8uVQO-#+qon2hr+yjGxLqfy;BqSy!r=;c=6c!bi zl-4&iHZ`}j_74mW4Ude@FDx!CudMDL93CB?oZde?K0Uv@0#<<_fxk|N7iep56S}n2 zoWeY8bv1;X>;yltw6d~P_2i_)tYpv3q_{X!Sv1EFBX&Pg?T0}Wvx&2kJRkLy_2pW} z#Ld;diuOCt3;NtjbUgcK*u^=kXy@dGR%q(@PMdvK)e{(=wvjxnvwf|&%E0AI^+T2u z7YVcgWTq3naQB%800>Ip1n)3uQLQRf?JCQ)Dv?u0*9ZVK(}`Z$cs1(TWI%1LQ(g07 zb)(Sv#4@_36|cb~&%r!26<9c7gQG>vgY7h;5gL_5nqkQmjNYQt1(@F2+v<4x`vJ^s z=CyHHRCWqFp&Q<-SnC$7f9R}vu<};3K5hilr>)%{HWnTZO@vPvD6lr(vZY_rmmZfA z2Vc>vQBW*9nFr+{@I26cn&1u7zOcPBe~puH`_FYDGgRHGcpb6e@x3cB`w;R-DlU!P zs&ueAQ&zNQwqZy7XiZ+Pe^X%3;CyXD*Wgo=PzzaAV>>9wq_!3F@q)O;l8&p$ejeI7 zQEN!5tVzBIU((8GDH{1myj#O&L>2Ju-*3|1om^BN(aIP%C^oZy(5O^@${$lUB#9<| z=fy|ADv$X73*Qg&+jnb7EZ^MgP9S0JyxbxmGqa%G1aj_Il({bH*Y&)^qr0)^ zo*NlN{L%ZZ+pe>SqKT^*#5}j_2VRmkUPPAe=dRk?VrH+(qvbRWTjRFqZ>w^oxucU< z2E^pQx4czA{U<9pjIWo|d*#L{%+M*L@`kCYe>D^psW%&cm!zVm;?q=hZ+<+x+Wfcz z%>O;`)A;v~PG%oQuM56g*BsVC}gr?yB@l?zc`B7oF{DW~bVt+P5q(Jyzk_%A#Rrl>2(N z^#bMH@p%Q^>lMpD@~}@~01)DvOT#Ckv&$XO<;Gci>(N2e9okjo#vLeeOXG*M06Gn8 z)t+V{X#S&@GTHoGhxS0x+MUUh^*~-!E839NkEh`Lf(+B$#4sDc!Uh0n4_)_&Qzn<= z%=1!3%wfe-%ZW$1kZl&VqP*wuHqBeoUkE9mULy7rjj}Kba?h8fI94}`&5t0O=i{Gi zx45;p%y$c#=YF@pB=R^lRJl4Zm2#$ieD*Q7xVDBTCU07{zxLLIs1#C_g|1DQP_q(7 zcIy3x5EsQ7|0BLIzams!6vbm+V)yBt;Al5X(WRU_@)mu6@%4pp0o%cW$He?TK+7%a zjDqU|j9Okv?}odg{g^fCaM(J+e8^q%lGk*X1>vUA*F^ml8hSLH;KJcnz-zY^eQ)xx zYVD8e`u8ELSE<*Y~#b697F%FYZrS8*FHf5E;%am znl}Yc>y_Danvm0p3OUT6+Hg|?3kEv6pV^}(sCO&k&JY>0h73;h^wll=mb6!ZrT@Iw z|GDj#c*jQ7-V8cA+21{arou``=AC(Ne3z-@b?t5!`1Xm*!wK}R+d5N!sJF*2^&$0H3ZL{TxE3uX8t4iKvh-F!1SWwt<#_8b8C*c+^(N7 zmBnFkVL?H0=*4B_<)s;WeyNG6ko&2*xhA4Hp}1l{8qgoVo(s zs8+@twHZ&|5SQfk7{{cg!*Vjxl;bl>vI_DGii_RH#07qdg=ULvCx?b4I)trxxt-#d zmbfPXAn7#;vjxkEw)nxz&=LkV&p^%RZ{zv^pui4EC3bhP0aV72ZtdsR9IWGqJ}dkoEzku+{uf^6sT6lFivzSUBw1ps1W?4ab>2|C|o?fH^Ab9$-B7_%JNPEt%E8N zx;4?p(yr4`?8b_QNLxlONCKGKY{fk|6%W&lbo`N0Lz3GU0TPnzE^+^lfvU@Uk*HRoxZ;0?wan!7riYr@ylvNn(jbwtFgiQ540J z+iYV7mN{z(!-6pLFGI9Pk~f<%ALviixKGY^-e=NfB>)h{C-vi2Zj(Hj70SI6yB z>=Do6;H|3_07QE2#wmx zMfmglqciIXu4s$m>QUqKk@l;D#B=16jQ>?vbC#Ev&Bx>Q$Iy$=d#22lNc5XK04VIv z$2xiH9(lLm6YewA6W43!QKyf$<&(DMlM?;u?ZSI$?&yeb1R$yyqWTTN;l%d&@IdfR+C{zx+21 z`YXNsw}4yrZ!!r-EBhB%?f6B^{s;IBM=NjSVB=(OVB~=RFDt;mS){e&R}%;Pzlhf_ zxHqkok)fF$zl|%t#@7#Y_{{9g_-tRm=)XvD0ec(U|9*?Sjia8U(O2{T3J3o$S?|}8 z*xMK=7&(4LrIi&F{z`+5Tpj-j`VW}@pIUQPeEPpq^e<_-FdIJozx*|5h1u~L{&lRh z!t`Hw_P@~Czb(GZ>i_pwM*P39&wmkSY5!Ag{x_(I0zE#{UrqW~9}$JGKL6UoU%&eQ znn3?;D&WA3&+xCS@OSgy^*>0r|EAymZS^17H8TtAm*8CZzp+62+t0z#-bl|14q7zy zTt&JY`p|92wiFRCEW=aRsazh+c41bX$MQkZU@IJQKt|f zOAUkE&mXL|kBGuyNjMSr)W3 z&$zkM_rc$G?EE#o?(*IxKSgDq-d|3oo5v4xTcb_*OF}j9`51pR8h?4b zIOsN`cqOB5Blg7)^0Tc*0yY%G;BU%M)&!8Lb126Bn?W6%7o|GG??f(xo|qEe4%E zKLRrps9SSmHAR1ED`&&!bqUwj&K=X5H9Zo_yY=3@z=^T0g7%Aamykv!d$BU1u7HWP zJ&T1ja!VUHeQR>g`vGGU^+JBtNb`n(m|e zcKpCv3Rb&d$u#AW__LV^_2-a25jckbD1^jes(3@sTi059Bz+TGCk0rD>7v;~-3Qx~ z@QXN1d^o!k?N%i*icp{RIIj@qQljcb4Jw&oqcSg)*Cwx`n)$Tk3Wi@WMF8w647}{s zD#s@V&(_=3K(!XEHmpZ)7kZyF{qq>sAgW}ejU_)gZbZB%GgKpe1;N9I-&+M{0Z>2L zw7yoIN8bW8&O@E?({b^PS;1&u=Hjp4D13T+K_{o=;NQ7#!lO0y%(ia`tKtsx zky4I$PFDV>|}<&<0)T+hQ}xuXgXtzB-%cXF*|A zSUlRAD&7NuGjG;2CI{qyfa*hFw1U_4Y12+1H zR$!k2r*_kA^$UkV_q_kuTd#<|(;c6M2NYdELND+kQmT^45azG)Bb<>>oAM!jk(0&T zF@Nn7+Xh6CHQJ&>tQ#%B5jmpkh7RvqxlHqlbw$779iX4X$E+xqWo-O(WIZTW?9F}) zhHLa~IqI)jY9?)7>BBdP;s#CLOv*)RwQN&**?VmX<6zTh6)}#rH;fH@W6RRrY;{&}ySHx^f8$v7f6o^?!5zZFOW2VE zC@+q4=AX0P3PI+GfHUq&ItO@&G%%%#_LV#-E2g1)d&S`>XE0m0e%TjQvj>Xf5A-*>~6IOn?#ctDl0vlaYDcKdqoGJsG5TxMhaVZ_Qc0 zjCz}Sew$G;;ase!%-x*d9Sm4LN7vD_w|&xa5f;^KVkCIdhb_j@_*(Er&~75o9`656 z*N&bHnzJi!I1O(|`9@-=^^kVc6_oc9ksfKXy%@fN5rLBxO}l-3ci`yFO;}gEACJ}~i}Y@1#gHXk zhK3e?cwe6<^OI3KIL%hr?^l{vTc z$cbx-D%o40$y0ofw+jdiq3`}FA6v(1X2s8d3+BS|g}BrF-^cJr+bV49FycBiKm4%p zBB5l`J&vEzB0z+nK+af!Nl#2fI-EC2JLUs2mqn^qM&H!x#=*Vhoy3=gv2JRp>ZDU# zIID9}h|WouMb+&8G_9%CkU|V!a0fC19pQkCbmRxsKu~M8nu|!C187d1-zuz&?oiL6 zEYQ0_abS3vIoxJM;dmfay16Jj3G7KWfRUtZ1$oQO$%iljG)}lCauj`vVqQR36JG2G ziLghoG2uw5wq+$k{f{v}F5O~DoI!%Pl{AL3@SbxyrYkdbLO-1TXsi+*g^3>x_}d?` zoph4dLThyOS?k3KzxM>m#gJBj{@ih(3}sd1bn$7veOIdPv{@Ot(69*4hkGEZ_xyBs zr?+JFNAJtL{^yyPEfH+JLRD(5#+1X{85k_{zgqhWu&S1>f9aBvlI|2ZvoN=^aNIP zl*?or8-nF$+P{p0%u;CoFp41)ah97wm;wPVx{Le=r9zw_kICZ;KNpeZ%uhwNH1Vo1 z6r(V^a@+@ovd@|L;sTJhm|m&PlF_e>1E>9v5uha|tngE4QXMwrzD9Y|bi4u>gzob3%dkilUa}Xs-`(Dx1ijdV+ zx2mqWf8r3FigqB?8DAKTV=3IbX(+9{NdU?pHO+$|k<*IZ7E&bk&tQ~bwR7hymtq_)m;X{4G|sTusMdc{~AWZdkPnECJ*)Adc>)$@RA-8F<7+@5B~`|?H>2u z((mwW@MvW$V=7pSI>hQGhc@}gfNh++m(?iNgjsfbii95EISy(w9JWd(}Ep#+rGA%AFK6v&>{_;9T6{OqY6vkdaW!fT*3Ig ziP2BNJN3zNrHX*{?YVGSr{Oc>?gg!Izi*)S-zxUwwX;Vs-NtM-u64s}O5%EnVk=NHJYCHB*%Hh$SN6 zw8}NLwl1|yDw-xR@SnC7ZzzpwGlkK?oDQmGP54W&dw!2?An-(1>^Er`5Toba-jTBj ztwt3Qchu~Ug4=MibZTN>28T=fA5~HGJlGQ;nx)Y72}Z>y#+cZ}U6fqpT|Y(lD67^d zX{qm3_1M#fiWDr!npe=yfY#;X*Iw!YnQZS+V^_z_8>nGm3sw;uX1w+xM1iWYls z{UP##JDLI!Qd2Bp^-m*>_~2beB(k&2#&_Ddk%MsG*q)3Y>?3>)b=!zKUnyL=Th@9FZ zb)K#b7G53JKh{;pY2e;LL80#B$6C_FZB{{ruI()rpioL9jdI%1j?m=Qiej~x6kF=c z$Js*NZm{~}NiS>6I>H(zvzRM))z~9m$ZD$1shlDhsi+_Y=jamlIAN3>eix?wzL9Lb z9-iI1x?%ak+LiwKBSWSU(Mxp8zIBy4&#>fA0b*x1({NA?H#m(F!qE@iFb@38Vii%Z zbP1&8Z{DQ$@AALy>Kv$P#Usr?eq-TUp5Zh3^s9+hrF7i*XZ#k+iVHj1A#e#Qeoi@^ zde5-Q6O|xc&(mQ#*%9q^M(YFe;np*R#!Zqry{i7&kz+@F?h(&M$~JtcLf*;;X_b$R zTj@(*1oW4QHuaVEAf7Kr*beS0P#xLQiL!QB3pu^0AF9{D&WfHGT*XkLbfe5`U3Emi zuD|9Wff?)&S>dSTZO2nVP_AEKKYUd6Xl6LDTI};kP{k*^g#;#2#A(gm(L{z_nva|X zR(2cUN^Tzug9W`L-mk~{u0IdjSmCs?3Lax=Xr{+72FM`@TP?a#61*bDS7kGra;&yS z$yfUgc)E z4>Ha!1SH9S_Fu>i%i;y0P!R9mXc8J6K@Cg9=$faCYqeZd42NK}CHQiWAtob|RjpeV zu?16d!&yTqY^vdQ`$LM zWD0n>DtYu*Ozn)9qN&0dMn`kG(yUL$Y`ebu(Q0GLBm33U;CFv`H>&tMtPOj~`gfId*kNhPA@WBOg?4e-7^m35U{gDg88-lvP<^|J>Q6YXZwh-MDj@VL{E!7v@EqE&hyMLsXp(0>Or5>Ck-n z0WC{(UpS0X;Ho}==l9mIJ1a?%Xf}!)Hp$QtwZIQ?cDs++ zoswp`qdJCkc!8D%^_WLWeO|yNhE*HZ$A7`IMCF25^b;8o6Kx3^Nc0mY8e;*DP^q4y z9~)G&534%gN5(WaZ>Nv-V;(!Z2S|FA)(x47vcs*+BpmO!O3BGA;}`QxaJRIhDq-Bq zQ9r3Jfv4w6$Q!4NKqUqBYFGYU$Ol&s>gptO0_!IyWG zo`&eFubO7Vr=a zjwG?GSdVA~_gd;NmnKVsV>`S@cj7Kpx~uVNVm-t{)dwG{Mq(CJzDN5!&p1fM@%2ZU zPf1SFjyF0fw12tOe3Z1g>{4e7u(xGvbS2V#X_tiEp+;Kh?b=FRrhK?ZBhgMwvOEcN z_zZuQyrYMUF$~dD=^;!k=;X_#+Y3;_Ql;{j{_BJsF&{42YdU?7o*Ss1v}>J3nP+FK zD4vc(EZ7_lUSCbthmNv#S$2k7O|rE6LbDruuQwb<`M|FcUO=B-=cQtLmV|o~Nt8Zx z`NqrcU3s>O-cPCWX`b`KEpq0lBY=gM>}RrQ?fH4(y6=0eDr$5g?FYfPo^WvH=jB+3 z`{>YWDLDiQ&etgqqWqR&eW$-11l^n?U0+RCX6uG7-D=20jkO-Ij9%WJ6ZgTD`zngo zWgT@pX(Xc{CvOLFH^V9(Jr^p1i=cn>PEzom53TiN3hu*O8Re#j zTh3Wg--Lt_TcL-^=->l+2E8dx1$DRtdeqUY&V@bx&=;N4w{`{;v zAA{_iJe7Jv^b6sRTBaebvKW~Ue(4I&B+}1ii?An#4HUF?fC?5hP7l* zSH->!`Rcy|D>k=p4L)k$CFdlT*!Lc3)wlBAtlmHwkm?Nea|uR|9eH#+ErXH>$8ngi zg}*V?i9M=>rPtQ*G8Z=>^z6B~qVRS81O1|nw-I*BAF6Z)YZW7L(LQRlB#kzZZy9gRgP(gMuN&b?&IxX;HXu=ZR#>ay-yfv?r4rMg%d?v$0gsZ%(XO;HXhyHKh{OIF`Wi#w;V&t-a z1J72){IySTMlcc+phxq zo~K4(3WW7w=rbdXMtX+LIW>Ehn;o8?PlV)3V>z2VKowF;&r@Mi#lBcpUeU9ql|D+0 z3PG4Y6p6?!BLYk8!C@Ewsiro$?(uLu*NcSBla)OEpsS!(v^$rf zti~BnUE|7y4b`0PCEIPwBlV4dC%HL{47o|IF;`i736J9X6}+9gY8_QXvJE3;h+r}j zSyB^)ZmLAcX9N<%;2$?tGK2F=retLv%gK=KKaDmP;aKrEKbtH)A?DE7%Zn`wDtz~_ zPjNjrsO=*fwJXN{n;^8=_gDt_cO*RG{b-@mO_p@vso`TA&hgrujB; zX#c-X>qF+Z|5(*~KdpZ^Sq>bX;%4l6w~Pd`m;{1mDPiU8;woxs5x2k0I2tw|5JRQ z0V3o+umc5g0}!pKU@#XM7;s5IVlJRm2yQDRK+=%#XJ&v0Lch}xpmYDBH7X9thtb8bEjTtRrpJTzi0o8{;#Ru1VA$V-#}|+#U<`RYyTr)Ed&z)aB2PK z$@vWw?*CVp0@i0iw1;#VPF9|~z|94`2#_wt$wS7@%6S(!Sb&!vct;^LNc!#`@bWay zM#c#MBA`MNV0NG%vUA;iGeFdbT>sgnAky!;6r?_Y-jI4hRDei>)a6%~0+=|sfsP5p z5J?cGyE*|yK$su_LWjhVMuHT5#|!YILK2V)L0SjWUG8oI(z$qkbuCCq5aAq<5&`uf z@m-4oPXO`(UZ?-A>-_7R_?Lh`p5R|B+yK__uMgWFF@pN zH`I;a`_pfEP(i{jCJ-Mq7bozHL83;E_v;$D|0Db}@W6kSeGmQ2&Hg*`Gw_Tcm4hg! zU}OiZev}rI{I_2R{|WUO^1_3({?LPi>rOA=w-e%?rUN;eIa@iHf}BAvkfoKNKd_%I zK{6m0kkxMp(5@iszav2313y2%gMJ1;pIxl%Eo>ps&lb)`?q+uY&?XLcc19pa(C^^S zM!#i#iC_$Q)el=|KAsvRk>V4>oi+hZ1|Gqm*QD3Bv; zo@KXv%eBPQbbV5q2OAa*D~ zkK(00UK-hxughv_9oo5KznvfO?Qj8h7T*#PxwyS^BqGHkC4MSsuKy{|Hpy1c`XKL% z^Rp4J;?+^cR>a!afnCy;X{*;hncry?@mi-{hImac=)V?{2`u>CeiY&|T+REC84IsS zo=F53D_9jny0sd3=rmt$zm&^PPM(_}BGR3QJ>v=BL%x;y(aK3GE7BvF1s7=5>Vykht;twFI2-o+}&yq!wQkYO<5}eI3tK%oi>fR$4i1k0)s4&QSBAZaaU^YH) zfU!t@eYz_c*5zn#MCoWt6%yTDMfNa~$e+5!I8v`#&_s(S#L8)hEkv)U3W+E?6{Ww& z-1vL7_0D7SfujTR6si=$Ho8TR3cPd7D_$t(FkItq`EFTzlm!5pVmu6dR=j79u8CeP zSvJ6no+R0kGWAJrSU6hoOxzfUE)+K&>y;rZvN#krvOuVSXCSPr50fB{i55+}C)0|Hdt^zf&WFCmT847#6lr4>CwM}(j znFbB0X<|Zg7@u(lrn^wDZP)j-DrDv$)t8kikI`E<*p?SP;zE{AUUg3=9aKngE%XR?;>$Ua<+x1ZzrJUCM*9>2y!ZPJ@pOfci4Uws& zNKGM!RfJsjd&!n()LrPVQ)9jYb-@MnsmfEOzv8BU@wMhV3vRc=i=PLVNzcFSQgw}} zo;9pYzT~b?vIrJ=)CY|TV~^*3TIVBL%h_Kk&)N63+J+5l?L9@WY#S13cTq(l%a8Hu zq#E8s4dG@po7D!dMw+#N5GhQ8=rA6-?l`Sb1v^D&ntmhFDonkgNfK>24SbJu_r3Yz z%G2W~?E|{E{sBBRMT@6?i(S~8D9CkhUr6RY%shQTC-Es?YnFukhj^PKhmM?UZgU`} zU%7_YMtlO!~z0O(l>3(~>T1nn$Nii-5wt~i9TWXzW(omp1`uLo#g9pdn1V6~L zU#M@8GBL}`8)-zSs7CErAC~`!AvFkVvz|C@LVL9|VUx}w00YhoZ9*Lg?H)8&MVS-9 z;+I&!)u4^9c$S^kA#XpXp_zS_D`wP~0X3;Nzn9J!n35PVSt4!~I7m zx8K-ks#rxo3+3V!;5$_qF{ojSNO!(fWFauuM_5eA$j%nmr%8X_!jLs=`1x5Qj;*lU z(=TnMiy~9N9$?_CroES*Do*Zh`O}u)uma;@3W%u@PVB`xJ&I@W>u2yeSvSi;L&oD` za6A&LysR4^Q*&7vn#Df&^dwWfPJL|YSG#5!#ArXV6IJVTR&HT`LL`k;*qmT-i!T{6 zqIDgrxY2lfa3JzMl1p-GB(J@Nn3H>$9?jPfrak)X0otpP49pGFnwFOI6}7_F4-Y&; zz07uBdKI|5A2=3S+4(9(6JNom?rf^b8^e+X|2@veY+-{wr;s=-OuGS0pJl#>*ue$; z%qeN|W{TQX&7c@+NjUrFT`~CU78%v6$kfMr|8Cyn%(xR(Gf@c{#5uTxXNNu zP*+2oJmLw~XM1noF0`u4BL~YAET?A{*wL*uZhjoB>h_xvr)JcV%P>ocE}T2n8mK2R zi_L1)ao*TtVHUCA)}6k-EsP{M!N`AyVAu9;g~wY&xb5J56?T^i?R5sHKs$J__FW42 z>&yKEK0a(`oac4>e%jB(eVHs8$s8C|1Xa1tG{;)2msDPv&MsD^EULG59tIUP z^6;SbZ9~hB_o*ukG7Ri~0^o!Zy4t^9u_B)zO7Ulq?i@R-Pf$beL^79GqF;|>-qkH8p^@#_5=P0v% zTSaiHw23D3ff!MfQ)Ck|(ks8P(!ppQF(R3qp96k212*;;tsXCebmMF;CFf1u6n#-E z=81*iIn`XJ3w5||*_3bWvr!G=EWbsB3uC45%r+hzxO92hSBe;MT;iLd8tltTtOQJc zP6LnGET6oyP5th0gsw%BUE=i0vvYVtd-3M=BjZH|DEW=eht0;Hv^a(dvc18GY*;kL z2*=aZrjaW-%^@^$Yr*0>5@QC*7GlzU4?ogsyW=yi(Qb=s!6L!=^ zPrn5eW}K4YDzG~kGDAUUfC4|lh=O$rMZ7-jVAc3Y-@F<@-4FV)V2Ec6+wp;*2suM~ z?AWy%JF(~dV8D%fI@mg{ zj9RRoIAmj{-iKV3%PGfCo}^-`QG#B;=Ye4Be}F*FXo9K`ctir9wL-<)_M&|7Rw^Gw;4sR!h-8IT6ck9)z^U@jYk7T2)LrUw>uO@Dnn8 z^vm~SEY*CW26e@syY&h(_^ zxaMWuQ8=mL&vrEcXqz$|O(=u3R9&jqpTnJ|R>F3sblte(GplTz3Lw~0-yOaMqid(Mi8vdEijHAam1Eh z<<5N4wFEoR?5mTk+nv?uSEihPEos~R_%waD>*w-^9}JVWg_b30#u-juhF|yyTS3v_ zj4x9KqRj+(8Va4+1-uH8;z6g4q@w%O4LuaTYN*FIfrL^+AD#-Wiq-O-W`wbkL+RuU zfw5g<&38dye0RW<#SE02LU`~HD-gA>!be`g^< z>>vmy#C~F80|QnzNW#|00$9BZW@F|0r3=N6hR6gHIV3?-E(jCsDQ*k;viL!BuE`3 z{>$8v1_{&uGTVT4vu5mMzn10xZsGiPc=W$rTYJCymW_quf3OK2b?^gva94I3e zX2}>3O(8>E57r)dn;stVWp+CKMJtc{j#*Te)la}x&2F*I*Rhnjw13rsJ`fPdLNp*r z&3I1cG?phVTQg*Zf4K=45>@xy#A|zb?4d90`qc6btME-VvLNx!R6J!I+LtS>^HnNp zz1U9dAWe3BWF}0X@et))})KgtcY1v&rS=3v(GXn~TxmX%!hM*7KI1 zd`vmhQ1NN>MAm62Gt5j+%tg0Nv+VV~TI?^DeG=@O-LU8h zVhZI$GH&Mc3N~?1YEx+`k3mvVkIq4j_a@dpXr{*(g&SW_g%Z2>iJm3J8F$%MsebI*htV&v*2tifVk_cH<~hG05G7HmUYmWK zVrmS-iYLnUaLi7S8{l4p4pT35!PmW<@#kRoqN$2kxysShLL={VG?4hT2f>69;uC%#@D8)siryuT{$1U$^A%KOb5EHPfnXi=9zrFy)= ztuZ7CG1g=!6cHlUY9U|av8Idd$MV(`coehq)I>-*u5fLA&PBtNx=w|vE{)Q~inIG@@ zOLKf5T+;$8no3IyB`o5&AWo?nox#hjm5x{ueB-@(neA(5V7lmYP#k@w%N)rpqHPvj zI=)t3Qk#+>cPQEsC8+4Qbi`%&5NGD%T5K5;Auf!DFOP_tWKonY`s&%zGm_LJ>^6G6 zCG?fLVJjEy@?hqSoy1h1&lXBysPz+jhu^;Bv5-l8?8FDVEoW_rVHynK;oyXe|3IOQ z92-kS7C~vb^4;%lHG_Hcks9iwkr1j0sK+)HC+yv*ZI-kr%S6qqNv)o74@bYz)YFMpl0{v9MN?6zY&$}QGdNR{%kh!rKQ%A#__ zJN<0Xt)_Cg13&9bSTxgAcd&;=Y7 z9h>~xG}Cx{3*tyP@t5suQZM;)MQl(;&V9Mruda?Z87u><2K@(hG>!6RJ3>J7)e%(< zrw*-!{=MH>zCCj}g!3r`*HY*6?Xh6;uy7BSs)tx z2!S~yNUTDJQ_OI)JW0T|tKF@upOGM^3+mK523InvX3X^0cXMpNKA3h( zAmQjWEart4)a+tUEe3nkTDW)_8-fBsacFj}RpOVJ#8I)hV$fT(} zBw#~JAgtCbqla2gVgdI)Gswt5NDMEik$(qn_q_vRWWHbRE<@tCio^&CGo6MVR;&`d zGC}0a1D#QvM(Fq3G!un84|$M%KHL_bqI|!O{PNmI-AtflOl4Tx0~;56+>*#kat0^w zLos@93V8#Xx#n3)P)>Thl8qf%1McoYciVQQ3{nvtl-&8o<~aFA&jV7gjsz;Gv-lN? z;vY{XVHvl5kysv}9m*d6>nIG^IrQ5wOcdCi<7(w#F9zAOL?^}z?2u#y#!Nh{tn6SG z7GR75Y*sRLGcj`}qZ2l9b}%+_B@?r9aka9w1xOssEM(>m&SdgNCRX;Y4lb4ezp$ID zB|xH+mr(_-)T~@>0l6|}ZuVv_0MrFAt?c6H;Oq+Ax+@vNsqALtY@?;*3^3@@x!UQ{ z19Dke0S2|V4(4Qe!oo_+!a_^``*;a(asF1VUndJdc7C}4Lp50|Q;5r# z_17+`UmL*gHgP$)0i#L4OZ=N#?76dqf)01lsj(6}I5&Hh0HJTQQp;ct04S=m_r znU|B56L5b0gO45jFY-9qxdHFzpM0*)M!;ay8I_L@824IvnE{P}3OE#W_{>dAO-(sj zjm=F=jE#)UxxipfW5Ce^RL+czhZAfh@V|5D0c{NI1v7GXy=!1rE_PIEYH>vg)c*(g C<@G%P diff --git a/tfjm/static/Instructions.tex b/tfjm/static/Instructions.tex deleted file mode 100644 index da293ef..0000000 --- a/tfjm/static/Instructions.tex +++ /dev/null @@ -1,88 +0,0 @@ -\documentclass[a4paper,french,11pt]{article} - -\usepackage[T1]{fontenc} -\usepackage[utf8]{inputenc} -\usepackage{lmodern} -\usepackage[frenchb]{babel} - -\usepackage{fancyhdr} -\usepackage{graphicx} -\usepackage{amsmath} -\usepackage{amssymb} -%\usepackage{anyfontsize} -\usepackage{fancybox} -\usepackage{eso-pic,graphicx} -\usepackage{xcolor} -\usepackage{hyperref} - - -% Specials -\newcommand{\writingsep}{\vrule height 4ex width 0pt} - -% Page formating -\hoffset -1in -\voffset -1in -\textwidth 180 mm -\textheight 250 mm -\oddsidemargin 15mm -\evensidemargin 15mm -\pagestyle{fancy} - -% Headers and footers -\fancyfoot{} -\lhead{} -\rhead{} -\renewcommand{\headrulewidth}{0pt} -\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018} -\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.} - -\begin{document} - -\includegraphics[height=2cm]{assets/logo_animath.png}\hfill{\fontsize{50pt}{50pt}{$\mathbb{TFJM}^2$}} - - - -\begin{center} -\Large \bf Instructions ({TOURNAMENT_NAME}) -\end{center} - -\section{Documents} -\subsection{Autorisation parentale} -Elle est nécessaire si l'élève est mineur au moment du tournoi (y compris si son anniversaire est pendant le tournoi). - -\subsection{Autorisation de prise de vue} -Si l'élève est mineur \textbf{au moment de la signature}, il convient de remplir l'autorisation pour les mineurs. En revanche, s'il est majeur \textbf{au moment de la signature}, il convient de remplir la fiche pour majeur. - -\subsection{Fiche sanitaire} -Elle est nécessaire si l'élève est mineur au moment du tournoi (y compris si son anniversaire est pendant le tournoi). - - -\section{Paiement} - -\subsection{Montant} -Les frais d'inscription sont fixés à {PRICE} euros. Vous devez vous en acquitter \textbf{avant le {END_PAYMENT_DATE} {YEAR}}. Si l'élève est boursier, il en est dispensé, vous devez alors fournir une copie de sa notification de bourse directement sur la plateforme \textbf{avant le {END_PAYMENT_DATE} {YEAR}}. - -\subsection{Procédure} - -Si le paiement de plusieurs élèves est fait en une seule opération, merci de contacter \href{mailto: contact@tfjm.org}{contact@tfjm.org} \textbf{avant le paiement} pour garantir l'identification de ce dernier - -\subsubsection*{Carte bancaire (uniquement les cartes françaises)} -Le paiement s'effectue en ligne via la plateforme à l'adresse : \url{https://www.helloasso.com/associations/animath/evenements/tfjm-2020} - -Vous devez impérativement indiquer dans le champ "Référence" la mention "TFJMpu" suivie des noms et prénoms \textbf{de l'élève}. - -\subsubsection*{Virement} -\textbf{Si vous ne pouvez pas utiliser le paiement par carte}, vous pouvez faire un virement sur le compte ci-dessous en indiquant bien dans le champ "motif" (ou autre champ propre à votre banque dont le contenu est communiqué au destinataire) la mention "TFJMpu" suivie des noms et prénoms \textbf{de l'élève}. - -IBAN FR76 1027 8065 0000 0206 4290 127 - -BIC CMCIFR2A - -\subsubsection*{Autre} - -Si aucune de ces procédures n'est possible pour vous, envoyez un mail à \href{mailto: contact@tfjm.org}{contact@tfjm.org} pour que nous trouvions une solution à vos difficultés. - - - - -\end{document} diff --git a/tfjm/static/favicon.ico b/tfjm/static/favicon.ico deleted file mode 100644 index 97757d37b247a0f247f1288a6f1a43c15614b612..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1514 zcmcgsO-o{76uz^V)$C^-%w|^elllR+Y!L+NV1Ypz^nr>Lq*i7YrIls*QEDbGl+vhF zGzcTgJ~l}o)-yh5xFX@i%w~?f-uIsOyyrRRIp7_s3MiFIczSxm%gYPO<+A8bPEK%faY0j&bUKax z{(kiJ^lR4NtG6XWXYN_EqEW-=KVjYje1a=GyE z@PNn1M{qwnIx1(hd3$@4I|~a7Fq_Tr`~8@moyG3%E{H`Rdc7X^_xGZs9rJKF9OB1d zFo1sE3-euBS;6-9Hja;vTgQiohX@1$SY2Jk>+7p59^TyC;Qag?$z&3dNJMO?XB>Pd z=G@#I#>U3Pmb|R5uL}mx-pilxbGe-O=3Vke-zo?6LHuYm+QLsH5{SiOQp2UCCFpcI zxx+fHt*yc5^GR&~g1@n`A$1uX9F%AK-A?#BJ39ylgIHW#l(?p+r@=g!LnnOZ^HKi9 zpPHJ2#bOaYKf>SJ+k@BZ1#|a!Jkkf;FE1~HepGzcV}E~N_>0Hm(yMCy+3U4hP5LCC z&$sx$zP<)?V=mlN=%bx~^1&V?-qzNZ*l?x>1_oOEa~{}FJv}`b9v((_cQ;b06r4_{ z+zW@pn3$M=-ENoo$Rm54F=ew^^!E13ERi?%ChIysKQA0|9yA(_^xoOonc%bEv|24} zHXB%rySqD?N%l!76q0yHMn;5RVsf5`hK8hX$RTxBs}*jyTli;B(?_*h#pL9qEc!k; zIFKAnCX?jOJXK-b^g-XW<1BH$Syxqz>*lK0KcP5FkY-{HS`{sP(p$%_C0 diff --git a/tfjm/static/logo.svg b/tfjm/static/logo.svg deleted file mode 100644 index 699316b..0000000 --- a/tfjm/static/logo.svg +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - diff --git a/tfjm/static/logo_animath.png b/tfjm/static/logo_animath.png deleted file mode 100644 index da4533ee3695392417e3db8afc4b9852fddc1cd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106600 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