From cc5f04e2b30598c8af04d2cdd36b5689e367a2a9 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 2 Sep 2020 15:26:57 +0200 Subject: [PATCH 01/41] Add script to launch a Docker bash easily --- apps/scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/scripts b/apps/scripts index c1c0a879..1145f75a 160000 --- a/apps/scripts +++ b/apps/scripts @@ -1 +1 @@ -Subproject commit c1c0a8797179d110ad919912378f05b030f44f61 +Subproject commit 1145f75a968999f24f9feb3fc82946aba14fb45d From d74007d5233a337b49f60661942198d0ecef62bf Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 2 Sep 2020 15:30:03 +0200 Subject: [PATCH 02/41] TOC and update dev instructions --- README.md | 115 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index fb3cad6e..fd9fab66 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,82 @@ [![pipeline status](https://gitlab.crans.org/bde/nk20/badges/master/pipeline.svg)](https://gitlab.crans.org/bde/nk20/commits/master) [![coverage report](https://gitlab.crans.org/bde/nk20/badges/master/coverage.svg)](https://gitlab.crans.org/bde/nk20/commits/master) -## Installation sur un serveur +## Table des matières -On supposera pour la suite que vous utilisez une installation de Debian Buster ou Ubuntu 20.04 fraîche ou bien configuré. + - [Installation d'une instance de développement](#installation-d-une-instance-de-developpement) + - [Installation d'une instance de production](#installation-d-une-instance-de-production) + +## Installation d'une instance de développement + +L'instance de développement installe la majorité des dépendances dans un environnement Python isolé. +Bien que cela permette de créer une instance sur toutes les distributions, +**cela veut dire que vos dépendances ne seront pas mises à jour automatiquement.** + +1. **Installation des dépendances de la distribution.** + Il y a quelques dépendances qui ne sont pas trouvable dans PyPI. + On donne ci-dessous l'exemple pour une distribution basée sur Debian, mais vous pouvez facilement adapter pour ArchLinux ou autre. + + ```bash + $ sudo apt update + $ sudo apt install --no-install-recommends -y \ + ipython3 python3-setuptools python3-venv python3-dev \ + texlive-latex-base texlive-lang-french lmodern texlive-fonts-recommended \ + gettext libjs-bootstrap4 fonts-font-awesome git + ``` + +2. **Clonage du dépot** là où vous voulez : + + ```bash + $ git clone git@gitlab.crans.org:bde/nk20.git && cd nk20 + ``` + +3. **Création d'un environment de travail Python décorrélé du système.** + On n'utilise pas `--system-site-packages` ici pour ne pas avoir des clashs de versions de modules avec le système. + + ```bash + $ python3 -m venv env + $ source env/bin/activate # entrer dans l'environnement + (env)$ pip3 install -r requirements.txt + (env)$ deactivate # sortir de l'environnement + ``` + +4. **Variable d'environnement.** + Copier le fichier `.env_example` vers `.env` à la racine du projet et mettre à jour + ce qu'il faut. + +5. **Migrations et chargement des données initiales.** + Pour initialiser la base de données avec de quoi travailler. + + ```bash + (env)$ ./manage.py collectstatic --noinput + (env)$ ./manage.py compilemessages + (env)$ ./manage.py makemigrations + (env)$ ./manage.py migrate + (env)$ ./manage.py loaddata initial + (env)$ ./manage.py createsuperuser # Création d'un utilisateur initial + ``` + +6. Enjoy : + + ```bash + (env)$ ./manage.py runserver 0.0.0.0:8000 + ``` + +En mettant `0.0.0.0:8000` après `runserver`, vous rendez votre instance Django +accessible depuis l'ensemble de votre réseau, pratique pour tester le rendu +de la note sur un téléphone ! + +## Installation d'une instance de production + +**En production on souhaite absolument utiliser les modules Python packagées dans le gestionnaire de paquet.** +Cela permet de mettre à jour facilement les dépendances critiques telles que Django. + +L'installation d'une instance de production néccessite **une installation de Debian Buster ou d'Ubuntu 20.04**. Pour aller vite vous pouvez lancer le Playbook Ansible fournit dans ce dépôt en l'adaptant. -Sinon vous pouvez suivre les étapes ici. +Sinon vous pouvez suivre les étapes décrites ci-dessous. -### Installation avec Debian/Ubuntu - -0. **Activer Debian Backports.** En effet Django 2.2 LTS n'est que disponible dans les backports. +0. Sous Debian Buster, **activer Debian Backports.** En effet Django 2.2 LTS n'est que disponible dans les backports. ```bash $ echo "deb http://deb.debian.org/debian buster-backports main" | sudo tee /etc/apt/sources.list.d/deb_debian_org_debian.list @@ -192,43 +258,6 @@ nk20: - "traefik.http.services.nk20.loadbalancer.server.port=8080" ``` -### Lancer un serveur de développement - -Avec `./manage.py runserver` il est très rapide de mettre en place -un serveur de développement par exemple sur son ordinateur. - -1. Cloner le dépôt là où vous voulez : - - $ git clone git@gitlab.crans.org:bde/nk20.git && cd nk20 - -2. Créer un environnement Python isolé - pour ne pas interférer avec les versions de paquets systèmes : - - $ python3 -m venv venv - $ source venv/bin/activate - (env)$ pip install -r requirements.txt - -3. Copier le fichier `.env_example` vers `.env` à la racine du projet et mettre à jour - ce qu'il faut - -4. Migrations et chargement des données initiales : - - (env)$ ./manage.py makemigrations - (env)$ ./manage.py migrate - (env)$ ./manage.py loaddata initial - -5. Créer un super-utilisateur : - - (env)$ ./manage.py createsuperuser - -6. Enjoy : - - (env)$ ./manage.py runserver 0.0.0.0:8000 - -En mettant `0.0.0.0:8000` après `runserver`, vous rendez votre instance Django -accessible depuis l'ensemble de votre réseau, pratique pour tester le rendu -de la note sur un téléphone ! - ## Documentation Le cahier des charges initial est disponible [sur le Wiki Crans](https://wiki.crans.org/NoteKfet/NoteKfet2018/CdC). From e85ec1fa05148623eea48964ad10248f6236a3f4 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 2 Sep 2020 15:31:57 +0200 Subject: [PATCH 03/41] Fix TOC link in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fd9fab66..43c5a8a5 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ ## Table des matières - - [Installation d'une instance de développement](#installation-d-une-instance-de-developpement) - - [Installation d'une instance de production](#installation-d-une-instance-de-production) + - [Installation d'une instance de développement](#installation-dune-instance-de-développement) + - [Installation d'une instance de production](#installation-dune-instance-de-production) ## Installation d'une instance de développement From d73d9f8bda2cd6a2925ecee8384c598b9e5d65ed Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 2 Sep 2020 15:45:07 +0200 Subject: [PATCH 04/41] Add Debian requirements to Pip --- requirements.txt | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index 782beb77..bb63a69c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,17 @@ -django-htcpcp-tea==0.3.1 -django-mailer==2.0.1 -django-phonenumber-field==5.0.0 -django-tables2==2.3.1 -django-rest-polymorphic==0.1.9 -django-bootstrap-datepicker-plus==3.0.5 -django-colorfield==0.3.2 +beautifulsoup4~=4.7.1 +Django~=2.2.15 +django-bootstrap-datepicker-plus~=3.0.5 +django-cas-server~=0.9.0 +django-colorfield~=0.3.2 +django-crispy-forms~=1.7.2 +django-extensions~=2.1.4 +django-filter~=2.1.0 +django-htcpcp-tea~=0.3.1 +django-mailer~=2.0.1 +django-phonenumber-field~=5.0.0 +django-polymorphic~=2.0.3 +djangorestframework~=3.9.0 +django-rest-polymorphic~=0.1.9 +django-tables2~=2.3.1 +phonenumbers~=8.9.10 +Pillow~=5.4.1 From f1fe6c499675780444401cfe5ca99d12e9042d4e Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 2 Sep 2020 15:52:44 +0200 Subject: [PATCH 05/41] Fix some pip requirements versions --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index bb63a69c..870ea3b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ beautifulsoup4~=4.7.1 Django~=2.2.15 django-bootstrap-datepicker-plus~=3.0.5 -django-cas-server~=0.9.0 +django-cas-server>=0.9.0 django-colorfield~=0.3.2 django-crispy-forms~=1.7.2 django-extensions~=2.1.4 @@ -14,4 +14,4 @@ djangorestframework~=3.9.0 django-rest-polymorphic~=0.1.9 django-tables2~=2.3.1 phonenumbers~=8.9.10 -Pillow~=5.4.1 +Pillow>=5.4.1 From 3e42f4fffb99918cfa57e3efca82572e9e2e98ff Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 2 Sep 2020 17:22:06 +0200 Subject: [PATCH 06/41] superusers at creation gets automatically valid registration --- apps/member/signals.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/member/signals.py b/apps/member/signals.py index fbb66c1f..70162b37 100644 --- a/apps/member/signals.py +++ b/apps/member/signals.py @@ -13,4 +13,7 @@ def save_user_profile(instance, created, raw, **_kwargs): if created and instance.is_active: from .models import Profile Profile.objects.get_or_create(user=instance) + if instance.is_superuser: + instance.profile.email_confirmed = True + instance.profile.registration_valid = True instance.profile.save() From 8db9e92986696f9f722d229fc8c9f0b5cd778f9b Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 2 Sep 2020 18:01:41 +0200 Subject: [PATCH 07/41] Sqlite does not support order by in subqueries --- apps/api/urls.py | 8 +++++++- apps/note/api/views.py | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/api/urls.py b/apps/api/urls.py index 9b4d44de..40d21874 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -51,7 +51,10 @@ class UserViewSet(ReadProtectedModelViewSet): filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ] def get_queryset(self): - queryset = super().get_queryset().order_by("username") + queryset = super().get_queryset() + # Sqlite doesn't support ORDER BY in subqueries + queryset = queryset.order_by("username") \ + if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' else queryset if "search" in self.request.GET: pattern = self.request.GET["search"] @@ -87,6 +90,9 @@ class UserViewSet(ReadProtectedModelViewSet): ), all=True) + queryset = queryset if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' \ + else queryset.order_by("username") + return queryset diff --git a/apps/note/api/views.py b/apps/note/api/views.py index 9b213025..1ab954c9 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -1,6 +1,6 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later - +from django.conf import settings from django.db.models import Q from django.core.exceptions import ValidationError from django_filters.rest_framework import DjangoFilterBackend @@ -117,6 +117,9 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet): """ queryset = super().get_queryset() + # Sqlite doesn't support ORDER BY in subqueries + queryset = queryset.order_by("username") \ + if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' else queryset alias = self.request.query_params.get("alias", ".*") queryset = queryset.prefetch_related('note') @@ -137,7 +140,7 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet): ), all=True) - return queryset.order_by('name').distinct() + return queryset.distinct() class TemplateCategoryViewSet(ReadProtectedModelViewSet): From 4da5c41f40cfcaba82507787fd9c7ff0414ccaff Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 2 Sep 2020 19:00:04 +0200 Subject: [PATCH 08/41] move viewsets and serializers out of urls.py --- apps/api/serializers.py | 32 ++++++++++++ apps/api/urls.py | 105 +--------------------------------------- apps/api/viewsets.py | 77 +++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 106 deletions(-) create mode 100644 apps/api/serializers.py diff --git a/apps/api/serializers.py b/apps/api/serializers.py new file mode 100644 index 00000000..1f658217 --- /dev/null +++ b/apps/api/serializers.py @@ -0,0 +1,32 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + + +from django.contrib.contenttypes.models import ContentType +from django.contrib.auth.models import User +from rest_framework.serializers import ModelSerializer + +class UserSerializer(ModelSerializer): + """ + REST API Serializer for Users. + The djangorestframework plugin will analyse the model `User` and parse all fields in the API. + """ + + class Meta: + model = User + exclude = ( + 'password', + 'groups', + 'user_permissions', + ) + + +class ContentTypeSerializer(ModelSerializer): + """ + REST API Serializer for Users. + The djangorestframework plugin will analyse the model `User` and parse all fields in the API. + """ + + class Meta: + model = ContentType + fields = '__all__' diff --git a/apps/api/urls.py b/apps/api/urls.py index 40d21874..4addbdf1 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -3,109 +3,8 @@ from django.conf import settings from django.conf.urls import url, include -from django.contrib.auth.models import User -from django.contrib.contenttypes.models import ContentType -from django.db.models import Q -from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import routers, serializers -from rest_framework.viewsets import ReadOnlyModelViewSet -from api.viewsets import ReadProtectedModelViewSet -from note.models import Alias - - -class UserSerializer(serializers.ModelSerializer): - """ - REST API Serializer for Users. - The djangorestframework plugin will analyse the model `User` and parse all fields in the API. - """ - - class Meta: - model = User - exclude = ( - 'password', - 'groups', - 'user_permissions', - ) - - -class ContentTypeSerializer(serializers.ModelSerializer): - """ - REST API Serializer for Users. - The djangorestframework plugin will analyse the model `User` and parse all fields in the API. - """ - - class Meta: - model = ContentType - fields = '__all__' - - -class UserViewSet(ReadProtectedModelViewSet): - """ - REST API View set. - The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer, - then render it on /api/users/ - """ - queryset = User.objects.all() - serializer_class = UserSerializer - filter_backends = [DjangoFilterBackend] - filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ] - - def get_queryset(self): - queryset = super().get_queryset() - # Sqlite doesn't support ORDER BY in subqueries - queryset = queryset.order_by("username") \ - if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' else queryset - - if "search" in self.request.GET: - pattern = self.request.GET["search"] - - # We match first a user by its username, then if an alias is matched without normalization - # And finally if the normalized pattern matches a normalized alias. - queryset = queryset.filter( - username__iregex="^" + pattern - ).union( - queryset.filter( - Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern) - ), all=True).union( - queryset.filter( - Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) - & ~Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern) - ), - all=True).union( - queryset.filter( - Q(note__alias__normalized_name__iregex="^" + pattern.lower()) - & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) - & ~Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern) - ), - all=True).union( - queryset.filter( - (Q(last_name__iregex="^" + pattern) | Q(first_name__iregex="^" + pattern)) - & ~Q(note__alias__normalized_name__iregex="^" + pattern.lower()) - & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) - & ~Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern) - ), - all=True) - - queryset = queryset if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' \ - else queryset.order_by("username") - - return queryset - - -# This ViewSet is the only one that is accessible from all authenticated users! -class ContentTypeViewSet(ReadOnlyModelViewSet): - """ - REST API View set. - The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer, - then render it on /api/users/ - """ - queryset = ContentType.objects.all() - serializer_class = ContentTypeSerializer - +from rest_framework import routers +from .viewsets import ContentTypeViewSet, UserViewSet # Routers provide an easy way of automatically determining the URL conf. # Register each app API router and user viewset diff --git a/apps/api/viewsets.py b/apps/api/viewsets.py index 01fc7998..825082c0 100644 --- a/apps/api/viewsets.py +++ b/apps/api/viewsets.py @@ -2,12 +2,22 @@ # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib.contenttypes.models import ContentType +from django_filters.rest_framework import DjangoFilterBackend +from django.db.models import Q +from django.conf import settings +from django.contrib.auth.models import User + +from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet + from permission.backends import PermissionBackend -from rest_framework import viewsets + from note_kfet.middlewares import get_current_session +from note.models import Alias + +from .serializers import UserSerializer, ContentTypeSerializer -class ReadProtectedModelViewSet(viewsets.ModelViewSet): +class ReadProtectedModelViewSet(ModelViewSet): """ Protect a ModelViewSet by filtering the objects that the user cannot see. """ @@ -22,7 +32,7 @@ class ReadProtectedModelViewSet(viewsets.ModelViewSet): return self.model.objects.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct() -class ReadOnlyProtectedModelViewSet(viewsets.ReadOnlyModelViewSet): +class ReadOnlyProtectedModelViewSet(ReadOnlyModelViewSet): """ Protect a ReadOnlyModelViewSet by filtering the objects that the user cannot see. """ @@ -35,3 +45,64 @@ class ReadOnlyProtectedModelViewSet(viewsets.ReadOnlyModelViewSet): user = self.request.user get_current_session().setdefault("permission_mask", 42) return self.model.objects.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct() + + + +class UserViewSet(ReadProtectedModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer, + then render it on /api/users/ + """ + queryset = User.objects.all() + serializer_class = UserSerializer + filter_backends = [DjangoFilterBackend] + filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ] + + def get_queryset(self): + queryset = super().get_queryset() + # Sqlite doesn't support ORDER BY in subqueries + queryset = queryset.order_by("username") \ + if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' else queryset + + if "search" in self.request.GET: + pattern = self.request.GET["search"] + + # We match first a user by its username, then if an alias is matched without normalization + # And finally if the normalized pattern matches a normalized alias. + queryset = queryset.filter( + username__iregex="^" + pattern).union( + queryset.filter( + Q(note__alias__name__iregex="^" + pattern) + & ~Q(username__iregex="^" + pattern)), all=True).union( + queryset.filter( + Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) + & ~Q(note__alias__name__iregex="^" + pattern) + & ~Q(username__iregex="^" + pattern)), all=True).union( + queryset.filter( + Q(note__alias__normalized_name__iregex="^" + pattern.lower()) + & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) + & ~Q(note__alias__name__iregex="^" + pattern) + & ~Q(username__iregex="^" + pattern)), all=True).union( + queryset.filter( + (Q(last_name__iregex="^" + pattern) | Q(first_name__iregex="^" + pattern)) + & ~Q(note__alias__normalized_name__iregex="^" + pattern.lower()) + & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) + & ~Q(note__alias__name__iregex="^" + pattern) + & ~Q(username__iregex="^" + pattern)), all=True) + + queryset = queryset if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' \ + else queryset.order_by("username") + + return queryset + + +# This ViewSet is the only one that is accessible from all authenticated users! +class ContentTypeViewSet(ReadOnlyModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer, + then render it on /api/users/ + """ + queryset = ContentType.objects.all() + serializer_class = ContentTypeSerializer From 6a2b46be72185dc5b208c8726bf359f273226061 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 2 Sep 2020 19:16:08 +0200 Subject: [PATCH 09/41] make API token button nicer --- .../member/templates/member/includes/profile_info.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/member/templates/member/includes/profile_info.html b/apps/member/templates/member/includes/profile_info.html index 04fc6742..372592d5 100644 --- a/apps/member/templates/member/includes/profile_info.html +++ b/apps/member/templates/member/includes/profile_info.html @@ -48,7 +48,9 @@ {% if user_object.pk == user_object.pk %} - - {% trans 'Manage auth token' %} - -{% endif %} \ No newline at end of file + +{% endif %} From 9d1a355ea14e539b05d930af0c8d00ea23f5c9b0 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 2 Sep 2020 21:14:02 +0200 Subject: [PATCH 10/41] French translation --- locale/fr/LC_MESSAGES/django.po | 1520 ++++++++++++++++++++++++++++--- 1 file changed, 1388 insertions(+), 132 deletions(-) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 0335d24a..96a0dd5c 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -3,20 +3,20 @@ # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # -#, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +"Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-01 10:28+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" +"POT-Creation-Date: 2020-09-02 19:18+0200\n" +"PO-Revision-Date: 2020-09-02 21:13+0200\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" +"Last-Translator: \n" +"Language-Team: \n" +"X-Generator: Poedit 2.3\n" #: apps/activity/apps.py:10 apps/activity/models.py:145 #: apps/activity/models.py:161 @@ -52,8 +52,8 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." #: apps/member/models.py:198 #: apps/member/templates/member/includes/club_info.html:4 #: apps/member/templates/member/includes/profile_info.html:4 -#: apps/note/models/notes.py:250 apps/note/models/transactions.py:26 -#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:297 +#: apps/note/models/notes.py:247 apps/note/models/transactions.py:26 +#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:302 #: apps/permission/models.py:329 #: apps/registration/templates/registration/future_profile_detail.html:16 #: apps/wei/models.py:66 apps/wei/models.py:118 @@ -105,13 +105,13 @@ msgstr "Lieu où l'activité est organisée, par exemple la Kfet." #: apps/activity/models.py:82 #: apps/activity/templates/activity/includes/activity_info.html:22 -#: apps/note/models/notes.py:226 apps/note/models/transactions.py:66 +#: apps/note/models/notes.py:223 apps/note/models/transactions.py:66 #: apps/permission/models.py:164 msgid "type" msgstr "type" #: apps/activity/models.py:88 apps/logs/models.py:22 apps/member/models.py:303 -#: apps/note/models/notes.py:142 apps/treasury/models.py:267 +#: apps/note/models/notes.py:138 apps/treasury/models.py:267 #: apps/treasury/templates/treasury/sogecredit_detail.html:14 #: apps/wei/models.py:160 apps/wei/templates/wei/survey.html:15 msgid "user" @@ -167,7 +167,7 @@ msgid "entry time" msgstr "heure d'entrée" #: apps/activity/models.py:172 apps/note/apps.py:14 -#: apps/note/models/notes.py:76 +#: apps/note/models/notes.py:78 msgid "note" msgstr "note" @@ -240,7 +240,7 @@ msgstr "Entré le " msgid "remove" msgstr "supprimer" -#: apps/activity/tables.py:80 apps/note/forms.py:66 apps/treasury/models.py:186 +#: apps/activity/tables.py:80 apps/note/forms.py:68 apps/treasury/models.py:186 msgid "Type" msgstr "Type" @@ -257,7 +257,7 @@ msgstr "Nom de famille" msgid "First name" msgstr "Prénom" -#: apps/activity/tables.py:86 apps/note/models/notes.py:85 +#: apps/activity/tables.py:86 apps/note/models/notes.py:87 msgid "Note" msgstr "Note" @@ -273,18 +273,18 @@ msgstr "Liste des invités" #: apps/note/models/transactions.py:259 #: apps/note/templates/note/transaction_form.html:16 #: apps/note/templates/note/transaction_form.html:148 -#: note_kfet/templates/base.html:78 +#: note_kfet/templates/base.html:68 msgid "Transfer" msgstr "Virement" #: apps/activity/templates/activity/activity_entry.html:18 -#: apps/note/models/transactions.py:313 +#: apps/note/models/transactions.py:318 #: apps/note/templates/note/transaction_form.html:21 msgid "Credit" msgstr "Crédit" #: apps/activity/templates/activity/activity_entry.html:21 -#: apps/note/models/transactions.py:313 +#: apps/note/models/transactions.py:318 #: apps/note/templates/note/transaction_form.html:25 msgid "Debit" msgstr "Débit" @@ -301,7 +301,7 @@ msgstr "Retour à la page de l'activité" #: apps/activity/templates/activity/activity_form.html:16 #: apps/member/templates/member/add_members.html:32 #: apps/member/templates/member/club_form.html:16 -#: apps/note/templates/note/transactiontemplate_form.html:15 +#: apps/note/templates/note/transactiontemplate_form.html:18 #: apps/treasury/forms.py:93 apps/treasury/forms.py:147 #: apps/treasury/templates/treasury/invoice_form.html:74 #: apps/wei/templates/wei/bus_form.html:17 @@ -333,7 +333,7 @@ msgstr "Toutes les activités" #: apps/activity/templates/activity/includes/activity_info.html:32 msgid "creater" -msgstr "Créateur" +msgstr "créateur" #: apps/activity/templates/activity/includes/activity_info.html:53 msgid "opened" @@ -356,9 +356,9 @@ msgid "validate" msgstr "valider" #: apps/activity/templates/activity/includes/activity_info.html:71 -#: apps/logs/models.py:62 apps/note/tables.py:169 +#: apps/logs/models.py:62 apps/note/tables.py:162 msgid "edit" -msgstr "Modifier" +msgstr "modifier" #: apps/activity/templates/activity/includes/activity_info.html:74 msgid "Invite" @@ -368,7 +368,7 @@ msgstr "Inviter" msgid "Create new activity" msgstr "Créer une nouvelle activité" -#: apps/activity/views.py:59 note_kfet/templates/base.html:96 +#: apps/activity/views.py:59 note_kfet/templates/base.html:86 msgid "Activities" msgstr "Activités" @@ -416,37 +416,37 @@ msgstr "Adresse IP" #: apps/logs/models.py:36 apps/permission/models.py:134 msgid "model" -msgstr "Modèle" +msgstr "modèle" #: apps/logs/models.py:43 msgid "identifier" -msgstr "Identifiant" +msgstr "identifiant" #: apps/logs/models.py:48 msgid "previous data" -msgstr "Données précédentes" +msgstr "données précédentes" #: apps/logs/models.py:53 msgid "new data" -msgstr "Nouvelles données" +msgstr "ouvelles données" #: apps/logs/models.py:61 msgid "create" -msgstr "Créer" +msgstr "créer" -#: apps/logs/models.py:63 apps/note/tables.py:139 apps/note/tables.py:175 +#: apps/logs/models.py:63 apps/note/tables.py:132 apps/note/tables.py:168 #: apps/permission/models.py:127 apps/treasury/tables.py:38 #: apps/wei/tables.py:75 msgid "delete" -msgstr "Supprimer" +msgstr "supprimer" #: apps/logs/models.py:66 msgid "action" -msgstr "Action" +msgstr "action" #: apps/logs/models.py:74 msgid "timestamp" -msgstr "Date" +msgstr "date" #: apps/logs/models.py:78 msgid "Logs cannot be destroyed." @@ -496,7 +496,7 @@ msgstr "Vous ne pouvez pas vous inscrire à la note si vous venez du futur." #: apps/member/forms.py:73 msgid "select an image" -msgstr "Choisissez une image" +msgstr "choisissez une image" #: apps/member/forms.py:74 msgid "Maximal size: 2MB" @@ -661,6 +661,7 @@ msgid "Tells if the user receive a salary." msgstr "Indique si l'utilisateur perçoit un salaire." #: apps/member/models.py:101 apps/treasury/tables.py:146 +#: env/lib/python3.8/site-packages/django/forms/widgets.py:711 msgid "No" msgstr "Non" @@ -772,7 +773,7 @@ msgstr "" "renouveler." #: apps/member/models.py:284 apps/member/models.py:309 -#: apps/note/models/notes.py:183 +#: apps/note/models/notes.py:179 msgid "club" msgstr "club" @@ -969,7 +970,7 @@ msgstr "solde du compte" #: apps/member/templates/member/includes/club_info.html:47 #: apps/member/templates/member/includes/profile_info.html:20 -#: apps/note/models/notes.py:273 apps/wei/templates/wei/base.html:66 +#: apps/note/models/notes.py:270 apps/wei/templates/wei/base.html:66 msgid "aliases" msgstr "alias" @@ -992,10 +993,9 @@ msgstr "mot de passe" msgid "Change password" msgstr "Changer le mot de passe" -#: apps/member/templates/member/includes/profile_info.html:52 -#: apps/member/views.py:318 -msgid "Manage auth token" -msgstr "Gérer les jetons d'authentification" +#: apps/member/templates/member/includes/profile_info.html:53 +msgid "API token" +msgstr "Acces API" #: apps/member/templates/member/manage_auth_tokens.html:19 msgid "Token" @@ -1049,6 +1049,10 @@ msgstr "Chercher un utilisateur" msgid "Update note picture" msgstr "Modifier la photo de la note" +#: apps/member/views.py:318 +msgid "Manage auth token" +msgstr "Gérer les jetons d'authentification" + #: apps/member/views.py:346 msgid "Create new club" msgstr "Créer un nouveau club" @@ -1088,6 +1092,9 @@ msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." #: apps/member/views.py:701 apps/member/views.py:703 apps/member/views.py:705 #: apps/registration/views.py:292 apps/registration/views.py:294 #: apps/registration/views.py:296 apps/wei/views.py:927 apps/wei/views.py:931 +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_form_helper.py:140 +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_form_helper.py:150 +#: env/lib/python3.8/site-packages/django/forms/fields.py:53 msgid "This field is required." msgstr "Ce champ est requis." @@ -1099,21 +1106,21 @@ msgstr "Gérer les rôles d'un utilisateur dans le club" msgid "Members of the club" msgstr "Membres du club" -#: apps/note/admin.py:133 apps/note/models/transactions.py:106 +#: apps/note/admin.py:129 apps/note/models/transactions.py:106 msgid "source" msgstr "source" -#: apps/note/admin.py:141 apps/note/admin.py:191 +#: apps/note/admin.py:137 apps/note/admin.py:205 #: apps/note/models/transactions.py:56 apps/note/models/transactions.py:119 msgid "destination" msgstr "destination" -#: apps/note/admin.py:196 apps/note/models/transactions.py:60 +#: apps/note/admin.py:210 apps/note/models/transactions.py:60 #: apps/note/models/transactions.py:137 msgid "amount" msgstr "montant" -#: apps/note/api/serializers.py:177 apps/note/api/serializers.py:183 +#: apps/note/api/serializers.py:178 apps/note/api/serializers.py:184 #: apps/note/models/transactions.py:224 msgid "" "The transaction can't be saved since the source note or the destination note " @@ -1122,74 +1129,74 @@ msgstr "" "La transaction ne peut pas être sauvegardée puisque la note source ou la " "note de destination n'est pas active." -#: apps/note/forms.py:37 +#: apps/note/forms.py:39 msgid "Source" msgstr "Source" -#: apps/note/forms.py:51 +#: apps/note/forms.py:53 msgid "Destination" msgstr "Destination" -#: apps/note/forms.py:72 apps/note/templates/note/transaction_form.html:119 +#: apps/note/forms.py:74 apps/note/templates/note/transaction_form.html:119 msgid "Reason" msgstr "Raison" -#: apps/note/forms.py:77 apps/treasury/tables.py:139 +#: apps/note/forms.py:79 apps/treasury/tables.py:139 msgid "Valid" msgstr "Valide" -#: apps/note/forms.py:83 +#: apps/note/forms.py:85 msgid "Total amount greater than" msgstr "Montant total supérieur à" -#: apps/note/forms.py:91 +#: apps/note/forms.py:93 msgid "Total amount less than" msgstr "Montant total inférieur à" -#: apps/note/forms.py:97 +#: apps/note/forms.py:99 msgid "Created after" msgstr "Créé après" -#: apps/note/forms.py:104 +#: apps/note/forms.py:106 msgid "Created before" msgstr "Créé avant" -#: apps/note/models/notes.py:30 +#: apps/note/models/notes.py:32 msgid "account balance" msgstr "solde du compte" -#: apps/note/models/notes.py:31 +#: apps/note/models/notes.py:33 msgid "in centimes, money credited for this instance" msgstr "en centimes, argent crédité pour cette instance" -#: apps/note/models/notes.py:36 +#: apps/note/models/notes.py:38 msgid "last negative date" msgstr "dernier date de négatif" -#: apps/note/models/notes.py:37 +#: apps/note/models/notes.py:39 msgid "last time the balance was negative" msgstr "dernier instant où la note était en négatif" -#: apps/note/models/notes.py:43 +#: apps/note/models/notes.py:45 msgid "display image" msgstr "image affichée" -#: apps/note/models/notes.py:52 apps/note/models/transactions.py:129 +#: apps/note/models/notes.py:54 apps/note/models/transactions.py:129 msgid "created at" msgstr "créée le" -#: apps/note/models/notes.py:57 +#: apps/note/models/notes.py:59 msgid "active" msgstr "actif" -#: apps/note/models/notes.py:60 +#: apps/note/models/notes.py:62 msgid "" "Designates whether this note should be treated as active. Unselect this " "instead of deleting notes." msgstr "" "Indique si la note est active. Désactiver cela plutôt que supprimer la note." -#: apps/note/models/notes.py:67 +#: apps/note/models/notes.py:69 msgid "" "The user blocked his/her note manually, eg. when he/she left the school for " "holidays. It can be reactivated at any time." @@ -1197,78 +1204,78 @@ msgstr "" "La note a été bloquée manuellement, par exemple pour des raisons de " "vacances. Elle peut être débloquée à tout moment." -#: apps/note/models/notes.py:69 +#: apps/note/models/notes.py:71 msgid "The note is blocked by the the BDE and can't be manually reactivated." msgstr "" "La note est bloquée de force par le BDE et ne peut pas être débloquée par le " "possesseur de la note." -#: apps/note/models/notes.py:77 +#: apps/note/models/notes.py:79 msgid "notes" msgstr "notes" -#: apps/note/models/notes.py:102 apps/note/models/notes.py:126 +#: apps/note/models/notes.py:122 msgid "This alias is already taken." msgstr "Cet alias est déjà pris." -#: apps/note/models/notes.py:146 +#: apps/note/models/notes.py:142 msgid "one's note" msgstr "note d'un utilisateur" -#: apps/note/models/notes.py:147 +#: apps/note/models/notes.py:143 msgid "users note" msgstr "notes des utilisateurs" -#: apps/note/models/notes.py:153 +#: apps/note/models/notes.py:149 #, python-format msgid "%(user)s's note" msgstr "Note de %(user)s" -#: apps/note/models/notes.py:187 +#: apps/note/models/notes.py:183 msgid "club note" msgstr "note d'un club" -#: apps/note/models/notes.py:188 +#: apps/note/models/notes.py:184 msgid "clubs notes" msgstr "notes des clubs" -#: apps/note/models/notes.py:194 +#: apps/note/models/notes.py:190 #, python-format msgid "Note of %(club)s club" msgstr "Note du club %(club)s" -#: apps/note/models/notes.py:232 +#: apps/note/models/notes.py:229 msgid "special note" msgstr "note spéciale" -#: apps/note/models/notes.py:233 +#: apps/note/models/notes.py:230 msgid "special notes" msgstr "notes spéciales" -#: apps/note/models/notes.py:256 +#: apps/note/models/notes.py:253 msgid "Invalid alias" msgstr "Alias invalide" -#: apps/note/models/notes.py:272 +#: apps/note/models/notes.py:269 msgid "alias" msgstr "alias" -#: apps/note/models/notes.py:296 +#: apps/note/models/notes.py:293 msgid "Alias is too long." msgstr "L'alias est trop long." -#: apps/note/models/notes.py:299 +#: apps/note/models/notes.py:296 msgid "" "This alias contains only complex character. Please use a more simple alias." msgstr "" "Cet alias ne contient que des caractères complexes. Merci d'utiliser un " "alias plus simple." -#: apps/note/models/notes.py:303 +#: apps/note/models/notes.py:300 msgid "An alias with a similar name already exists: {} " -msgstr "Un alias avec un nom similaire existe déjà : {}" +msgstr "Un alias avec un nom similaire existe déjà : {} " -#: apps/note/models/notes.py:316 +#: apps/note/models/notes.py:313 msgid "You can't delete your main alias." msgstr "Vous ne pouvez pas supprimer votre alias principal." @@ -1282,7 +1289,7 @@ msgstr "catégories de transaction" #: apps/note/models/transactions.py:49 msgid "A template with this name already exist" -msgstr "Un modèle de transaction avec un nom similaire existe déjà." +msgstr "Un modèle de transaction avec un nom similaire existe déjà" #: apps/note/models/transactions.py:72 msgid "display" @@ -1294,14 +1301,14 @@ msgstr "mis en avant" #: apps/note/models/transactions.py:87 msgid "transaction template" -msgstr "Modèle de transaction" +msgstr "modèle de transaction" #: apps/note/models/transactions.py:88 msgid "transaction templates" -msgstr "Modèles de transaction" +msgstr "modèles de transaction" #: apps/note/models/transactions.py:112 apps/note/models/transactions.py:125 -#: apps/note/tables.py:34 apps/note/tables.py:43 +#: apps/note/tables.py:33 apps/note/tables.py:42 msgid "used alias" msgstr "alias utilisé" @@ -1313,18 +1320,18 @@ msgstr "quantité" msgid "reason" msgstr "raison" -#: apps/note/models/transactions.py:151 apps/note/tables.py:114 +#: apps/note/models/transactions.py:151 apps/note/tables.py:107 msgid "invalidity reason" -msgstr "Motif d'invalidité" +msgstr "motif d'invalidité" #: apps/note/models/transactions.py:159 msgid "transaction" -msgstr "Transaction" +msgstr "transaction" #: apps/note/models/transactions.py:160 #: apps/treasury/templates/treasury/sogecredit_detail.html:22 msgid "transactions" -msgstr "Transactions" +msgstr "transactions" #: apps/note/models/transactions.py:182 #, python-brace-format @@ -1344,71 +1351,80 @@ msgstr "" "€ et 92 233 720 368 547 758.07 €. Ne cherchez pas à capitaliser l'argent du " "BDE." -#: apps/note/models/transactions.py:283 +#: apps/note/models/transactions.py:279 +msgid "" +"The destination of this transaction must equal to the destination of the " +"template." +msgstr "" +"Le destinataire de cette transaction doit être identique à celui du bouton " +"utilisé." + +#: apps/note/models/transactions.py:288 msgid "Template" msgstr "Bouton" -#: apps/note/models/transactions.py:286 +#: apps/note/models/transactions.py:291 msgid "recurrent transaction" -msgstr "Transaction issue de bouton" +msgstr "transaction issue de bouton" -#: apps/note/models/transactions.py:287 +#: apps/note/models/transactions.py:292 msgid "recurrent transactions" -msgstr "Transactions issues de boutons" +msgstr "transactions issues de boutons" -#: apps/note/models/transactions.py:302 +#: apps/note/models/transactions.py:307 msgid "first_name" msgstr "prénom" -#: apps/note/models/transactions.py:307 +#: apps/note/models/transactions.py:312 msgid "bank" msgstr "banque" -#: apps/note/models/transactions.py:324 +#: apps/note/models/transactions.py:329 msgid "" "A special transaction is only possible between a Note associated to a " "payment method and a User or a Club" msgstr "" "Une transaction spéciale n'est possible que entre une note associée à un " -"mode de paiement et un utilisateur ou un club." +"mode de paiement et un utilisateur ou un club" -#: apps/note/models/transactions.py:328 +#: apps/note/models/transactions.py:337 msgid "Special transaction" msgstr "Transaction de crédit/retrait" -#: apps/note/models/transactions.py:329 +#: apps/note/models/transactions.py:338 msgid "Special transactions" msgstr "Transactions de crédit/retrait" -#: apps/note/models/transactions.py:345 apps/note/models/transactions.py:350 +#: apps/note/models/transactions.py:354 apps/note/models/transactions.py:359 msgid "membership transaction" -msgstr "Transaction d'adhésion" +msgstr "transaction d'adhésion" -#: apps/note/models/transactions.py:346 apps/treasury/models.py:273 +#: apps/note/models/transactions.py:355 apps/treasury/models.py:273 msgid "membership transactions" -msgstr "Transactions d'adhésion" +msgstr "transactions d'adhésion" -#: apps/note/tables.py:62 +#: apps/note/tables.py:61 msgid "Click to invalidate" msgstr "Cliquez pour dévalider" -#: apps/note/tables.py:62 +#: apps/note/tables.py:61 msgid "Click to validate" msgstr "Cliquez pour valider" -#: apps/note/tables.py:112 +#: apps/note/tables.py:105 msgid "No reason specified" msgstr "Pas de motif spécifié" -#: apps/note/tables.py:143 apps/note/tables.py:177 apps/treasury/tables.py:39 +#: apps/note/tables.py:136 apps/note/tables.py:170 apps/treasury/tables.py:39 #: apps/treasury/templates/treasury/invoice_confirm_delete.html:30 #: apps/treasury/templates/treasury/sogecredit_detail.html:59 #: apps/wei/tables.py:76 apps/wei/tables.py:103 #: apps/wei/templates/wei/weiregistration_confirm_delete.html:31 +#: env/lib/python3.8/site-packages/django/forms/formsets.py:375 msgid "Delete" msgstr "Supprimer" -#: apps/note/tables.py:171 apps/note/templates/note/conso_form.html:132 +#: apps/note/tables.py:164 apps/note/templates/note/conso_form.html:132 #: apps/wei/tables.py:47 apps/wei/tables.py:48 #: apps/wei/templates/wei/base.html:89 #: apps/wei/templates/wei/bus_detail.html:20 @@ -1504,19 +1520,19 @@ msgstr "Sélection du destinataire" msgid "Transfer type" msgstr "Type de transfert" -#: apps/note/templates/note/transactiontemplate_form.html:10 +#: apps/note/templates/note/transactiontemplate_form.html:8 msgid "Buttons list" msgstr "Liste des boutons" -#: apps/note/templates/note/transactiontemplate_form.html:21 +#: apps/note/templates/note/transactiontemplate_form.html:24 msgid "Price history" msgstr "Historique des prix" -#: apps/note/templates/note/transactiontemplate_form.html:24 +#: apps/note/templates/note/transactiontemplate_form.html:27 msgid "Obsolete since" msgstr "Obsolète depuis" -#: apps/note/templates/note/transactiontemplate_form.html:24 +#: apps/note/templates/note/transactiontemplate_form.html:27 msgid "Current price" msgstr "Prix actuel" @@ -1530,11 +1546,11 @@ msgstr "Nouveau bouton" #: apps/note/templates/note/transactiontemplate_list.html:22 msgid "buttons listing " -msgstr "Liste des boutons" +msgstr "liste des boutons " #: apps/note/templates/note/transactiontemplate_list.html:73 msgid "button successfully deleted " -msgstr "Le bouton a bien été supprimé" +msgstr "le bouton a bien été supprimé " #: apps/note/templates/note/transactiontemplate_list.html:77 msgid "Unable to delete button " @@ -1556,7 +1572,7 @@ msgstr "Chercher un bouton" msgid "Update button" msgstr "Modifier le bouton" -#: apps/note/views.py:151 note_kfet/templates/base.html:72 +#: apps/note/views.py:151 note_kfet/templates/base.html:62 msgid "Consumptions" msgstr "Consommations" @@ -1580,7 +1596,7 @@ msgstr "Can {type} {model} in {query}" #: apps/permission/models.py:104 msgid "rank" -msgstr "Rang" +msgstr "rang" #: apps/permission/models.py:117 msgid "permission mask" @@ -1647,7 +1663,7 @@ msgstr "s'applique au club" #: apps/permission/models.py:349 apps/permission/models.py:350 msgid "role permissions" -msgstr "Permissions par rôles" +msgstr "permissions par rôles" #: apps/permission/signals.py:63 #, python-brace-format @@ -1735,7 +1751,7 @@ msgstr "" "Vous n'avez pas la permission d'ajouter une instance du modèle « {model} » " "avec ces paramètres. Merci de les corriger et de réessayer." -#: apps/permission/views.py:96 note_kfet/templates/base.html:114 +#: apps/permission/views.py:96 note_kfet/templates/base.html:104 msgid "Rights" msgstr "Droits" @@ -1890,11 +1906,11 @@ msgstr "Valider l'adresse e-mail" #: apps/registration/views.py:126 msgid "Email validation unsuccessful" -msgstr " La validation de l'adresse mail a échoué" +msgstr "La validation de l'adresse mail a échoué" #: apps/registration/views.py:137 msgid "Email validation email sent" -msgstr "L'email de vérification de l'adresse email a bien été envoyé." +msgstr "L'email de vérification de l'adresse email a bien été envoyé" #: apps/registration/views.py:145 msgid "Resend email validation link" @@ -1931,7 +1947,7 @@ msgstr "" msgid "Invalidate pre-registration" msgstr "Invalider l'inscription" -#: apps/treasury/apps.py:12 note_kfet/templates/base.html:102 +#: apps/treasury/apps.py:12 note_kfet/templates/base.html:92 msgid "Treasury" msgstr "Trésorerie" @@ -1993,7 +2009,7 @@ msgstr "Une facture ne peut plus être modifiée si elle est verrouillée." #: apps/treasury/models.py:76 msgid "tex source" -msgstr "Fichier TeX source" +msgstr "fichier TeX source" #: apps/treasury/models.py:89 #: apps/treasury/templates/treasury/invoice_form.html:22 @@ -2058,11 +2074,11 @@ msgstr "Remise n°{:d} : {}" #: apps/treasury/models.py:256 msgid "special transaction proxy" -msgstr "Proxy de transaction spéciale" +msgstr "proxy de transaction spéciale" #: apps/treasury/models.py:257 msgid "special transaction proxies" -msgstr "Proxys de transactions spéciales" +msgstr "proxys de transactions spéciales" #: apps/treasury/models.py:279 msgid "credit transaction" @@ -2105,6 +2121,7 @@ msgid "View" msgstr "Voir" #: apps/treasury/tables.py:146 +#: env/lib/python3.8/site-packages/django/forms/widgets.py:710 msgid "Yes" msgstr "Oui" @@ -2320,14 +2337,14 @@ msgstr "Gérer les crédits de la Société générale" #: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50 #: apps/wei/models.py:61 apps/wei/models.py:167 -#: note_kfet/templates/base.html:108 +#: note_kfet/templates/base.html:98 msgid "WEI" msgstr "WEI" #: apps/wei/forms/registration.py:51 apps/wei/models.py:113 #: apps/wei/models.py:283 msgid "bus" -msgstr "Bus" +msgstr "bus" #: apps/wei/forms/registration.py:52 msgid "" @@ -2459,11 +2476,11 @@ msgstr "problèmes de santé" #: apps/wei/models.py:224 apps/wei/templates/wei/weimembership_form.html:70 msgid "emergency contact name" -msgstr "Nom du contact en cas d'urgence" +msgstr "nom du contact en cas d'urgence" #: apps/wei/models.py:229 apps/wei/templates/wei/weimembership_form.html:73 msgid "emergency contact phone" -msgstr "Téléphone du contact en cas d'urgence" +msgstr "téléphone du contact en cas d'urgence" #: apps/wei/models.py:234 apps/wei/templates/wei/weimembership_form.html:52 msgid "first year" @@ -2499,15 +2516,15 @@ msgstr "équipe" #: apps/wei/models.py:303 msgid "WEI registration" -msgstr "inscription au WEI" +msgstr "Inscription au WEI" #: apps/wei/models.py:307 msgid "WEI membership" -msgstr "adhésion au WEI" +msgstr "Adhésion au WEI" #: apps/wei/models.py:308 msgid "WEI memberships" -msgstr "adhésions au WEI" +msgstr "Adhésions au WEI" #: apps/wei/tables.py:127 msgid "Year" @@ -2855,6 +2872,1242 @@ msgstr "Valider l'inscription WEI" msgid "This user didn't give her/his caution check." msgstr "Cet utilisateur n'a pas donné son chèque de caution." +#: env/lib/python3.8/site-packages/colorfield/fields.py:17 +msgid "Enter a valid color." +msgstr "Entrer une couleur valide." + +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout.py:355 +msgid "i18n text" +msgstr "i18n text" + +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout.py:357 +msgid "i18n legend" +msgstr "i18n legend" + +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout_objects.py:127 +#: env/lib/python3.8/site-packages/django/core/validators.py:31 +msgid "Enter a valid value." +msgstr "Entrer une valeur correcte." + +#: env/lib/python3.8/site-packages/django/contrib/messages/apps.py:7 +msgid "Messages" +msgstr "Messages" + +#: env/lib/python3.8/site-packages/django/contrib/sitemaps/apps.py:7 +msgid "Site Maps" +msgstr "Plan du site" + +#: env/lib/python3.8/site-packages/django/contrib/staticfiles/apps.py:9 +msgid "Static Files" +msgstr "Fichiers statiques" + +#: env/lib/python3.8/site-packages/django/contrib/syndication/apps.py:7 +msgid "Syndication" +msgstr "Invitation" + +#: env/lib/python3.8/site-packages/django/core/paginator.py:45 +msgid "That page number is not an integer" +msgstr "Ce numéro de page n'est pas entier" + +#: env/lib/python3.8/site-packages/django/core/paginator.py:47 +msgid "That page number is less than 1" +msgstr "Ce numéro de page est inférieur à 1" + +#: env/lib/python3.8/site-packages/django/core/paginator.py:52 +msgid "That page contains no results" +msgstr "Il n'y a pas de résultat" + +#: env/lib/python3.8/site-packages/django/core/validators.py:102 +#: env/lib/python3.8/site-packages/django/forms/fields.py:658 +msgid "Enter a valid URL." +msgstr "Entrer une URL valide." + +#: env/lib/python3.8/site-packages/django/core/validators.py:154 +msgid "Enter a valid integer." +msgstr "Entrer un entier valid." + +#: env/lib/python3.8/site-packages/django/core/validators.py:165 +msgid "Enter a valid email address." +msgstr "Entrer une adresse mail valide." + +#. Translators: "letters" means latin letters: a-z and A-Z. +#: env/lib/python3.8/site-packages/django/core/validators.py:239 +msgid "" +"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Entrer un 'slug' valide, constitué de lettres, chiffres, tirets ou " +"underscores." + +#: env/lib/python3.8/site-packages/django/core/validators.py:246 +msgid "" +"Enter a valid 'slug' consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" +"Entrer un 'slug' valide, constitué de caractère unicode, chiffres, tirets ou " +"underscores." + +#: env/lib/python3.8/site-packages/django/core/validators.py:255 +#: env/lib/python3.8/site-packages/django/core/validators.py:275 +msgid "Enter a valid IPv4 address." +msgstr "Entrer une adresse IPv4 valide." + +#: env/lib/python3.8/site-packages/django/core/validators.py:260 +#: env/lib/python3.8/site-packages/django/core/validators.py:276 +msgid "Enter a valid IPv6 address." +msgstr "Entrer une adresse IPv6 valide." + +#: env/lib/python3.8/site-packages/django/core/validators.py:270 +#: env/lib/python3.8/site-packages/django/core/validators.py:274 +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "Entrer une adresse IPv4 ou IPv6 valide." + +#: env/lib/python3.8/site-packages/django/core/validators.py:304 +msgid "Enter only digits separated by commas." +msgstr "Entrer seulement des chiffres séparés par des virgules." + +#: env/lib/python3.8/site-packages/django/core/validators.py:310 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" +"Vérifier que cette valeur est %(limit_value)s (actuellement %(show_value)s)." + +#: env/lib/python3.8/site-packages/django/core/validators.py:342 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Vérifier que cette valeur est plus petite que %(limit_value)s." + +#: env/lib/python3.8/site-packages/django/core/validators.py:351 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Vérifier que cette valeur est plus grande que %(limit_value)s." + +#: env/lib/python3.8/site-packages/django/core/validators.py:361 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Vérifier que cette valeur a au moins %(limit_value)d caractère " +"(actuellement %(show_value)d)." +msgstr[1] "" +"Assurer vous que cette valeur a au moins %(limit_value)d caractères " +"(actuellement %(show_value)d)." + +#: env/lib/python3.8/site-packages/django/core/validators.py:376 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +"Vérifier que cette valeur a au plus %(limit_value)d caractère (actuellement " +"%(show_value)d)." +msgstr[1] "" +"Assurer vous que cette valeur a au plus %(limit_value)d caractères " +"(actuellement %(show_value)d)." + +#: env/lib/python3.8/site-packages/django/core/validators.py:395 +#: env/lib/python3.8/site-packages/django/forms/fields.py:290 +#: env/lib/python3.8/site-packages/django/forms/fields.py:325 +msgid "Enter a number." +msgstr "Numéro de téléphone." + +#: env/lib/python3.8/site-packages/django/core/validators.py:397 +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "Vérifier qu'il n'y a pas plus de %(max)s chiffre au total." +msgstr[1] "Vérifier qu'il n'y a pas plus de %(max)s chiffres au total." + +#: env/lib/python3.8/site-packages/django/core/validators.py:402 +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "Vérifier qu'il y n'as pas plus de %(max)s chiffre décimal." +msgstr[1] "Vérifier qu'il y n'as pas plus de %(max)s chiffres décimaux." + +#: env/lib/python3.8/site-packages/django/core/validators.py:407 +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "Vérifier qu'il y n'as pas plus d'%(max)s chiffre avant la virgule." +msgstr[1] "" +"Vérifier qu'il y n'as pas plus de %(max)s chiffres avant la virgule." + +#: env/lib/python3.8/site-packages/django/core/validators.py:469 +#, python-format +msgid "" +"File extension '%(extension)s' is not allowed. Allowed extensions are: " +"'%(allowed_extensions)s'." +msgstr "" +"Les fichiers d'extension '%(extension)s' ne sont pas autorisé. Les extension " +"autorisées sont: \"%(allowed_extensions)s'." + +#: env/lib/python3.8/site-packages/django/core/validators.py:521 +msgid "Null characters are not allowed." +msgstr "Les caractères Null ne sont pas autorisés." + +#: env/lib/python3.8/site-packages/django/db/models/base.py:1165 +#: env/lib/python3.8/site-packages/django/forms/models.py:756 +msgid "and" +msgstr "et" + +#: env/lib/python3.8/site-packages/django/db/models/base.py:1167 +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "Un %(model_name)s avec ce %(field_labels)s existe déjà." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:104 +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "Le choix %(value)r n'est pas possible." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:105 +msgid "This field cannot be null." +msgstr "Ce champ est requis." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:106 +msgid "This field cannot be blank." +msgstr "Ce champ est requis." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:107 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "Un %(model_name)s avec ce %(field_label)s existe déjà." + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:111 +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" +"%(field_label)s doit être unique pour %(date_field_label)s %(lookup_type)s." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:128 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Champ du type %(field_type)s" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:905 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1772 +msgid "Integer" +msgstr "Nombre entier" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:909 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1770 +#, python-format +msgid "'%(value)s' value must be an integer." +msgstr "'%(value)s' doit être un nombre entier." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:984 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1850 +msgid "Big (8 byte) integer" +msgstr "Gros nombre entier (8 octets)" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:996 +#, python-format +msgid "'%(value)s' value must be either True or False." +msgstr "'%(value)s' doit être Vrai ou Faux." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:997 +#, python-format +msgid "'%(value)s' value must be either True, False, or None." +msgstr "'%(value)s' doit être Vrai, Faux, ou None." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:999 +msgid "Boolean (Either True or False)" +msgstr "Booléen (Vrai ou Faux)" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1040 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Chaîne de caractère (maximum %(max_length)s caractères)" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1104 +msgid "Comma-separated integers" +msgstr "Liste d'entier séparer par des virgules" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1153 +#, python-format +msgid "" +"'%(value)s' value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "'%(value)s n'est pas formaté correctement (AAAA-MM-JJ)." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1155 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1298 +#, python-format +msgid "" +"'%(value)s' value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" +"'%(value)s possède le format date requis (AAAA-MM-JJ) mais n'est pas une " +"date valide." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1158 +msgid "Date (without time)" +msgstr "Date (sans horaire)" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1296 +#, python-format +msgid "" +"'%(value)s' value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" +"'%(value)s' n'est pas formatée correctement (AAAA-MM-JJ HH:MM[:ss[.uuuuuu]]" +"[TZ])." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1300 +#, python-format +msgid "" +"'%(value)s' value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" +"'%(value)s' est formaté correctement, mais n'est pas une date/horaire valide." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1304 +msgid "Date (with time)" +msgstr "Date (avec horaire)" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1452 +#, python-format +msgid "'%(value)s' value must be a decimal number." +msgstr "'%(value)s doit être un nombre décimal." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1454 +msgid "Decimal number" +msgstr "Nombre décimal" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1593 +#, python-format +msgid "" +"'%(value)s' value has an invalid format. It must be in [DD] [HH:[MM:]]ss[." +"uuuuuu] format." +msgstr "" +"'%(value)s' n'est pas formatée correctement (AAAA-MM-JJ HH:MM[:ss[.uuuuuu]]" +"[TZ])." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1596 +msgid "Duration" +msgstr "Durée" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1646 +msgid "Email address" +msgstr "Courriel" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1669 +msgid "File path" +msgstr "Chemin du fichier" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1735 +#, python-format +msgid "'%(value)s' value must be a float." +msgstr "'%(value)s doit être un nombre décimal." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1737 +msgid "Floating point number" +msgstr "Nombre décimal" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1866 +msgid "IPv4 address" +msgstr "Adresse IPv4" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1897 +msgid "IP address" +msgstr "Adresse IP" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1977 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1978 +#, python-format +msgid "'%(value)s' value must be either None, True or False." +msgstr "'%(value)s' doit être Vrai, Faux, ou None." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1980 +msgid "Boolean (Either True, False or None)" +msgstr "Booléen (Vrai ou Faux)" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2015 +msgid "Positive integer" +msgstr "Nombre entier positif" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2028 +msgid "Positive small integer" +msgstr "Nombre entier positif petit" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2042 +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "Slug (maximum %(max_length)s caractères)" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2074 +msgid "Small integer" +msgstr "Petit entier" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2081 +msgid "Text" +msgstr "Texte" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2109 +#, python-format +msgid "" +"'%(value)s' value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "'%(value)s' n'est pas formatée correctement (HH:MM[:ss[.uuuuuu]])." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2111 +#, python-format +msgid "" +"'%(value)s' value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" +"'%(value)s' est formaté correctement, mais n'est pas un horaire valide." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2114 +msgid "Time" +msgstr "Temps" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2240 +msgid "URL" +msgstr "URL" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2262 +msgid "Raw binary data" +msgstr "Donnée binaire brute" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2312 +#, python-format +msgid "'%(value)s' is not a valid UUID." +msgstr "'%(value)s' n'est pas un UUID valide." + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2314 +msgid "Universally unique identifier" +msgstr "Identifiant unique universel" + +#: env/lib/python3.8/site-packages/django/db/models/fields/files.py:221 +msgid "File" +msgstr "Fichier" + +#: env/lib/python3.8/site-packages/django/db/models/fields/files.py:360 +msgid "Image" +msgstr "Image" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:778 +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "l'objet %(model)s avec les champs %(field)s %(value)r n'existe pas." + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:780 +msgid "Foreign Key (type determined by related field)" +msgstr "Relations plusieurs-à-un (type déterminé par le champ relié)" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1007 +msgid "One-to-one relationship" +msgstr "Relation un-à-un" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1057 +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "Relation %(from)s-%(to)s" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1058 +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "Relations %(from)s-%(to)s" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1100 +msgid "Many-to-many relationship" +msgstr "Relation plusieurs-à-plusieurs" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the label +#: env/lib/python3.8/site-packages/django/forms/boundfield.py:146 +msgid ":?.!" +msgstr ":?.!" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:245 +msgid "Enter a whole number." +msgstr "Entrer un numéro complet." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:396 +#: env/lib/python3.8/site-packages/django/forms/fields.py:1126 +msgid "Enter a valid date." +msgstr "Entrer une date valide." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:420 +#: env/lib/python3.8/site-packages/django/forms/fields.py:1127 +msgid "Enter a valid time." +msgstr "Entrer un horaire valide." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:442 +msgid "Enter a valid date/time." +msgstr "Entrer une date et un horaire valide." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:471 +msgid "Enter a valid duration." +msgstr "Entrer une durée valide." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:472 +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "Le nombre de jours doit être compris entre {min_days} et {max_days}." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:532 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Aucun fichier n'as été ajouté. Vérifier l'encodage du formulaire." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:533 +msgid "No file was submitted." +msgstr "Aucun fichier n'as été ajouté." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:534 +msgid "The submitted file is empty." +msgstr "Le fichier ajouté est vide." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:536 +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +"Vérifier que le nom du fichier a au plus %(max)d caractère (actuellement " +"%(length)d)." +msgstr[1] "" +"Vérifier que le nom du fichier a au plus %(max)d caractères (actuellement " +"%(length)d)." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:539 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "Veuillez ajouter un fichier ou cocher la case envoi vide." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:600 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Veuillez choisir une image valide." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:762 +#: env/lib/python3.8/site-packages/django/forms/fields.py:852 +#: env/lib/python3.8/site-packages/django/forms/models.py:1270 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Faites un choix valide. %(value)s n'est pas autorisé." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:853 +#: env/lib/python3.8/site-packages/django/forms/fields.py:968 +#: env/lib/python3.8/site-packages/django/forms/models.py:1269 +msgid "Enter a list of values." +msgstr "Entrer une liste de valeurs." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:969 +msgid "Enter a complete value." +msgstr "Entrer une valeur complète." + +#: env/lib/python3.8/site-packages/django/forms/fields.py:1185 +msgid "Enter a valid UUID." +msgstr "Entrer un UUID valid." + +#. Translators: This is the default suffix added to form field labels +#: env/lib/python3.8/site-packages/django/forms/forms.py:86 +msgid ":" +msgstr ":" + +#: env/lib/python3.8/site-packages/django/forms/forms.py:212 +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "(Champ caché %(name)s) %(error)s" + +#: env/lib/python3.8/site-packages/django/forms/formsets.py:91 +msgid "ManagementForm data is missing or has been tampered with" +msgstr "Les données ManagementForm sont manquantes ou falsifiées" + +#: env/lib/python3.8/site-packages/django/forms/formsets.py:338 +#, python-format +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "Veuillez remplir %d formulaire ou moins." +msgstr[1] "Veuillez remplir %d formulaires ou moins." + +#: env/lib/python3.8/site-packages/django/forms/formsets.py:345 +#, python-format +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "Veuillez remplir %d formulaire ou plus." +msgstr[1] "Veuillez remplir %d formulaires ou plus." + +#: env/lib/python3.8/site-packages/django/forms/formsets.py:371 +#: env/lib/python3.8/site-packages/django/forms/formsets.py:373 +msgid "Order" +msgstr "Ordre" + +#: env/lib/python3.8/site-packages/django/forms/models.py:751 +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Veuillez corriger les données dupliquées pour %(field)s." + +#: env/lib/python3.8/site-packages/django/forms/models.py:755 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "Veuillez rendre uniques les valeurs des champs %(field)s." + +#: env/lib/python3.8/site-packages/django/forms/models.py:761 +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" +"Veuillez rendre unique les valeurs des champs %(field_name)s pour les " +"%(lookup)s dans %(date_field)s." + +#: env/lib/python3.8/site-packages/django/forms/models.py:770 +msgid "Please correct the duplicate values below." +msgstr "Veuillez rendre uniques les valeurs ci-dessous." + +#: env/lib/python3.8/site-packages/django/forms/models.py:1091 +msgid "The inline value did not match the parent instance." +msgstr "La valeur renseignée ne correspond pas à l'objet parent." + +#: env/lib/python3.8/site-packages/django/forms/models.py:1158 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "Faites un choix possible." + +#: env/lib/python3.8/site-packages/django/forms/models.py:1272 +#, python-format +msgid "\"%(pk)s\" is not a valid value." +msgstr "\"%(pk)s\" n'est pas une valeur autorisée." + +#: env/lib/python3.8/site-packages/django/forms/utils.py:162 +#, python-format +msgid "" +"%(datetime)s couldn't be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" +"%(datetime)s n'a pas pu être interprété dans la zone %(current_timezone)s, " +"La valeur est ambigüe ou impossible." + +#: env/lib/python3.8/site-packages/django/forms/widgets.py:395 +msgid "Clear" +msgstr "Vider" + +#: env/lib/python3.8/site-packages/django/forms/widgets.py:396 +msgid "Currently" +msgstr "Activité en cours" + +#: env/lib/python3.8/site-packages/django/forms/widgets.py:397 +msgid "Change" +msgstr "Modifier" + +#: env/lib/python3.8/site-packages/django/forms/widgets.py:709 +msgid "Unknown" +msgstr "Inconnu" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:788 +msgid "yes,no,maybe" +msgstr "oui, non, peut-être" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:817 +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:834 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d octet" +msgstr[1] "%(size)d octets" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:836 +#, python-format +msgid "%s KB" +msgstr "%s Ko" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:838 +#, python-format +msgid "%s MB" +msgstr "%s Mo" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:840 +#, python-format +msgid "%s GB" +msgstr "%s Go" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:842 +#, python-format +msgid "%s TB" +msgstr "%s To" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:844 +#, python-format +msgid "%s PB" +msgstr "%s Po" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:62 +msgid "p.m." +msgstr "p.m." + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:63 +msgid "a.m." +msgstr "a.m." + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:68 +msgid "PM" +msgstr "PM" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:69 +msgid "AM" +msgstr "AM" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:150 +msgid "midnight" +msgstr "minuit" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:152 +msgid "noon" +msgstr "midi" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Monday" +msgstr "Lundi" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Tuesday" +msgstr "Mardi" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Wednesday" +msgstr "Mercredi" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Thursday" +msgstr "Jeudi" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Friday" +msgstr "Vendredi" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:7 +msgid "Saturday" +msgstr "Samedi" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:7 +msgid "Sunday" +msgstr "Dimanche" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Mon" +msgstr "Lun" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Tue" +msgstr "Mar" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Wed" +msgstr "Mer" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Thu" +msgstr "Jeu" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Fri" +msgstr "Ven" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:11 +msgid "Sat" +msgstr "Sam" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:11 +msgid "Sun" +msgstr "Dim" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "January" +msgstr "Janvier" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "February" +msgstr "Février" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "March" +msgstr "Mars" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "April" +msgstr "Avril" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "May" +msgstr "Mai" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "June" +msgstr "Juin" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "July" +msgstr "Juillet" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "August" +msgstr "Août" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "September" +msgstr "Septembre" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "October" +msgstr "Octobre" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "November" +msgstr "Novembre" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:16 +msgid "December" +msgstr "Décembre" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "jan" +msgstr "jan" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "feb" +msgstr "fev" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "mar" +msgstr "mar" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "apr" +msgstr "avr" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "may" +msgstr "mai" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "jun" +msgstr "jun" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "jul" +msgstr "jui" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "aug" +msgstr "aou" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "sep" +msgstr "sep" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "oct" +msgstr "oct" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "nov" +msgstr "nov" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "dec" +msgstr "dec" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:23 +msgctxt "abbrev. month" +msgid "Jan." +msgstr "[abbrev. month] Jan." + +#: env/lib/python3.8/site-packages/django/utils/dates.py:24 +msgctxt "abbrev. month" +msgid "Feb." +msgstr "[abbrev. month] Fév." + +#: env/lib/python3.8/site-packages/django/utils/dates.py:25 +msgctxt "abbrev. month" +msgid "March" +msgstr "[abbrev. month] Mars" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:26 +msgctxt "abbrev. month" +msgid "April" +msgstr "[abbrev. month] Avril" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:27 +msgctxt "abbrev. month" +msgid "May" +msgstr "[abbrev. month] Mai" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:28 +msgctxt "abbrev. month" +msgid "June" +msgstr "[abbrev. month] Juin" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:29 +msgctxt "abbrev. month" +msgid "July" +msgstr "[abbrev. month] Jui" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:30 +msgctxt "abbrev. month" +msgid "Aug." +msgstr "[abbrev. month] Août." + +#: env/lib/python3.8/site-packages/django/utils/dates.py:31 +msgctxt "abbrev. month" +msgid "Sept." +msgstr "[abbrev. month] Sep." + +#: env/lib/python3.8/site-packages/django/utils/dates.py:32 +msgctxt "abbrev. month" +msgid "Oct." +msgstr "[abbrev. month] Oct." + +#: env/lib/python3.8/site-packages/django/utils/dates.py:33 +msgctxt "abbrev. month" +msgid "Nov." +msgstr "[abbrev. month] Nov." + +#: env/lib/python3.8/site-packages/django/utils/dates.py:34 +msgctxt "abbrev. month" +msgid "Dec." +msgstr "[abbrev. month] Dec." + +#: env/lib/python3.8/site-packages/django/utils/dates.py:37 +msgctxt "alt. month" +msgid "January" +msgstr "[alt. month] Janvier" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:38 +msgctxt "alt. month" +msgid "February" +msgstr "[alt. month] Février" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:39 +msgctxt "alt. month" +msgid "March" +msgstr "[alt. month] Mars" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:40 +msgctxt "alt. month" +msgid "April" +msgstr "[alt. month] Avril" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:41 +msgctxt "alt. month" +msgid "May" +msgstr "[alt. month] Mai" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:42 +msgctxt "alt. month" +msgid "June" +msgstr "[alt. month] Juin" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:43 +msgctxt "alt. month" +msgid "July" +msgstr "[alt. month] Juillet" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:44 +msgctxt "alt. month" +msgid "August" +msgstr "[alt. month] Août" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:45 +msgctxt "alt. month" +msgid "September" +msgstr "[alt. month] Septembre" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:46 +msgctxt "alt. month" +msgid "October" +msgstr "[alt. month] Octobre" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:47 +msgctxt "alt. month" +msgid "November" +msgstr "[alt. month] Novembre" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:48 +msgctxt "alt. month" +msgid "December" +msgstr "[alt. month] Décembre" + +#: env/lib/python3.8/site-packages/django/utils/ipv6.py:8 +msgid "This is not a valid IPv6 address." +msgstr "Adresse IPv6 non valide." + +#: env/lib/python3.8/site-packages/django/utils/text.py:67 +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "[String to return when truncating text] %(truncated_text)s…" + +#: env/lib/python3.8/site-packages/django/utils/text.py:233 +msgid "or" +msgstr "ou" + +#. Translators: This string is used as a separator between list elements +#: env/lib/python3.8/site-packages/django/utils/text.py:252 +#: env/lib/python3.8/site-packages/django/utils/timesince.py:83 +msgid ", " +msgstr ", " + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:9 +#, python-format +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "%d année" +msgstr[1] "%d années" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:10 +#, python-format +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "%d mois" +msgstr[1] "%d mois" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:11 +#, python-format +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "%d semaine" +msgstr[1] "%d semaines" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:12 +#, python-format +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d jour" +msgstr[1] "%d jours" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:13 +#, python-format +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "%d heure" +msgstr[1] "%d heures" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:14 +#, python-format +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "%d minute" +msgstr[1] "%d minutes" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:72 +msgid "0 minutes" +msgstr "0 minutes" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:110 +msgid "Forbidden" +msgstr "Interdit" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:111 +msgid "CSRF verification failed. Request aborted." +msgstr "Echec de la vérification CSRF. Requête interrompue." + +#: env/lib/python3.8/site-packages/django/views/csrf.py:115 +msgid "" +"You are seeing this message because this HTTPS site requires a 'Referer " +"header' to be sent by your Web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" +"Vous voyez ce message car ce site HTTPS nécessite l'envoi d'un \"en-tête " +"référent\" par votre navigateur. Cet en-tête est necessaire pour des raisons " +"de sécurité, pour confirmer que votre navigateur n'est pas compromis par un " +"tiers." + +#: env/lib/python3.8/site-packages/django/views/csrf.py:120 +msgid "" +"If you have configured your browser to disable 'Referer' headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for 'same-" +"origin' requests." +msgstr "" +"Si vous avez configurer votre navigateur pour désactiver les \"en-tête " +"référent\", veuillez les réactiver, au moins pour ce site, ou pour les " +"requêtes HTTPS, ou les requête de même origine." + +#: env/lib/python3.8/site-packages/django/views/csrf.py:124 +msgid "" +"If you are using the tag or " +"including the 'Referrer-Policy: no-referrer' header, please remove them. The " +"CSRF protection requires the 'Referer' header to do strict referer checking. " +"If you're concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" +"Si vous utiliser le tag ou " +"incluez l'en-tête 'Referrer-Policy: no-referrer', veuillez les retirer. La " +"protection CSRF nécessite que l'en-tête référent fasse une vérification " +"stricte. Si vous avez des préoccupations pour votre vie privée, vous pouvez " +"utiliser des alternatives comme pour les liens " +"vers des sites tiers." + +#: env/lib/python3.8/site-packages/django/views/csrf.py:132 +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" +"Vous voyez ce message car ce site nécessite un cookie CSRF pour l'envoi de " +"formulaire. Ce cookie est demandé pour des raisons de sécurité, pour " +"s'assurer que votre navigateur n'est pas compromis par un tiers." + +#: env/lib/python3.8/site-packages/django/views/csrf.py:137 +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for 'same-origin' requests." +msgstr "" +"Si vous avez configuré votre navigateur pour désactiver les cookies, " +"veuillez les réactiver, au moins pour ce site, ou pour les requêtes de même " +"origine." + +#: env/lib/python3.8/site-packages/django/views/csrf.py:142 +msgid "More information is available with DEBUG=True." +msgstr "Plus d'informations disponible avec DEBUG=True." + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:41 +msgid "No year specified" +msgstr "Pas d'année spécifiée" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:61 +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:111 +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:208 +msgid "Date out of range" +msgstr "Date impossible" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:90 +msgid "No month specified" +msgstr "Pas de mois spécifié" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:142 +msgid "No day specified" +msgstr "Pas de jours spécifié" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:188 +msgid "No week specified" +msgstr "Pas de semaine spécifiée" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:338 +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:367 +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "%(verbose_name_plural)s non disponible" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:589 +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" +"Le futur %(verbose_name_plural)s n'est pas possible car %(class_name)s." +"allow_future = False." + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:623 +#, python-format +msgid "Invalid date string '%(datestr)s' given format '%(format)s'" +msgstr "Date '%(datestr)s' au format non valide '%(format)s'." + +#: env/lib/python3.8/site-packages/django/views/generic/detail.py:54 +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "Aucun %(verbose_name)s trouvé pour cette requête" + +#: env/lib/python3.8/site-packages/django/views/generic/list.py:67 +msgid "Page is not 'last', nor can it be converted to an int." +msgstr "" +"La page spécifié n'est pas la dernière (last) et ne peux pas être convertie " +"en nombre entier." + +#: env/lib/python3.8/site-packages/django/views/generic/list.py:72 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Le numéro de page %(page_number)s est non valide: %(message)s" + +#: env/lib/python3.8/site-packages/django/views/generic/list.py:154 +#, python-format +msgid "Empty list and '%(class_name)s.allow_empty' is False." +msgstr "Liste vide et '%(class_name)s.allow_empty = False." + +#: env/lib/python3.8/site-packages/django/views/static.py:40 +msgid "Directory indexes are not allowed here." +msgstr "L'index de Dossier n'est pas autorisé ici." + +#: env/lib/python3.8/site-packages/django/views/static.py:42 +#, python-format +msgid "\"%(path)s\" does not exist" +msgstr "\"%(path)s\" n'existe pas" + +#: env/lib/python3.8/site-packages/django/views/static.py:80 +#, python-format +msgid "Index of %(directory)s" +msgstr "Contenu de %(directory)s" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:6 +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" +"Django: Le cadriciel Web pour les perfectionniste avec des dates limites." + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:345 +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" +"Visiter les Notes de versions\" pour " +"Django %(version)s" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:367 +msgid "The install worked successfully! Congratulations!" +msgstr "L'installation a réussie! Félicitation!" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:368 +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" +"Vous voyez cette page car DEBUG=True est renseigné dans vos paramètres et vous n'avez pas " +"configuré d'URLs." + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:383 +msgid "Django Documentation" +msgstr "Documentation Django" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:384 +msgid "Topics, references, & how-to's" +msgstr "Sujet, references & how-to" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:395 +msgid "Tutorial: A Polling App" +msgstr "Tutoriel: Une application de sondage" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:396 +msgid "Get started with Django" +msgstr "Débuter avec Django" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:407 +msgid "Django Community" +msgstr "Communauté Django" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:408 +msgid "Connect, get help, or contribute" +msgstr "Prendre contact, obtenir de l'aide ou contribuer" + #: note_kfet/settings/base.py:155 msgid "German" msgstr "Allemand" @@ -2928,19 +4181,19 @@ msgstr "Réinitialiser" msgid "The ENS Paris-Saclay BDE note." msgstr "La note du BDE de l'ENS Paris-Saclay." -#: note_kfet/templates/base.html:84 +#: note_kfet/templates/base.html:74 msgid "Users" msgstr "Utilisateurs" -#: note_kfet/templates/base.html:90 +#: note_kfet/templates/base.html:80 msgid "Clubs" msgstr "Clubs" -#: note_kfet/templates/base.html:119 +#: note_kfet/templates/base.html:109 msgid "Admin" msgstr "Admin" -#: note_kfet/templates/base.html:163 +#: note_kfet/templates/base.html:153 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -3018,6 +4271,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 " +"renseigné votre nouveau mot de passe à deux reprises, pour être sur de " +"l'avoir tapé correctement." #: note_kfet/templates/registration/password_change_form.html:16 #: note_kfet/templates/registration/password_reset_confirm.html:17 From 06679b2e6a718ac936e2e08c1fc87a61dbe07e18 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 2 Sep 2020 22:27:04 +0200 Subject: [PATCH 11/41] Remove deprecation warning and enable some linting rules --- tox.ini | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 240c9523..51429fd0 100644 --- a/tox.ini +++ b/tox.ini @@ -11,8 +11,6 @@ skipsdist = True [testenv] sitepackages = True -setenv = - PYTHONWARNINGS = all deps = -r{toxinidir}/requirements.txt coverage @@ -34,8 +32,7 @@ commands = flake8 apps/activity apps/api apps/logs apps/member apps/note apps/permission apps/treasury apps/wei [flake8] -# Ignore too many errors, should be reduced in the future -ignore = D203, W503, E203, I100, I101, C901 +ignore = I100, I101 exclude = .tox, .git, From 3d20987b18877aefa4a0fb2489a516dd9893beff Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 2 Sep 2020 22:33:31 +0200 Subject: [PATCH 12/41] Ignore W503 in linting --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 51429fd0..ad5cdc6f 100644 --- a/tox.ini +++ b/tox.ini @@ -32,7 +32,7 @@ commands = flake8 apps/activity apps/api apps/logs apps/member apps/note apps/permission apps/treasury apps/wei [flake8] -ignore = I100, I101 +ignore = W503, I100, I101 exclude = .tox, .git, From 1b8cb7abb031cd3ec6949567848db723b470aff8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 2 Sep 2020 22:53:43 +0200 Subject: [PATCH 13/41] Send user id and group id in Docker console --- apps/scripts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/scripts b/apps/scripts index 1145f75a..4e1bcd18 160000 --- a/apps/scripts +++ b/apps/scripts @@ -1 +1 @@ -Subproject commit 1145f75a968999f24f9feb3fc82946aba14fb45d +Subproject commit 4e1bcd1808a24b532aa27bf2a119f6f8155af534 From bf7f5b9cd6f52099060f43c4a1abc577c08d1845 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 2 Sep 2020 22:54:01 +0200 Subject: [PATCH 14/41] Test and cover fully member app --- apps/member/admin.py | 4 +- apps/member/models.py | 54 +-- apps/member/signals.py | 6 +- .../templates/member/manage_auth_tokens.html | 2 +- apps/member/tests/test_login.py | 37 +- apps/member/tests/test_memberships.py | 407 ++++++++++++++++++ apps/member/views.py | 90 ++-- apps/note/api/views.py | 5 +- 8 files changed, 490 insertions(+), 115 deletions(-) create mode 100644 apps/member/tests/test_memberships.py diff --git a/apps/member/admin.py b/apps/member/admin.py index 4cc2d0bf..7936f564 100644 --- a/apps/member/admin.py +++ b/apps/member/admin.py @@ -31,9 +31,7 @@ class CustomUserAdmin(UserAdmin): """ When creating a new user don't show profile one the first step """ - if not obj: - return list() - return super().get_inline_instances(request, obj) + return super().get_inline_instances(request, obj) if obj else [] @admin.register(Club, site=admin_site) diff --git a/apps/member/models.py b/apps/member/models.py index b17f1f09..a1628fae 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -339,43 +339,40 @@ class Membership(models.Model): return self.date_start.toordinal() <= datetime.datetime.now().toordinal() def renew(self): - if Membership.objects.filter( + if not Membership.objects.filter( user=self.user, club=self.club, date_start__gte=self.club.membership_start, ).exists(): - # Membership is already renewed - return - new_membership = Membership( - user=self.user, - club=self.club, - date_start=max(self.date_end + datetime.timedelta(days=1), self.club.membership_start), - ) - if hasattr(self, '_force_renew_parent') and self._force_renew_parent: - new_membership._force_renew_parent = True - if hasattr(self, '_soge') and self._soge: - new_membership._soge = True - if hasattr(self, '_force_save') and self._force_save: - new_membership._force_save = True - new_membership.save() - new_membership.roles.set(self.roles.all()) - new_membership.save() + # Membership is not renewed yet + new_membership = Membership( + user=self.user, + club=self.club, + date_start=max(self.date_end + datetime.timedelta(days=1), self.club.membership_start), + ) + if hasattr(self, '_force_renew_parent') and self._force_renew_parent: + new_membership._force_renew_parent = True + if hasattr(self, '_soge') and self._soge: + new_membership._soge = True + if hasattr(self, '_force_save') and self._force_save: + new_membership._force_save = True + new_membership.save() + new_membership.roles.set(self.roles.all()) + new_membership.save() def save(self, *args, **kwargs): """ Calculate fee and end date before saving the membership and creating the transaction if needed. """ - - if self.pk: + created = not self.pk + if not created: for role in self.roles.all(): club = role.for_club if club is not None: if club.pk != self.club_id: raise ValidationError(_('The role {role} does not apply to the club {club}.') .format(role=role.name, club=club.name)) - - created = not self.pk - if created: + else: if Membership.objects.filter( user=self.user, club=self.club, @@ -384,7 +381,7 @@ class Membership(models.Model): ).exists(): raise ValidationError(_('User is already a member of the club')) - if self.club.parent_club is not None and not self.pk: + if self.club.parent_club is not None: # Check that the user is already a member of the parent club if the membership is created if not Membership.objects.filter( user=self.user, @@ -433,15 +430,10 @@ class Membership(models.Model): raise ValidationError(_('User is not a member of the parent club') + ' ' + self.club.parent_club.name) - if self.user.profile.paid: - self.fee = self.club.membership_fee_paid - else: - self.fee = self.club.membership_fee_unpaid + self.fee = self.club.membership_fee_paid if self.user.profile.paid else self.club.membership_fee_unpaid - if self.club.membership_duration is not None: - self.date_end = self.date_start + datetime.timedelta(days=self.club.membership_duration) - else: - self.date_end = self.date_start + datetime.timedelta(days=424242) + self.date_end = self.date_start + datetime.timedelta(days=self.club.membership_duration) \ + if self.club.membership_duration is not None else self.date_start + datetime.timedelta(days=424242) if self.club.membership_end is not None and self.date_end > self.club.membership_end: self.date_end = self.club.membership_end diff --git a/apps/member/signals.py b/apps/member/signals.py index 70162b37..43659b01 100644 --- a/apps/member/signals.py +++ b/apps/member/signals.py @@ -6,11 +6,7 @@ def save_user_profile(instance, created, raw, **_kwargs): """ Hook to create and save a profile when an user is updated if it is not registered with the signup form """ - if raw: - # When provisionning data, do not try to autocreate - return - - if created and instance.is_active: + if not raw and created and instance.is_active: from .models import Profile Profile.objects.get_or_create(user=instance) if instance.is_superuser: diff --git a/apps/member/templates/member/manage_auth_tokens.html b/apps/member/templates/member/manage_auth_tokens.html index 473286c1..014686f1 100644 --- a/apps/member/templates/member/manage_auth_tokens.html +++ b/apps/member/templates/member/manage_auth_tokens.html @@ -1,4 +1,4 @@ -{% extends "member/base.html" %} +{% extends "base.html" %} {% comment %} SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} diff --git a/apps/member/tests/test_login.py b/apps/member/tests/test_login.py index 51a4ab94..c4467f81 100644 --- a/apps/member/tests/test_login.py +++ b/apps/member/tests/test_login.py @@ -1,8 +1,10 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later - +from django.conf import settings from django.contrib.auth.models import User from django.test import TestCase +from django.urls import reverse + from note.models import TransactionTemplate, TemplateCategory """ @@ -31,7 +33,20 @@ class TemplateLoggedInTests(TestCase): sess.save() def test_login_page(self): - response = self.client.get('/accounts/login/') + response = self.client.get(reverse("login")) + self.assertEqual(response.status_code, 200) + + self.client.logout() + + response = self.client.post('/accounts/login/', data=dict( + username="admin", + password="adminadmin", + permission_mask=3, + )) + self.assertRedirects(response, settings.LOGIN_REDIRECT_URL, 302, 200) + + def test_logout(self): + response = self.client.get(reverse("logout")) self.assertEqual(response.status_code, 200) def test_admin_index(self): @@ -42,21 +57,3 @@ class TemplateLoggedInTests(TestCase): response = self.client.get('/accounts/password_reset/') self.assertEqual(response.status_code, 200) - def test_logout_page(self): - response = self.client.get('/accounts/logout/') - self.assertEqual(response.status_code, 200) - - def test_transfer_page(self): - response = self.client.get('/note/transfer/') - self.assertEqual(response.status_code, 200) - - def test_consos_page(self): - # Create one button and ensure that it is visible - cat = TemplateCategory.objects.create() - TransactionTemplate.objects.create( - destination_id=5, - category=cat, - amount=0, - ) - response = self.client.get('/note/consos/') - self.assertEqual(response.status_code, 200) diff --git a/apps/member/tests/test_memberships.py b/apps/member/tests/test_memberships.py new file mode 100644 index 00000000..8ad7b7cb --- /dev/null +++ b/apps/member/tests/test_memberships.py @@ -0,0 +1,407 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +import hashlib +import os +from datetime import date, timedelta + +from django.conf import settings +from django.contrib.auth.models import User +from django.core.files.uploadedfile import SimpleUploadedFile +from django.db.models import Q +from django.test import TestCase +from django.urls import reverse +from django.utils import timezone + +from member.models import Club, Membership, Profile +from note.models import Alias, NoteSpecial +from permission.models import Role +from treasury.models import SogeCredit + +""" +Create some users and clubs and test that all pages are rendering properly +and that memberships are working. +""" + + +class TestMemberships(TestCase): + fixtures = ('initial', ) + + def setUp(self) -> None: + """ + Create a sample superuser, a club and a membership for all tests. + """ + self.user = User.objects.create_superuser( + username="toto", + email="toto@example.com", + password="toto", + ) + self.user.profile.registration_valid = True + self.user.profile.email_confirmed = True + self.user.profile.save() + self.client.force_login(self.user) + + sess = self.client.session + sess["permission_mask"] = 42 + sess.save() + + self.club = Club.objects.create(name="totoclub", parent_club=Club.objects.get(name="BDE")) + self.bde_membership = Membership.objects.create(user=self.user, club=Club.objects.get(name="BDE")) + self.membership = Membership.objects.create(user=self.user, club=self.club) + self.membership.roles.add(Role.objects.get(name="Bureau de club")) + self.membership.save() + + def test_admin_pages(self): + """ + Check that Django Admin pages for the member app are loading successfully. + """ + response = self.client.get(reverse("admin:index") + "member/membership/") + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("admin:index") + "member/club/") + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("admin:index") + "auth/user/") + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("admin:index") + "auth/user/" + str(self.user.pk) + "/change/") + self.assertEqual(response.status_code, 200) + + def test_render_club_list(self): + """ + Render the list of all clubs, with a search. + """ + response = self.client.get(reverse("member:club_list")) + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("member:club_list") + "?search=toto") + self.assertEqual(response.status_code, 200) + + def test_render_club_create(self): + """ + Try to create a new club. + """ + response = self.client.get(reverse("member:club_create")) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("member:club_create"), data=dict( + name="Club toto", + email="clubtoto@example.com", + parent_club=self.club.pk, + require_memberships=False, + membership_fee_paid=0, + membership_fee_unpaid=0, + )) + self.assertTrue(Club.objects.filter(name="Club toto").exists()) + club = Club.objects.get(name="Club toto") + self.assertRedirects(response, club.get_absolute_url(), 302, 200) + + def test_render_club_detail(self): + """ + Display the detail of a club. + """ + response = self.client.get(reverse("member:club_detail", args=(self.club.pk,))) + self.assertEqual(response.status_code, 200) + + def test_render_club_update(self): + """ + Try to update the information about a club. + """ + response = self.client.get(reverse("member:club_update", args=(self.club.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("member:club_update", args=(self.club.pk, )), data=dict( + name="Toto club updated", + email="clubtoto@example.com", + require_memberships=True, + membership_fee_paid=0, + membership_fee_unpaid=0, + )) + self.assertRedirects(response, self.club.get_absolute_url(), 302, 200) + self.assertTrue(Club.objects.exclude(name="Toto club updated")) + + def test_render_club_update_picture(self): + """ + Try to update the picture of the note of a club. + """ + response = self.client.get(reverse("member:club_update_pic", args=(self.club.pk,))) + self.assertEqual(response.status_code, 200) + + old_pic = self.club.note.display_image + + with open("media/pic/default.png", "rb") as f: + image = SimpleUploadedFile("image.png", f.read(), "image/png") + response = self.client.post(reverse("member:club_update_pic", args=(self.club.pk,)), dict( + image=image, + x=0, + y=0, + width=200, + height=200, + )) + self.assertRedirects(response, self.club.get_absolute_url(), 302, 200) + + self.club.note.refresh_from_db() + self.assertTrue(os.path.exists(self.club.note.display_image.path)) + os.remove(self.club.note.display_image.path) + + self.club.note.display_image = old_pic + self.club.note.save() + + def test_render_club_aliases(self): + """ + Display the list of the aliases of a club. + """ + # Alias creation and deletion is already tested in the note app + response = self.client.get(reverse("member:club_alias", args=(self.club.pk,))) + self.assertEqual(response.status_code, 200) + + def test_render_club_members(self): + """ + Display the list of the members of a club. + """ + response = self.client.get(reverse("member:club_members", args=(self.club.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.get(reverse("member:club_members", args=(self.club.pk,)) + "?search=toto&roles=" + + ",".join([str(role.pk) for role in + Role.objects.filter(weirole__isnull=True).all()])) + self.assertEqual(response.status_code, 200) + + def test_render_club_add_member(self): + """ + Try to add memberships and renew them. + """ + response = self.client.get(reverse("member:club_add_member", args=(Club.objects.get(name="BDE").pk,))) + self.assertEqual(response.status_code, 200) + + user = User.objects.create(username="totototo") + user.profile.registration_valid = True + user.profile.email_confirmed = True + user.profile.save() + user.save() + + # We create a club without any parent and one club with parent BDE (that is the club Kfet) + for bde_parent in False, True: + if bde_parent: + club = Club.objects.get(name="Kfet") + else: + club = Club.objects.create( + name="Second club " + ("with BDE" if bde_parent else "without BDE"), + parent_club=None, + email="newclub@example.com", + require_memberships=True, + membership_fee_paid=1000, + membership_fee_unpaid=500, + membership_start=date.today(), + membership_end=date.today() + timedelta(days=366), + membership_duration=366, + ) + + response = self.client.get(reverse("member:club_add_member", args=(club.pk,))) + self.assertEqual(response.status_code, 200) + + # Create a new membership + response = self.client.post(reverse("member:club_add_member", args=(club.pk,)), data=dict( + user=user.pk, + date_start="{:%Y-%m-%d}".format(timezone.now().date()), + soge=False, + credit_type=NoteSpecial.objects.get(special_type="Espèces").id, + credit_amount=4200, + last_name="TOTO", + first_name="Toto", + bank="Le matelas", + )) + self.assertRedirects(response, club.get_absolute_url(), 302, 200) + + self.assertTrue(Membership.objects.filter(user=user, club=club).exists()) + + # Membership is sent to the past to check renewals + membership = Membership.objects.get(user=user, club=club) + self.assertTrue(membership.valid) + membership.date_start = date(year=2000, month=1, day=1) + membership.date_end = date(year=2000, month=12, day=31) + membership.save() + self.assertFalse(membership.valid) + + response = self.client.get(reverse("member:club_members", args=(club.pk,)) + "?only_active=0") + self.assertEqual(response.status_code, 200) + + bde_membership = self.bde_membership + if bde_parent: + bde_membership = Membership.objects.get(club__name="BDE", user=user) + bde_membership.date_start = date(year=2000, month=1, day=1) + bde_membership.date_end = date(year=2000, month=12, day=31) + bde_membership.save() + + response = self.client.get(reverse("member:club_renew_membership", args=(bde_membership.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.get(reverse("member:club_renew_membership", args=(membership.pk,))) + self.assertEqual(response.status_code, 200) + + # Renew membership + response = self.client.post(reverse("member:club_renew_membership", args=(membership.pk,)), data=dict( + user=user.pk, + date_start="{:%Y-%m-%d}".format(timezone.now().date()), + soge=bde_parent, + credit_type=NoteSpecial.objects.get(special_type="Chèque").id, + credit_amount=14242, + last_name="TOTO", + first_name="Toto", + bank="Bank", + )) + self.assertRedirects(response, club.get_absolute_url(), 302, 200) + + response = self.client.get(user.profile.get_absolute_url()) + self.assertEqual(response.status_code, 200) + + def test_auto_join_kfet_when_join_bde_with_soge(self): + """ + When we join the BDE club with a Soge registration, a Kfet membership is automatically created. + We check that it is the case. + """ + user = User.objects.create(username="new1A") + user.profile.registration_valid = True + user.profile.email_confirmed = True + user.profile.save() + user.save() + + bde = Club.objects.get(name="BDE") + kfet = Club.objects.get(name="Kfet") + + response = self.client.post(reverse("member:club_add_member", args=(bde.pk,)), data=dict( + user=user.pk, + date_start="{:%Y-%m-%d}".format(timezone.now().date()), + soge=True, + credit_type=NoteSpecial.objects.get(special_type="Virement bancaire").id, + credit_amount=(bde.membership_fee_paid + kfet.membership_fee_paid) / 100, + last_name="TOTO", + first_name="Toto", + bank="Société générale", + )) + self.assertRedirects(response, bde.get_absolute_url(), 302, 200) + + self.assertTrue(Membership.objects.filter(user=user, club=bde).exists()) + self.assertTrue(Membership.objects.filter(user=user, club=kfet).exists()) + self.assertTrue(SogeCredit.objects.filter(user=user).exists()) + + def test_change_roles(self): + """ + Check to change the roles of a membership. + """ + response = self.client.get(reverse("member:club_manage_roles", args=(self.membership.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("member:club_manage_roles", args=(self.membership.pk,)), data=dict( + roles=[role.id for role in Role.objects.filter( + Q(name="Membre de club") | Q(name="Trésorier·ère de club") | Q(name="Bureau de club")).all()], + )) + self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200) + self.membership.refresh_from_db() + self.assertEqual(self.membership.roles.count(), 3) + + def test_render_user_list(self): + """ + Display the user search page. + """ + response = self.client.get(reverse("member:user_list")) + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("member:user_list") + "?search=toto") + self.assertEqual(response.status_code, 200) + + def test_render_user_detail(self): + """ + Display the user detail page. + """ + response = self.client.get(self.user.profile.get_absolute_url()) + self.assertEqual(response.status_code, 200) + + def test_render_user_update(self): + """ + Update some data about the user. + """ + response = self.client.get(reverse("member:user_update_profile", args=(self.user.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("member:user_update_profile", args=(self.user.pk,)), data=dict( + first_name="Toto", + last_name="Toto", + username="toto changed", + email="updated@example.com", + phone_number="+33600000000", + section="", + department="A0", + promotion=timezone.now().year, + address="Earth", + paid=True, + ml_events_registration="en", + ml_sports_registration=True, + ml_art_registration=True, + report_frequency=7, + )) + self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200) + self.assertTrue(User.objects.filter(username="toto changed").exists()) + self.assertTrue(Profile.objects.filter(address="Earth").exists()) + self.assertTrue(Alias.objects.filter(normalized_name="totochanged").exists()) + + def test_render_user_update_picture(self): + """ + Update the note picture of the user. + """ + response = self.client.get(reverse("member:user_update_pic", args=(self.user.pk,))) + self.assertEqual(response.status_code, 200) + + old_pic = self.user.note.display_image + + with open("media/pic/default.png", "rb") as f: + image = SimpleUploadedFile("image.png", f.read(), "image/png") + response = self.client.post(reverse("member:user_update_pic", args=(self.user.pk,)), dict( + image=image, + x=0, + y=0, + width=200, + height=200, + )) + self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200) + + self.user.note.refresh_from_db() + self.assertTrue(os.path.exists(self.user.note.display_image.path)) + os.remove(self.user.note.display_image.path) + + self.user.note.display_image = old_pic + self.user.note.save() + + def test_render_user_aliases(self): + """ + Display the list of aliases of the user. + """ + # Alias creation and deletion is already tested in the note app + response = self.client.get(reverse("member:user_alias", args=(self.user.pk,))) + self.assertEqual(response.status_code, 200) + + def test_manage_auth_token(self): + """ + Display the page to see the API authentication token, see it and regenerate it. + :return: + """ + response = self.client.get(reverse("member:auth_token")) + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("member:auth_token") + "?view") + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("member:auth_token") + "?regenerate") + self.assertRedirects(response, reverse("member:auth_token") + "?view", 302, 200) + + def test_random_coverage(self): + # Useless, only for coverage + self.assertEqual(str(self.user), str(self.user.profile)) + self.user.profile.promotion = None + self.assertEqual(self.user.profile.ens_year, 0) + self.membership.date_end = None + self.assertTrue(self.membership.valid) + + def test_nk15_hasher(self): + """ + Test that NK15 passwords are successfully imported. + """ + salt = "42" + password = "strongpassword42" + hashed = hashlib.sha256((salt + password).encode("utf-8")).hexdigest() + self.user.password = "custom_nk15$1$" + salt + "|" + hashed + self.user.save() + self.assertTrue(self.user.check_password(password)) diff --git a/apps/member/views.py b/apps/member/views.py index cccffc4a..c2f9f136 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -97,8 +97,7 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): note = NoteUser.objects.filter( alias__normalized_name=Alias.normalize(new_username)) if note.exists() and note.get().user != self.object: - form.add_error('username', - _("An alias with a similar name already exists.")) + form.add_error('username', _("An alias with a similar name already exists.")) return super().form_invalid(form) # Check if the username is one of user's aliases. alias = Alias.objects.filter(name=new_username) @@ -142,9 +141,8 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): We can't display information of a not registered user. """ qs = super().get_queryset() - if self.request.user.is_superuser and self.request.session.get("permission_mask", -1) >= 42: - return qs - return qs.filter(profile__registration_valid=True) + return qs if self.request.user.is_superuser and self.request.session.get("permission_mask", -1) >= 42\ + else qs.filter(profile__registration_valid=True) def get_context_data(self, **kwargs): """ @@ -204,14 +202,16 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ Filter the user list with the given pattern. """ - qs = super().get_queryset().distinct("username").annotate(alias=F("note__alias__name"))\ + qs = super().get_queryset().annotate(alias=F("note__alias__name"))\ .annotate(normalized_alias=F("note__alias__normalized_name"))\ - .filter(profile__registration_valid=True).order_by("username") - if "search" in self.request.GET: - pattern = self.request.GET["search"] + .filter(profile__registration_valid=True) - if not pattern: - return qs.none() + # Sqlite doesn't support order by in subqueries + qs = qs.order_by("username").distinct("username")\ + if settings.DATABASES[qs.db]["ENGINE"] == 'django.db.backends.postgresql' else qs.distinct() + + if "search" in self.request.GET and self.request.GET["search"]: + pattern = self.request.GET["search"] qs = qs.filter( username__iregex="^" + pattern @@ -270,12 +270,7 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det def post(self, request, *args, **kwargs): form = self.get_form() self.object = self.get_object() - if form.is_valid(): - return self.form_valid(form) - else: - print('is_invalid') - print(form) - return self.form_invalid(form) + return self.form_valid(form) if form.is_valid() else self.form_invalid(form) def form_valid(self, form): image_field = form.cleaned_data['image'] @@ -320,8 +315,7 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView): def get(self, request, *args, **kwargs): if 'regenerate' in request.GET and Token.objects.filter(user=request.user).exists(): Token.objects.get(user=self.request.user).delete() - return redirect(reverse_lazy('member:auth_token') + "?show", - permanent=True) + return redirect(reverse_lazy('member:auth_token') + "?show") return super().get(request, *args, **kwargs) @@ -351,8 +345,9 @@ class ClubCreateView(ProtectQuerysetMixin, ProtectedCreateView): email="", ) - def form_valid(self, form): - return super().form_valid(form) + def get_success_url(self): + self.object.refresh_from_db() + return reverse_lazy("member:club_detail", kwargs={"pk": self.object.pk}) class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): @@ -655,7 +650,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView): fee += c.membership_fee_paid if user.profile.paid else c.membership_fee_unpaid c = c.parent_club - if user.note.balance + credit_amount < fee and not Membership.objects.filter( + if not soge and user.note.balance + credit_amount < fee and not Membership.objects.filter( club__name="Kfet", user=user, date_start__lte=date.today(), @@ -683,7 +678,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView): if club.membership_end and form.instance.date_start > club.membership_end: form.add_error('user', _("The membership must begin before {:%m-%d-%Y}.") - .format(form.instance.club.membership_start)) + .format(form.instance.club.membership_end)) return super().form_invalid(form) # Now, all is fine, the membership can be created. @@ -719,46 +714,38 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView): transaction._force_save = True transaction.save() + # Parent club memberships are automatically renewed / created. + # For example, a Kfet membership creates a BDE membership if it does not exist. form.instance._force_renew_parent = True ret = super().form_valid(form) - if club.name == "BDE": - member_role = Role.objects.filter(Q(name="Adhérent BDE") | Q(name="Membre de club")).all() - elif club.name == "Kfet": - member_role = Role.objects.filter(Q(name="Adhérent Kfet") | Q(name="Membre de club")).all() - else: - member_role = Role.objects.filter(name="Membre de club").all() + member_role = Role.objects.filter(Q(name="Adhérent BDE") | Q(name="Membre de club")).all() \ + if club.name == "BDE" else Role.objects.filter(Q(name="Adhérent Kfet") | Q(name="Membre de club")).all() \ + if club.name == "Kfet"else Role.objects.filter(name="Membre de club").all() form.instance.roles.set(member_role) form.instance._force_save = True form.instance.save() # If Société générale pays, then we assume that this is the BDE membership, and we auto-renew the # Kfet membership. - if soge: - # If not already done, create BDE and Kfet memberships - bde = Club.objects.get(name="BDE") + if soge and club.name == "BDE": kfet = Club.objects.get(name="Kfet") + fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid - soge_clubs = [bde, kfet] - for club in soge_clubs: - fee = club.membership_fee_paid if user.profile.paid else club.membership_fee_unpaid - - # Get current membership, to get the end date - old_membership = Membership.objects.filter( - club=club, - user=user, - ).order_by("-date_start") - - if old_membership.filter(date_start__gte=club.membership_start).exists(): - # Membership is already renewed - continue + # Get current membership, to get the end date + old_membership = Membership.objects.filter( + club=kfet, + user=user, + ).order_by("-date_start") + if not old_membership.filter(date_start__gte=kfet.membership_start).exists(): + # If the membership is not already renewed membership = Membership( - club=club, + club=kfet, user=user, fee=fee, - date_start=max(old_membership.first().date_end + timedelta(days=1), club.membership_start) + date_start=max(old_membership.first().date_end + timedelta(days=1), kfet.membership_start) if old_membership.exists() else form.instance.date_start, ) membership._force_save = True @@ -767,10 +754,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView): membership.refresh_from_db() if old_membership.exists(): membership.roles.set(old_membership.get().roles.all()) - elif c.name == "BDE": - membership.roles.set(Role.objects.filter(Q(name="Adhérent BDE") | Q(name="Membre de club")).all()) - elif c.name == "Kfet": - membership.roles.set(Role.objects.filter(Q(name="Adhérent Kfet") | Q(name="Membre de club")).all()) + membership.roles.set(Role.objects.filter(Q(name="Adhérent Kfet") | Q(name="Membre de club")).all()) membership.save() return ret @@ -830,9 +814,7 @@ class ClubMembersListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableV qs = qs.filter(date_start__lte=timezone.now().today(), date_end__gte=timezone.now().today()) if "roles" in self.request.GET: - if not self.request.GET["roles"]: - return qs.none() - roles_str = self.request.GET["roles"].replace(' ', '').split(',') + roles_str = self.request.GET["roles"].replace(' ', '').split(',') if self.request.GET["roles"] else ['0'] roles_int = map(int, roles_str) qs = qs.filter(roles__in=roles_int) diff --git a/apps/note/api/views.py b/apps/note/api/views.py index 1ab954c9..a478e7ff 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -118,7 +118,7 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet): queryset = super().get_queryset() # Sqlite doesn't support ORDER BY in subqueries - queryset = queryset.order_by("username") \ + queryset = queryset.order_by("name") \ if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' else queryset alias = self.request.query_params.get("alias", ".*") @@ -140,6 +140,9 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet): ), all=True) + queryset = queryset if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' \ + else queryset.order_by("name") + return queryset.distinct() From be6cf93cdb003624b8e2aa763a59d9aac2e9c8b1 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 2 Sep 2020 23:08:40 +0200 Subject: [PATCH 15/41] Move default profile picture in member app --- .../member/static/member/img/default_picture.png | Bin apps/note/templates/note/conso_form.html | 2 +- apps/note/templates/note/transaction_form.html | 2 +- note_kfet/static/js/base.js | 2 +- note_kfet/static/js/consos.js | 2 +- note_kfet/static/js/transfer.js | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename media/pic/default.png => apps/member/static/member/img/default_picture.png (100%) diff --git a/media/pic/default.png b/apps/member/static/member/img/default_picture.png similarity index 100% rename from media/pic/default.png rename to apps/member/static/member/img/default_picture.png diff --git a/apps/note/templates/note/conso_form.html b/apps/note/templates/note/conso_form.html index 07c63488..d5914055 100644 --- a/apps/note/templates/note/conso_form.html +++ b/apps/note/templates/note/conso_form.html @@ -15,7 +15,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
-
diff --git a/apps/note/templates/note/transaction_form.html b/apps/note/templates/note/transaction_form.html index acb09beb..15478219 100644 --- a/apps/note/templates/note/transaction_form.html +++ b/apps/note/templates/note/transaction_form.html @@ -38,7 +38,7 @@ SPDX-License-Identifier: GPL-2.0-or-later {# Preview note profile (picture, username and balance) #}
-
diff --git a/note_kfet/static/js/base.js b/note_kfet/static/js/base.js index a20c72bc..8315f01a 100644 --- a/note_kfet/static/js/base.js +++ b/note_kfet/static/js/base.js @@ -122,7 +122,7 @@ function displayStyle (note) { */ function displayNote (note, alias, user_note_field = null, profile_pic_field = null) { if (!note.display_image) { - note.display_image = '/media/pic/default.png'; + note.display_image = '/static/member/img/default_picture.png'; } let img = note.display_image; if (alias !== note.name && note.name) diff --git a/note_kfet/static/js/consos.js b/note_kfet/static/js/consos.js index fc04b2b2..ec5a0940 100644 --- a/note_kfet/static/js/consos.js +++ b/note_kfet/static/js/consos.js @@ -158,7 +158,7 @@ function reset() { $("#consos_list").html(""); $("#note").val(""); $("#note").attr("data-original-title", "").tooltip("hide"); - $("#profile_pic").attr("src", "/media/pic/default.png"); + $("#profile_pic").attr("src", "/static/member/img/default_picture.png"); $("#profile_pic_link").attr("href", "#"); refreshHistory(); refreshBalance(); diff --git a/note_kfet/static/js/transfer.js b/note_kfet/static/js/transfer.js index 28b28aef..cbae7456 100644 --- a/note_kfet/static/js/transfer.js +++ b/note_kfet/static/js/transfer.js @@ -40,7 +40,7 @@ function reset(refresh=true) { $("#first_name").val(""); $("#bank").val(""); $("#user_note").val(""); - $("#profile_pic").attr("src", "/media/pic/default.png"); + $("#profile_pic").attr("src", "/static/member/img/default_picture.png"); $("#profile_pic_link").attr("href", "#"); if (refresh) { refreshBalance(); From 7fc255953029a48b5817b6cfed2bfa8bf13ef8f1 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 2 Sep 2020 23:14:35 +0200 Subject: [PATCH 16/41] French update --- locale/fr/LC_MESSAGES/django.po | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 96a0dd5c..1b2f1dd5 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-09-02 19:18+0200\n" -"PO-Revision-Date: 2020-09-02 21:13+0200\n" +"PO-Revision-Date: 2020-09-02 21:38+0200\n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2935,8 +2935,8 @@ msgstr "Entrer une adresse mail valide." msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "" -"Entrer un 'slug' valide, constitué de lettres, chiffres, tirets ou " -"underscores." +"Entrer un 'slug' valide, constitué de lettres, chiffres, tirets ou tirets " +"bas." #: env/lib/python3.8/site-packages/django/core/validators.py:246 msgid "" @@ -3052,7 +3052,7 @@ msgstr "" #: env/lib/python3.8/site-packages/django/core/validators.py:521 msgid "Null characters are not allowed." -msgstr "Les caractères Null ne sont pas autorisés." +msgstr "Les caractères nuls ne sont pas autorisés." #: env/lib/python3.8/site-packages/django/db/models/base.py:1165 #: env/lib/python3.8/site-packages/django/forms/models.py:756 From 4b149213f9dd82d9b5bc85a0060c10ef00cca4ee Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 2 Sep 2020 23:14:55 +0200 Subject: [PATCH 17/41] =?UTF-8?q?Anfang=20der=20Deutschen=20=C3=9Cbersetzu?= =?UTF-8?q?ng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locale/de/LC_MESSAGES/django.po | 1901 +++++++++++++++++++++++++------ 1 file changed, 1559 insertions(+), 342 deletions(-) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 3145e2ad..0ce8cf0d 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -3,67 +3,68 @@ # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # -#, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +"Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-01 10:28+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" +"POT-Creation-Date: 2020-09-02 19:18+0200\n" +"PO-Revision-Date: 2020-09-02 23:14+0200\n" +"Language: de\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" +"Last-Translator: \n" +"Language-Team: \n" +"X-Generator: Poedit 2.3\n" #: apps/activity/apps.py:10 apps/activity/models.py:145 #: apps/activity/models.py:161 msgid "activity" -msgstr "" +msgstr "Veranstaltung" #: apps/activity/forms.py:35 apps/activity/models.py:130 msgid "The end date must be after the start date." -msgstr "" +msgstr "Das Abschlussdatum muss nach das Anfangsdatum sein." #: apps/activity/forms.py:76 apps/activity/models.py:257 msgid "You can't invite someone once the activity is started." msgstr "" +"Sie dürfen nicht jemandem einladen wenn die Veranstaltung angefangen hat." #: apps/activity/forms.py:79 apps/activity/models.py:260 msgid "This activity is not validated yet." -msgstr "" +msgstr "Diese Veranstaltung ist noch nicht bestätigt." #: apps/activity/forms.py:89 apps/activity/models.py:268 msgid "This person has been already invited 5 times this year." -msgstr "" +msgstr "Diese Person wurde schon 5 mal dieses Jahr eingeladen." #: apps/activity/forms.py:93 apps/activity/models.py:272 msgid "This person is already invited." -msgstr "" +msgstr "Diese Person wurde schon eingeladen." #: apps/activity/forms.py:97 apps/activity/models.py:276 msgid "You can't invite more than 3 people to this activity." -msgstr "" +msgstr "Sie dürfen höchstens 3 Leute zu dieser Veranstaltung einladen." #: apps/activity/models.py:27 apps/activity/models.py:62 #: apps/member/models.py:198 #: apps/member/templates/member/includes/club_info.html:4 #: apps/member/templates/member/includes/profile_info.html:4 -#: apps/note/models/notes.py:250 apps/note/models/transactions.py:26 -#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:297 +#: apps/note/models/notes.py:247 apps/note/models/transactions.py:26 +#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:302 #: apps/permission/models.py:329 #: apps/registration/templates/registration/future_profile_detail.html:16 #: apps/wei/models.py:66 apps/wei/models.py:118 #: apps/wei/templates/wei/base.html:26 #: apps/wei/templates/wei/weimembership_form.html:14 msgid "name" -msgstr "" +msgstr "Name" #: apps/activity/models.py:32 msgid "manage entries" -msgstr "" +msgstr "Einträge verwalten" #: apps/activity/models.py:33 msgid "Enable the support of entries for this activity." @@ -72,233 +73,235 @@ msgstr "" #: apps/activity/models.py:38 #: apps/activity/templates/activity/includes/activity_info.html:42 msgid "can invite" -msgstr "" +msgstr "Darf einladen" #: apps/activity/models.py:43 #: apps/activity/templates/activity/includes/activity_info.html:46 msgid "guest entry fee" -msgstr "" +msgstr "Gasteintrittspreis" #: apps/activity/models.py:48 msgid "activity type" -msgstr "" +msgstr "Veranstaltungart" #: apps/activity/models.py:49 msgid "activity types" -msgstr "" +msgstr "Vearnstaltungarte" #: apps/activity/models.py:67 #: apps/activity/templates/activity/includes/activity_info.html:19 #: apps/note/models/transactions.py:81 apps/permission/models.py:110 #: apps/permission/models.py:189 apps/wei/models.py:72 apps/wei/models.py:129 msgid "description" -msgstr "" +msgstr "Beschreibung" #: apps/activity/models.py:71 msgid "location" -msgstr "" +msgstr "Ort" #: apps/activity/models.py:75 msgid "Place where the activity is organized, eg. Kfet." -msgstr "" +msgstr "Wo findet die Veranstaltung statt ? (z.B Kfet)" #: apps/activity/models.py:82 #: apps/activity/templates/activity/includes/activity_info.html:22 -#: apps/note/models/notes.py:226 apps/note/models/transactions.py:66 +#: apps/note/models/notes.py:223 apps/note/models/transactions.py:66 #: apps/permission/models.py:164 msgid "type" -msgstr "" +msgstr "Type" #: apps/activity/models.py:88 apps/logs/models.py:22 apps/member/models.py:303 -#: apps/note/models/notes.py:142 apps/treasury/models.py:267 +#: apps/note/models/notes.py:138 apps/treasury/models.py:267 #: apps/treasury/templates/treasury/sogecredit_detail.html:14 #: apps/wei/models.py:160 apps/wei/templates/wei/survey.html:15 msgid "user" -msgstr "" +msgstr "User" #: apps/activity/models.py:95 #: apps/activity/templates/activity/includes/activity_info.html:36 msgid "organizer" -msgstr "" +msgstr "Veranstalter" #: apps/activity/models.py:96 msgid "Club that organizes the activity. The entry fees will go to this club." msgstr "" +"Die Veranstaltung wurde von einem Club organisert. Die Eintrittsbeiträge " +"gehen an diesen Club." #: apps/activity/models.py:103 #: apps/activity/templates/activity/includes/activity_info.html:39 msgid "attendees club" -msgstr "" +msgstr "Teilnehmer" #: apps/activity/models.py:104 msgid "Club that is authorized to join the activity. Mostly the Kfet club." -msgstr "" +msgstr "Club die an die Veranstaltung teilnehmen können." #: apps/activity/models.py:108 #: apps/activity/templates/activity/includes/activity_info.html:25 msgid "start date" -msgstr "" +msgstr "Anfangsdatum" #: apps/activity/models.py:112 #: apps/activity/templates/activity/includes/activity_info.html:28 msgid "end date" -msgstr "" +msgstr "Abschlussdatum" #: apps/activity/models.py:117 #: apps/activity/templates/activity/includes/activity_info.html:50 #: apps/note/models/transactions.py:146 msgid "valid" -msgstr "" +msgstr "gültig" #: apps/activity/models.py:122 #: apps/activity/templates/activity/includes/activity_info.html:65 msgid "open" -msgstr "" +msgstr "geöffnet" #: apps/activity/models.py:146 msgid "activities" -msgstr "" +msgstr "Veranstaltungen" #: apps/activity/models.py:166 msgid "entry time" -msgstr "" +msgstr "Eintrittzeit" #: apps/activity/models.py:172 apps/note/apps.py:14 -#: apps/note/models/notes.py:76 +#: apps/note/models/notes.py:78 msgid "note" -msgstr "" +msgstr "Note" #: apps/activity/models.py:183 #: apps/activity/templates/activity/activity_entry.html:46 msgid "entry" -msgstr "" +msgstr "Eintritt" #: apps/activity/models.py:184 #: apps/activity/templates/activity/activity_entry.html:46 msgid "entries" -msgstr "" +msgstr "Eintritte" #: apps/activity/models.py:190 msgid "Already entered on " -msgstr "" +msgstr "Schon eingetretten" #: apps/activity/models.py:190 apps/activity/tables.py:54 msgid "{:%Y-%m-%d %H:%M:%S}" -msgstr "" +msgstr "{:%Y-%m-%d %H:%M:%S}" #: apps/activity/models.py:198 msgid "The balance is negative." -msgstr "" +msgstr "Kontostand ist im Rot" #: apps/activity/models.py:228 msgid "last name" -msgstr "" +msgstr "Nachname" #: apps/activity/models.py:233 #: apps/member/templates/member/includes/profile_info.html:4 #: apps/registration/templates/registration/future_profile_detail.html:16 #: apps/wei/templates/wei/weimembership_form.html:14 msgid "first name" -msgstr "" +msgstr "Vorname" #: apps/activity/models.py:240 msgid "inviter" -msgstr "" +msgstr "Einlader" #: apps/activity/models.py:284 msgid "guest" -msgstr "" +msgstr "Gast" #: apps/activity/models.py:285 msgid "guests" -msgstr "" +msgstr "Gäste" #: apps/activity/models.py:297 msgid "Invitation" -msgstr "" +msgstr "Einladung" #: apps/activity/tables.py:25 msgid "The activity is currently open." -msgstr "" +msgstr "Die Veranstaltung ist geöffnet" #: apps/activity/tables.py:26 msgid "The validation of the activity is pending." -msgstr "" +msgstr "Diese Veranstaltung ist noch nicht bestätigt" #: apps/activity/tables.py:41 apps/treasury/tables.py:107 msgid "Remove" -msgstr "" +msgstr "entfernen" #: apps/activity/tables.py:54 msgid "Entered on " -msgstr "" +msgstr "Eingetreten um" #: apps/activity/tables.py:56 msgid "remove" -msgstr "" +msgstr "entfernen" -#: apps/activity/tables.py:80 apps/note/forms.py:66 apps/treasury/models.py:186 +#: apps/activity/tables.py:80 apps/note/forms.py:68 apps/treasury/models.py:186 msgid "Type" -msgstr "" +msgstr "Type" #: apps/activity/tables.py:82 apps/member/forms.py:131 #: apps/registration/forms.py:81 apps/treasury/forms.py:135 #: apps/wei/forms/registration.py:96 msgid "Last name" -msgstr "" +msgstr "Nachname" #: apps/activity/tables.py:84 apps/member/forms.py:136 #: apps/note/templates/note/transaction_form.html:134 #: apps/registration/forms.py:86 apps/treasury/forms.py:137 #: apps/wei/forms/registration.py:101 msgid "First name" -msgstr "" +msgstr "Vorname" -#: apps/activity/tables.py:86 apps/note/models/notes.py:85 +#: apps/activity/tables.py:86 apps/note/models/notes.py:87 msgid "Note" -msgstr "" +msgstr "Note" #: apps/activity/tables.py:88 apps/member/tables.py:46 msgid "Balance" -msgstr "" +msgstr "Kontostand" #: apps/activity/templates/activity/activity_detail.html:15 msgid "Guests list" -msgstr "" +msgstr "Gastliste" #: apps/activity/templates/activity/activity_entry.html:14 #: apps/note/models/transactions.py:259 #: apps/note/templates/note/transaction_form.html:16 #: apps/note/templates/note/transaction_form.html:148 -#: note_kfet/templates/base.html:78 +#: note_kfet/templates/base.html:68 msgid "Transfer" -msgstr "" +msgstr "Überweisen" #: apps/activity/templates/activity/activity_entry.html:18 -#: apps/note/models/transactions.py:313 +#: apps/note/models/transactions.py:318 #: apps/note/templates/note/transaction_form.html:21 msgid "Credit" -msgstr "" +msgstr "Kredit" #: apps/activity/templates/activity/activity_entry.html:21 -#: apps/note/models/transactions.py:313 +#: apps/note/models/transactions.py:318 #: apps/note/templates/note/transaction_form.html:25 msgid "Debit" -msgstr "" +msgstr "Soll" #: apps/activity/templates/activity/activity_entry.html:27 #: apps/note/templates/note/transaction_form.html:30 msgid "Entries" -msgstr "" +msgstr "Eintritte" #: apps/activity/templates/activity/activity_entry.html:37 msgid "Return to activity page" -msgstr "" +msgstr "Zurück zur Veranstaltungseite" #: apps/activity/templates/activity/activity_form.html:16 #: apps/member/templates/member/add_members.html:32 #: apps/member/templates/member/club_form.html:16 -#: apps/note/templates/note/transactiontemplate_form.html:15 +#: apps/note/templates/note/transactiontemplate_form.html:18 #: apps/treasury/forms.py:93 apps/treasury/forms.py:147 #: apps/treasury/templates/treasury/invoice_form.html:74 #: apps/wei/templates/wei/bus_form.html:17 @@ -306,219 +309,219 @@ msgstr "" #: apps/wei/templates/wei/weiclub_form.html:17 #: apps/wei/templates/wei/weiregistration_form.html:18 msgid "Submit" -msgstr "" +msgstr "vorlegen" #: apps/activity/templates/activity/activity_list.html:12 msgid "Current activity" -msgstr "" +msgstr "aktuelle Veranstaltung" #: apps/activity/templates/activity/activity_list.html:24 msgid "Upcoming activities" -msgstr "" +msgstr "Zukünftige Veranstaltungen" #: apps/activity/templates/activity/activity_list.html:31 msgid "There is no planned activity." -msgstr "" +msgstr "Es gibt keine geplante Veranstaltung " #: apps/activity/templates/activity/activity_list.html:38 msgid "New activity" -msgstr "" +msgstr "Neue Veranstaltung" #: apps/activity/templates/activity/activity_list.html:45 msgid "All activities" -msgstr "" +msgstr "Alle Veranstaltungen" #: apps/activity/templates/activity/includes/activity_info.html:32 msgid "creater" -msgstr "" +msgstr "Verantworter" #: apps/activity/templates/activity/includes/activity_info.html:53 msgid "opened" -msgstr "" +msgstr "geöffnet" #: apps/activity/templates/activity/includes/activity_info.html:60 msgid "Entry page" -msgstr "" +msgstr "Eintrittseite" #: apps/activity/templates/activity/includes/activity_info.html:65 msgid "close" -msgstr "" +msgstr "Schlusss" #: apps/activity/templates/activity/includes/activity_info.html:68 msgid "invalidate" -msgstr "" +msgstr "invalidate" #: apps/activity/templates/activity/includes/activity_info.html:68 msgid "validate" -msgstr "" +msgstr "validate" #: apps/activity/templates/activity/includes/activity_info.html:71 -#: apps/logs/models.py:62 apps/note/tables.py:169 +#: apps/logs/models.py:62 apps/note/tables.py:162 msgid "edit" -msgstr "" +msgstr "bearbeiten" #: apps/activity/templates/activity/includes/activity_info.html:74 msgid "Invite" -msgstr "" +msgstr "einladen" #: apps/activity/views.py:29 msgid "Create new activity" -msgstr "" +msgstr "Neue Veranstaltung schaffen" -#: apps/activity/views.py:59 note_kfet/templates/base.html:96 +#: apps/activity/views.py:59 note_kfet/templates/base.html:86 msgid "Activities" -msgstr "" +msgstr "Veranstaltungen" #: apps/activity/views.py:87 msgid "Activity detail" -msgstr "" +msgstr "Veranstaltunginfo" #: apps/activity/views.py:107 msgid "Update activity" -msgstr "" +msgstr "Veranstaltung bearbeiten" #: apps/activity/views.py:134 msgid "Invite guest to the activity \"{}\"" -msgstr "" +msgstr "Gast zur Veranstaltung \"{}\" einladen" #: apps/activity/views.py:168 msgid "You are not allowed to display the entry interface for this activity." -msgstr "" +msgstr "Sie haben nicht das Recht diese Seite zu benuzten." #: apps/activity/views.py:171 msgid "This activity does not support activity entries." -msgstr "" +msgstr "Diese Veranstaltung braucht nicht Eintritt" #: apps/activity/views.py:174 msgid "This activity is closed." -msgstr "" +msgstr "Diese Veranstaltung ist geschlossen." #: apps/activity/views.py:272 msgid "Entry for activity \"{}\"" -msgstr "" +msgstr "Eintritt zur Veranstaltung \"{}\"" #: apps/api/apps.py:10 msgid "API" -msgstr "" +msgstr "API" #: apps/logs/apps.py:11 msgid "Logs" -msgstr "" +msgstr "Logs" #: apps/logs/models.py:28 msgid "IP Address" -msgstr "" +msgstr "IP Adresse" #: apps/logs/models.py:36 apps/permission/models.py:134 msgid "model" -msgstr "" +msgstr "Model" #: apps/logs/models.py:43 msgid "identifier" -msgstr "" +msgstr "Kennzeichnung" #: apps/logs/models.py:48 msgid "previous data" -msgstr "" +msgstr "ehemalige Daten" #: apps/logs/models.py:53 msgid "new data" -msgstr "" +msgstr "neue Daten" #: apps/logs/models.py:61 msgid "create" -msgstr "" +msgstr "schaffen" -#: apps/logs/models.py:63 apps/note/tables.py:139 apps/note/tables.py:175 +#: apps/logs/models.py:63 apps/note/tables.py:132 apps/note/tables.py:168 #: apps/permission/models.py:127 apps/treasury/tables.py:38 #: apps/wei/tables.py:75 msgid "delete" -msgstr "" +msgstr "entfernen" #: apps/logs/models.py:66 msgid "action" -msgstr "" +msgstr "Aktion" #: apps/logs/models.py:74 msgid "timestamp" -msgstr "" +msgstr "Zeitstempel" #: apps/logs/models.py:78 msgid "Logs cannot be destroyed." -msgstr "" +msgstr "Logs können nicht entfernen sein." #: apps/logs/models.py:81 msgid "changelog" -msgstr "" +msgstr "Changelog" #: apps/logs/models.py:82 msgid "changelogs" -msgstr "" +msgstr "Changelogs" #: apps/member/admin.py:52 apps/member/models.py:225 #: apps/member/templates/member/includes/club_info.html:34 msgid "membership fee (paid students)" -msgstr "" +msgstr "Mitgliedschaftpreis (bezahlte Studenten)" #: apps/member/admin.py:53 apps/member/models.py:230 #: apps/member/templates/member/includes/club_info.html:37 msgid "membership fee (unpaid students)" -msgstr "" +msgstr "Mitgliedschaftpreis (unbezahlte Studenten)" #: apps/member/admin.py:67 apps/member/models.py:314 msgid "roles" -msgstr "" +msgstr "Rollen" #: apps/member/admin.py:68 apps/member/models.py:328 msgid "fee" -msgstr "" +msgstr "Preis" #: apps/member/apps.py:14 apps/wei/tables.py:181 apps/wei/tables.py:212 msgid "member" -msgstr "" +msgstr "Mitglied" #: apps/member/forms.py:41 msgid "Report frequency" -msgstr "" +msgstr "Bericht Frequenz" #: apps/member/forms.py:43 msgid "Last report date" -msgstr "" +msgstr "letzen Bericht Datum" #: apps/member/forms.py:48 msgid "You can't register to the note if you come from the future." -msgstr "" +msgstr "Sie dürfen nicht einloggen wenn sie aus der Zukunft kommen." #: apps/member/forms.py:73 msgid "select an image" -msgstr "" +msgstr "Wählen sie ein Bild aus" #: apps/member/forms.py:74 msgid "Maximal size: 2MB" -msgstr "" +msgstr "Maximal Größe: 2MB" #: apps/member/forms.py:87 apps/member/views.py:101 #: apps/registration/forms.py:33 msgid "An alias with a similar name already exists." -msgstr "" +msgstr "Ein ähnliches Alias ist schon benutzt." #: apps/member/forms.py:110 apps/registration/forms.py:61 msgid "Inscription paid by Société Générale" -msgstr "" +msgstr "Mitgliedschaft von der Société Générale bezahlt." #: apps/member/forms.py:112 apps/registration/forms.py:63 msgid "Check this case is the Société Générale paid the inscription." -msgstr "" +msgstr "Hat die Société Générale die Mitgliedschaft bezahlt ?" #: apps/member/forms.py:117 apps/registration/forms.py:68 #: apps/wei/forms/registration.py:83 msgid "Credit type" -msgstr "" +msgstr "Kredittype" #: apps/member/forms.py:118 apps/registration/forms.py:69 #: apps/wei/forms/registration.py:84 msgid "No credit" -msgstr "" +msgstr "Kein Kredit" #: apps/member/forms.py:120 msgid "You can credit the note of the user." @@ -527,187 +530,194 @@ msgstr "" #: apps/member/forms.py:124 apps/registration/forms.py:74 #: apps/wei/forms/registration.py:89 msgid "Credit amount" -msgstr "" +msgstr "Kreditanzahl" #: apps/member/forms.py:141 apps/note/templates/note/transaction_form.html:140 #: apps/registration/forms.py:91 apps/treasury/forms.py:139 #: apps/wei/forms/registration.py:106 msgid "Bank" -msgstr "" +msgstr "Bank" #: apps/member/forms.py:168 msgid "User" -msgstr "" +msgstr "User" #: apps/member/forms.py:182 msgid "Roles" -msgstr "" +msgstr "Rollen" #: apps/member/models.py:38 #: apps/member/templates/member/includes/profile_info.html:34 #: apps/registration/templates/registration/future_profile_detail.html:40 #: apps/wei/templates/wei/weimembership_form.html:44 msgid "phone number" -msgstr "" +msgstr "Telefonnummer" #: apps/member/models.py:45 #: apps/member/templates/member/includes/profile_info.html:28 #: apps/registration/templates/registration/future_profile_detail.html:34 #: apps/wei/templates/wei/weimembership_form.html:38 msgid "section" -msgstr "" +msgstr "Section" #: apps/member/models.py:46 msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" -msgstr "" +msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\"" #: apps/member/models.py:54 apps/wei/templates/wei/weimembership_form.html:32 msgid "department" -msgstr "" +msgstr "Fachabteilung" #: apps/member/models.py:56 msgid "Informatics (A0)" -msgstr "" +msgstr "Informatik (A0)" #: apps/member/models.py:57 msgid "Mathematics (A1)" -msgstr "" +msgstr "Mathematik (A1)" #: apps/member/models.py:58 msgid "Physics (A2)" -msgstr "" +msgstr "Physik (A2)" #: apps/member/models.py:59 msgid "Applied physics (A'2)" -msgstr "" +msgstr "Angewandte Physik (A'2)" #: apps/member/models.py:60 msgid "Chemistry (A''2)" -msgstr "" +msgstr "Chemie (A''2)" #: apps/member/models.py:61 msgid "Biology (A3)" -msgstr "" +msgstr "Biologie (A3)" #: apps/member/models.py:62 msgid "SAPHIRE (B1234)" -msgstr "" +msgstr "SAPHIRE (B1234)" #: apps/member/models.py:63 msgid "Mechanics (B1)" -msgstr "" +msgstr "Mechanik (B1)" #: apps/member/models.py:64 msgid "Civil engineering (B2)" -msgstr "" +msgstr "Bauingenieur (B2)" #: apps/member/models.py:65 msgid "Mechanical engineering (B3)" -msgstr "" +msgstr "Mechanikingenieur (B3)" #: apps/member/models.py:66 msgid "EEA (B4)" -msgstr "" +msgstr "Electrotechnik (B4)" #: apps/member/models.py:67 msgid "Design (C)" -msgstr "" +msgstr "Design (C)" #: apps/member/models.py:68 msgid "Economy-management (D2)" -msgstr "" +msgstr "Wirtschaftingenieure (D2)" #: apps/member/models.py:69 msgid "Social sciences (D3)" -msgstr "" +msgstr "Sozialwissenschaften (D3)" #: apps/member/models.py:70 msgid "English (E)" -msgstr "" +msgstr "English (E)" #: apps/member/models.py:71 msgid "External (EXT)" -msgstr "" +msgstr "Extern (EXT)" #: apps/member/models.py:78 msgid "promotion" -msgstr "" +msgstr "Promotion" #: apps/member/models.py:79 msgid "Year of entry to the school (None if not ENS student)" -msgstr "" +msgstr "ENS Eintrittjahr (None wenn kein ENS Student)" #: apps/member/models.py:83 #: apps/member/templates/member/includes/profile_info.html:38 #: apps/registration/templates/registration/future_profile_detail.html:37 #: apps/wei/templates/wei/weimembership_form.html:41 msgid "address" -msgstr "" +msgstr "Adresse" #: apps/member/models.py:90 #: apps/member/templates/member/includes/profile_info.html:45 #: apps/registration/templates/registration/future_profile_detail.html:43 #: apps/wei/templates/wei/weimembership_form.html:47 msgid "paid" -msgstr "" +msgstr "bezahlt" #: apps/member/models.py:91 msgid "Tells if the user receive a salary." -msgstr "" +msgstr "Ist der User bezahlt ?" #: apps/member/models.py:101 apps/treasury/tables.py:146 +#: env/lib/python3.8/site-packages/django/forms/widgets.py:711 msgid "No" -msgstr "" +msgstr "Nein" #: apps/member/models.py:102 msgid "Yes (receive them in french)" -msgstr "" +msgstr "Ja (auf Fränzosich)" #: apps/member/models.py:103 msgid "Yes (receive them in english)" -msgstr "" +msgstr "Ja (auf English)" #: apps/member/models.py:105 msgid "" "Register on the mailing list to stay informed of the events of the campus (1 " "mail/week)" msgstr "" +"melden Sie sich auf der Mailingliste an, um über die Ereignisse des Campus " +"informiert zu bleiben (1 Mail / Woche)" #: apps/member/models.py:110 msgid "" "Register on the mailing list to stay informed of the sport events of the " "campus (1 mail/week)" msgstr "" +"Melden Sie sich auf der Mailingliste an, um über die Sportereignisse des " +"Campus informiert zu bleiben (1 Mail / Woche)" #: apps/member/models.py:115 msgid "" "Register on the mailing list to stay informed of the art events of the " "campus (1 mail/week)" msgstr "" +"Melden Sie sich auf der Mailingliste an, um über die Kunstereignisse des " +"Campus informiert zu bleiben (1 Mail / Woche)" #: apps/member/models.py:119 msgid "report frequency (in days)" -msgstr "" +msgstr "Bericht Frequenz (Tagen)" #: apps/member/models.py:124 msgid "last report date" -msgstr "" +msgstr "letzen Bericht Datum" #: apps/member/models.py:129 msgid "email confirmed" -msgstr "" +msgstr "email bestätigt" #: apps/member/models.py:134 msgid "registration valid" -msgstr "" +msgstr "Anmeldung gültig" #: apps/member/models.py:163 apps/member/models.py:164 msgid "user profile" -msgstr "" +msgstr "Userprofile" #: apps/member/models.py:174 msgid "Activate your Note Kfet account" -msgstr "" +msgstr "Ihre Note Kfet Konto bestätigen" #: apps/member/models.py:203 #: apps/member/templates/member/includes/club_info.html:55 @@ -716,15 +726,15 @@ msgstr "" #: apps/wei/templates/wei/base.html:70 #: apps/wei/templates/wei/weimembership_form.html:20 msgid "email" -msgstr "" +msgstr "Email" #: apps/member/models.py:210 msgid "parent club" -msgstr "" +msgstr "Urclub" #: apps/member/models.py:219 msgid "require memberships" -msgstr "" +msgstr "erfordern Mitgliedschaft" #: apps/member/models.py:220 msgid "Uncheck if this club don't require memberships." @@ -733,76 +743,76 @@ msgstr "" #: apps/member/models.py:236 #: apps/member/templates/member/includes/club_info.html:26 msgid "membership duration" -msgstr "" +msgstr "Mitgliedscahftzeit" #: apps/member/models.py:237 msgid "The longest time (in days) a membership can last (NULL = infinite)." -msgstr "" +msgstr "Wie lang am höchsten eine Mitgliedschaft dauern kann." #: apps/member/models.py:244 #: apps/member/templates/member/includes/club_info.html:16 msgid "membership start" -msgstr "" +msgstr "Mitgliedschaftanfangsdatum" #: apps/member/models.py:245 msgid "Date from which the members can renew their membership." -msgstr "" +msgstr "Ab wann kann man sein Mitgliedschaft erneuern." #: apps/member/models.py:251 #: apps/member/templates/member/includes/club_info.html:21 msgid "membership end" -msgstr "" +msgstr "Mitgliedschaftenddatum" #: apps/member/models.py:252 msgid "Maximal date of a membership, after which members must renew it." msgstr "" #: apps/member/models.py:284 apps/member/models.py:309 -#: apps/note/models/notes.py:183 +#: apps/note/models/notes.py:179 msgid "club" -msgstr "" +msgstr "Club" #: apps/member/models.py:285 msgid "clubs" -msgstr "" +msgstr "Clubs" #: apps/member/models.py:319 msgid "membership starts on" -msgstr "" +msgstr "Mitgliedschaft fängt an " #: apps/member/models.py:323 msgid "membership ends on" -msgstr "" +msgstr "Mitgliedschaft endet am" #: apps/member/models.py:374 #, python-brace-format msgid "The role {role} does not apply to the club {club}." -msgstr "" +msgstr "Die Rolle {role} ist nicht erlaubt für das Club {club}." #: apps/member/models.py:385 apps/member/views.py:676 msgid "User is already a member of the club" -msgstr "" +msgstr "User ist schon ein Mitglied dieser club." #: apps/member/models.py:433 msgid "User is not a member of the parent club" -msgstr "" +msgstr "User ist noch nicht Mitglied des Urclubs." #: apps/member/models.py:486 #, python-brace-format msgid "Membership of {user} for the club {club}" -msgstr "" +msgstr "Mitgliedschaft von {user} für das Club {club}" #: apps/member/models.py:489 msgid "membership" -msgstr "" +msgstr "Mitgliedschaft" #: apps/member/models.py:490 msgid "memberships" -msgstr "" +msgstr "Mitgliedschaften" #: apps/member/tables.py:121 msgid "Renew" -msgstr "" +msgstr "Erneuern" #: apps/member/templates/member/add_members.html:16 #, python-format @@ -811,6 +821,9 @@ msgid "" "%(pretty_fee)s will be charged to renew automatically the membership in this/" "these club·s." msgstr "" +"Dieser User ist noch nicht Mitglied von den Urclub %(clubs)s. Ein extra " +"Beitrag von %(pretty_fee)s wurde bezahlt um die Mitgliedschaft von dieser/" +"diesen Club zu erneuern." #: apps/member/templates/member/add_members.html:21 #, python-format @@ -818,431 +831,455 @@ msgid "" "This club has parents %(clubs)s. An additional fee of %(pretty_fee)s will be " "charged to adhere automatically to this/these club·s." msgstr "" +"Dieses Club hat %(clubs)s als Urclub. Eine extra Beitrag von %(pretty_fee)s " +"wurde bezahlt um Mitglied von dieser/diesen Club zu werden." #: apps/member/templates/member/base.html:17 #: apps/registration/templates/registration/future_profile_detail.html:12 msgid "Account #" -msgstr "" +msgstr "Konto #" #: apps/member/templates/member/base.html:48 #: apps/member/templates/member/base.html:62 apps/member/views.py:59 #: apps/registration/templates/registration/future_profile_detail.html:48 #: apps/wei/templates/wei/weimembership_form.html:117 msgid "Update Profile" -msgstr "" +msgstr "Profile bearbeiten" #: apps/member/templates/member/base.html:52 #: apps/member/templates/member/base.html:67 msgid "View Profile" -msgstr "" +msgstr "Profile schauen" #: apps/member/templates/member/base.html:57 msgid "Add member" -msgstr "" +msgstr "Neue Mitglied" #: apps/member/templates/member/base.html:72 #: apps/member/templates/member/base.html:93 #: apps/member/templates/member/base.html:114 msgid "Lock note" -msgstr "" +msgstr "Note zuschliessen" #: apps/member/templates/member/base.html:76 #: apps/member/templates/member/base.html:126 #: apps/member/templates/member/base.html:138 msgid "Unlock note" -msgstr "" +msgstr "Note aufschliessen" #: apps/member/templates/member/base.html:99 msgid "" "Are you sure you want to lock this note? This will prevent any transaction " "that would be performed, until the note is unlocked." msgstr "" +"Seit Ihr sicher diese Note zuschliessen ? Keine Transaktion mit dieser Note " +"wurde erlaubt bis der Aufschluss." #: apps/member/templates/member/base.html:104 msgid "" "If you use the force mode, the user won't be able to unlock the note by " "itself." msgstr "" +"Wenn sie das stark mode benutzen, Der User wurde nicht erlaubt sich selbzt " +"zu aufschliessen." #: apps/member/templates/member/base.html:110 #: apps/member/templates/member/base.html:137 apps/treasury/forms.py:95 msgid "Close" -msgstr "" +msgstr "Schluss" #: apps/member/templates/member/base.html:112 msgid "Force mode" -msgstr "" +msgstr "Stark mode" #: apps/member/templates/member/base.html:132 msgid "" "Are you sure you want to unlock this note? Transactions will be re-enabled." msgstr "" +"Seit ihr sicher diese Note aufzuschliessen ? Transaktionen wurden wieder " +"erlaubt." #: apps/member/templates/member/club_alias.html:10 #: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:240 #: apps/member/views.py:450 msgid "Note aliases" -msgstr "" +msgstr "Note Aliases" #: apps/member/templates/member/club_alias.html:20 #: apps/member/templates/member/profile_alias.html:19 #: apps/treasury/tables.py:99 msgid "Add" -msgstr "" +msgstr "Neue" #: apps/member/templates/member/club_detail.html:13 #: apps/permission/templates/permission/all_rights.html:32 msgid "Club managers" -msgstr "" +msgstr "Clubmanager" #: apps/member/templates/member/club_detail.html:26 msgid "Club members" -msgstr "" +msgstr "Mitglied" #: apps/member/templates/member/club_detail.html:40 #: apps/member/templates/member/profile_detail.html:32 #: apps/wei/templates/wei/weiclub_detail.html:75 msgid "Transaction history" -msgstr "" +msgstr "Transaktionvergangenheit" #: apps/member/templates/member/club_list.html:10 msgid "Create club" -msgstr "" +msgstr "Neue Club" #: apps/member/templates/member/club_members.html:19 msgid "Display only active memberships" -msgstr "" +msgstr "Schau nur aktive Mitgliedschaften" #: apps/member/templates/member/club_members.html:23 msgid "Filter roles:" -msgstr "" +msgstr "Rollen filtern" #: apps/member/templates/member/club_members.html:36 #: apps/wei/templates/wei/weimembership_list.html:17 msgid "There is no membership found with this pattern." -msgstr "" +msgstr "Keine Mitgliedschaft mit diesem pattern gefunden." #: apps/member/templates/member/includes/club_info.html:9 msgid "Club Parent" -msgstr "" +msgstr "Urclub" #: apps/member/templates/member/includes/club_info.html:27 msgid "days" -msgstr "" +msgstr "Tagen" #: apps/member/templates/member/includes/club_info.html:31 #: apps/wei/templates/wei/base.html:40 msgid "membership fee" -msgstr "" +msgstr "Mitgliedsachftpreis" #: apps/member/templates/member/includes/club_info.html:43 #: apps/member/templates/member/includes/profile_info.html:42 #: apps/treasury/templates/treasury/sogecredit_detail.html:18 #: apps/wei/templates/wei/base.html:60 msgid "balance" -msgstr "" +msgstr "Kontostand" #: apps/member/templates/member/includes/club_info.html:47 #: apps/member/templates/member/includes/profile_info.html:20 -#: apps/note/models/notes.py:273 apps/wei/templates/wei/base.html:66 +#: apps/note/models/notes.py:270 apps/wei/templates/wei/base.html:66 msgid "aliases" -msgstr "" +msgstr "Aliases" #: apps/member/templates/member/includes/club_info.html:51 #: apps/member/templates/member/includes/profile_info.html:24 msgid "Manage aliases" -msgstr "" +msgstr "Aliases bearbeiten" #: apps/member/templates/member/includes/profile_info.html:7 #: apps/registration/templates/registration/future_profile_detail.html:19 #: apps/wei/templates/wei/weimembership_form.html:17 msgid "username" -msgstr "" +msgstr "Username" #: apps/member/templates/member/includes/profile_info.html:11 msgid "password" -msgstr "" +msgstr "Kennwort" #: apps/member/templates/member/includes/profile_info.html:15 msgid "Change password" -msgstr "" +msgstr "Kennword ändern" -#: apps/member/templates/member/includes/profile_info.html:52 -#: apps/member/views.py:318 -msgid "Manage auth token" -msgstr "" +#: apps/member/templates/member/includes/profile_info.html:53 +msgid "API token" +msgstr "API token" #: apps/member/templates/member/manage_auth_tokens.html:19 msgid "Token" -msgstr "" +msgstr "Token" #: apps/member/templates/member/manage_auth_tokens.html:26 msgid "Created" -msgstr "" +msgstr "Neue" #: apps/member/templates/member/manage_auth_tokens.html:34 msgid "Regenerate token" -msgstr "" +msgstr "Token erneuern" #: apps/member/templates/member/profile_detail.html:11 #: apps/registration/templates/registration/future_profile_detail.html:28 #: apps/wei/templates/wei/weimembership_form.html:26 msgid "This user doesn't have confirmed his/her e-mail address." -msgstr "" +msgstr "Dieser User hat noch nicht sein email bestätigt." #: apps/member/templates/member/profile_detail.html:13 #: apps/registration/templates/registration/future_profile_detail.html:29 #: apps/wei/templates/wei/weimembership_form.html:27 msgid "Click here to resend a validation link." -msgstr "" +msgstr "Click hier um eine Bestätigunglinke zu schicken" #: apps/member/templates/member/profile_detail.html:21 msgid "View my memberships" -msgstr "" +msgstr "Meine Mitgliedschaften schauen" #: apps/member/templates/member/profile_update.html:18 msgid "Save Changes" -msgstr "" +msgstr "Speichern" #: apps/member/templates/member/user_list.html:10 msgid "Registrations" -msgstr "" +msgstr "Anmeldung" #: apps/member/views.py:72 apps/registration/forms.py:23 msgid "This address must be valid." -msgstr "" +msgstr "Diese Adresse muss gültig sein." #: apps/member/views.py:138 msgid "Profile detail" -msgstr "" +msgstr "Profile detail" #: apps/member/views.py:201 msgid "Search user" -msgstr "" +msgstr "User finden" #: apps/member/views.py:260 msgid "Update note picture" -msgstr "" +msgstr "Notebild ändern" + +#: apps/member/views.py:318 +msgid "Manage auth token" +msgstr "auth token bearbeiten" #: apps/member/views.py:346 msgid "Create new club" -msgstr "" +msgstr "Neue Club" #: apps/member/views.py:364 msgid "Search club" -msgstr "" +msgstr "Club finden" #: apps/member/views.py:397 msgid "Club detail" -msgstr "" +msgstr "Club Details" #: apps/member/views.py:473 msgid "Update club" -msgstr "" +msgstr "Club bearbeiten" #: apps/member/views.py:507 msgid "Add new member to the club" -msgstr "" +msgstr "Neue Mitglieder " #: apps/member/views.py:667 apps/wei/views.py:922 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." msgstr "" +"Diese User hat nicht genug Geld um Mitglied zu werden, und darf nich im Rot " +"sein." #: apps/member/views.py:680 msgid "The membership must start after {:%m-%d-%Y}." -msgstr "" +msgstr "Die Mitgliedschaft muss nach {:%m-%d-Y} anfängen." #: apps/member/views.py:685 msgid "The membership must begin before {:%m-%d-%Y}." -msgstr "" +msgstr "Die Mitgliedschaft muss vor {:%m-%d-Y} anfängen." #: apps/member/views.py:701 apps/member/views.py:703 apps/member/views.py:705 #: apps/registration/views.py:292 apps/registration/views.py:294 #: apps/registration/views.py:296 apps/wei/views.py:927 apps/wei/views.py:931 +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_form_helper.py:140 +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_form_helper.py:150 +#: env/lib/python3.8/site-packages/django/forms/fields.py:53 msgid "This field is required." -msgstr "" +msgstr "Dies ist ein Pflichtfeld." #: apps/member/views.py:789 msgid "Manage roles of an user in the club" -msgstr "" +msgstr "Rollen in diesen Club bearbeiten" #: apps/member/views.py:814 msgid "Members of the club" -msgstr "" +msgstr "Mitlglieder dieses Club" -#: apps/note/admin.py:133 apps/note/models/transactions.py:106 +#: apps/note/admin.py:129 apps/note/models/transactions.py:106 msgid "source" -msgstr "" +msgstr "Sender" -#: apps/note/admin.py:141 apps/note/admin.py:191 +#: apps/note/admin.py:137 apps/note/admin.py:205 #: apps/note/models/transactions.py:56 apps/note/models/transactions.py:119 msgid "destination" -msgstr "" +msgstr "Empfänger" -#: apps/note/admin.py:196 apps/note/models/transactions.py:60 +#: apps/note/admin.py:210 apps/note/models/transactions.py:60 #: apps/note/models/transactions.py:137 msgid "amount" -msgstr "" +msgstr "Anzahl" -#: apps/note/api/serializers.py:177 apps/note/api/serializers.py:183 +#: apps/note/api/serializers.py:178 apps/note/api/serializers.py:184 #: apps/note/models/transactions.py:224 msgid "" "The transaction can't be saved since the source note or the destination note " "is not active." msgstr "" +"Diese Transaktion ist nicht möglich weil die Note des Sender oder des " +"Empfänger inaktiv ist. " -#: apps/note/forms.py:37 +#: apps/note/forms.py:39 msgid "Source" -msgstr "" +msgstr "Sender" -#: apps/note/forms.py:51 +#: apps/note/forms.py:53 msgid "Destination" -msgstr "" +msgstr "Empfänger" -#: apps/note/forms.py:72 apps/note/templates/note/transaction_form.html:119 +#: apps/note/forms.py:74 apps/note/templates/note/transaction_form.html:119 msgid "Reason" -msgstr "" +msgstr "Grund" -#: apps/note/forms.py:77 apps/treasury/tables.py:139 +#: apps/note/forms.py:79 apps/treasury/tables.py:139 msgid "Valid" -msgstr "" +msgstr "Gültig" -#: apps/note/forms.py:83 +#: apps/note/forms.py:85 msgid "Total amount greater than" -msgstr "" +msgstr "Totalanzahl größer als" -#: apps/note/forms.py:91 +#: apps/note/forms.py:93 msgid "Total amount less than" -msgstr "" +msgstr "Totalanzahl kleiner als " -#: apps/note/forms.py:97 +#: apps/note/forms.py:99 msgid "Created after" -msgstr "" +msgstr "Erschafft nacht" -#: apps/note/forms.py:104 +#: apps/note/forms.py:106 msgid "Created before" -msgstr "" +msgstr "Erschafft vor" -#: apps/note/models/notes.py:30 +#: apps/note/models/notes.py:32 msgid "account balance" -msgstr "" +msgstr "Kontostand " -#: apps/note/models/notes.py:31 +#: apps/note/models/notes.py:33 msgid "in centimes, money credited for this instance" -msgstr "" +msgstr "In Cent, der Anzahl Geld für diese Konto" -#: apps/note/models/notes.py:36 +#: apps/note/models/notes.py:38 msgid "last negative date" -msgstr "" +msgstr "letztes mal im Rot" -#: apps/note/models/notes.py:37 +#: apps/note/models/notes.py:39 msgid "last time the balance was negative" -msgstr "" +msgstr "letztes mal im Rot" -#: apps/note/models/notes.py:43 +#: apps/note/models/notes.py:45 msgid "display image" -msgstr "" +msgstr "Bild" -#: apps/note/models/notes.py:52 apps/note/models/transactions.py:129 +#: apps/note/models/notes.py:54 apps/note/models/transactions.py:129 msgid "created at" -msgstr "" +msgstr "erschafft am" -#: apps/note/models/notes.py:57 +#: apps/note/models/notes.py:59 msgid "active" -msgstr "" +msgstr "Aktiv" -#: apps/note/models/notes.py:60 +#: apps/note/models/notes.py:62 msgid "" "Designates whether this note should be treated as active. Unselect this " "instead of deleting notes." msgstr "" +"Designates whether this note should be treated as active. Unselect this " +"instead of deleting notes." -#: apps/note/models/notes.py:67 +#: apps/note/models/notes.py:69 msgid "" "The user blocked his/her note manually, eg. when he/she left the school for " "holidays. It can be reactivated at any time." msgstr "" +"The user blocked his/her note manually, eg. when he/she left the school for " +"holidays. It can be reactivated at any time." -#: apps/note/models/notes.py:69 +#: apps/note/models/notes.py:71 msgid "The note is blocked by the the BDE and can't be manually reactivated." -msgstr "" +msgstr "The note is blocked by the the BDE and can't be manually reactivated." -#: apps/note/models/notes.py:77 +#: apps/note/models/notes.py:79 msgid "notes" -msgstr "" +msgstr "Notes" -#: apps/note/models/notes.py:102 apps/note/models/notes.py:126 +#: apps/note/models/notes.py:122 msgid "This alias is already taken." -msgstr "" +msgstr "Dieses Alias ist schon benutzt." -#: apps/note/models/notes.py:146 +#: apps/note/models/notes.py:142 msgid "one's note" -msgstr "" +msgstr "Jemand Note" -#: apps/note/models/notes.py:147 +#: apps/note/models/notes.py:143 msgid "users note" -msgstr "" +msgstr "User Note" -#: apps/note/models/notes.py:153 +#: apps/note/models/notes.py:149 #, python-format msgid "%(user)s's note" -msgstr "" +msgstr "%(user)s's note" -#: apps/note/models/notes.py:187 +#: apps/note/models/notes.py:183 msgid "club note" -msgstr "" +msgstr "Club Note" -#: apps/note/models/notes.py:188 +#: apps/note/models/notes.py:184 msgid "clubs notes" -msgstr "" +msgstr "Club Notes" -#: apps/note/models/notes.py:194 +#: apps/note/models/notes.py:190 #, python-format msgid "Note of %(club)s club" -msgstr "" +msgstr "%(club)s Note" -#: apps/note/models/notes.py:232 +#: apps/note/models/notes.py:229 msgid "special note" -msgstr "" +msgstr "Sondernote" -#: apps/note/models/notes.py:233 +#: apps/note/models/notes.py:230 msgid "special notes" -msgstr "" +msgstr "Sondernoten" -#: apps/note/models/notes.py:256 +#: apps/note/models/notes.py:253 msgid "Invalid alias" -msgstr "" +msgstr "Unerlaublt Alias" -#: apps/note/models/notes.py:272 +#: apps/note/models/notes.py:269 msgid "alias" -msgstr "" +msgstr "Alias" + +#: apps/note/models/notes.py:293 +msgid "Alias is too long." +msgstr "Alias ist zu lang" #: apps/note/models/notes.py:296 -msgid "Alias is too long." -msgstr "" - -#: apps/note/models/notes.py:299 msgid "" "This alias contains only complex character. Please use a more simple alias." msgstr "" +"Dieser Alias enthält nur komplexe Zeichen. Bitte verwenden Sie einen " +"einfacheren Alias." -#: apps/note/models/notes.py:303 +#: apps/note/models/notes.py:300 msgid "An alias with a similar name already exists: {} " -msgstr "" +msgstr "Ein Alias mit einem ähnlichen Namen existiert bereits: {}" -#: apps/note/models/notes.py:316 +#: apps/note/models/notes.py:313 msgid "You can't delete your main alias." -msgstr "" +msgstr "Sie können Ihren Hauptalias nicht löschen." #: apps/note/models/transactions.py:32 msgid "transaction category" -msgstr "" +msgstr "Transaktionkategorie" #: apps/note/models/transactions.py:33 msgid "transaction categories" -msgstr "" +msgstr "Transaktionkategorien" #: apps/note/models/transactions.py:49 msgid "A template with this name already exist" @@ -1265,7 +1302,7 @@ msgid "transaction templates" msgstr "" #: apps/note/models/transactions.py:112 apps/note/models/transactions.py:125 -#: apps/note/tables.py:34 apps/note/tables.py:43 +#: apps/note/tables.py:33 apps/note/tables.py:42 msgid "used alias" msgstr "" @@ -1277,7 +1314,7 @@ msgstr "" msgid "reason" msgstr "" -#: apps/note/models/transactions.py:151 apps/note/tables.py:114 +#: apps/note/models/transactions.py:151 apps/note/tables.py:107 msgid "invalidity reason" msgstr "" @@ -1303,69 +1340,76 @@ msgid "" "720 368 547 758.07 €." msgstr "" -#: apps/note/models/transactions.py:283 +#: apps/note/models/transactions.py:279 +msgid "" +"The destination of this transaction must equal to the destination of the " +"template." +msgstr "" + +#: apps/note/models/transactions.py:288 msgid "Template" msgstr "" -#: apps/note/models/transactions.py:286 +#: apps/note/models/transactions.py:291 msgid "recurrent transaction" msgstr "" -#: apps/note/models/transactions.py:287 +#: apps/note/models/transactions.py:292 msgid "recurrent transactions" msgstr "" -#: apps/note/models/transactions.py:302 +#: apps/note/models/transactions.py:307 msgid "first_name" msgstr "" -#: apps/note/models/transactions.py:307 +#: apps/note/models/transactions.py:312 msgid "bank" msgstr "" -#: apps/note/models/transactions.py:324 +#: apps/note/models/transactions.py:329 msgid "" "A special transaction is only possible between a Note associated to a " "payment method and a User or a Club" msgstr "" -#: apps/note/models/transactions.py:328 +#: apps/note/models/transactions.py:337 msgid "Special transaction" msgstr "" -#: apps/note/models/transactions.py:329 +#: apps/note/models/transactions.py:338 msgid "Special transactions" msgstr "" -#: apps/note/models/transactions.py:345 apps/note/models/transactions.py:350 +#: apps/note/models/transactions.py:354 apps/note/models/transactions.py:359 msgid "membership transaction" msgstr "" -#: apps/note/models/transactions.py:346 apps/treasury/models.py:273 +#: apps/note/models/transactions.py:355 apps/treasury/models.py:273 msgid "membership transactions" msgstr "" -#: apps/note/tables.py:62 +#: apps/note/tables.py:61 msgid "Click to invalidate" msgstr "" -#: apps/note/tables.py:62 +#: apps/note/tables.py:61 msgid "Click to validate" msgstr "" -#: apps/note/tables.py:112 +#: apps/note/tables.py:105 msgid "No reason specified" msgstr "" -#: apps/note/tables.py:143 apps/note/tables.py:177 apps/treasury/tables.py:39 +#: apps/note/tables.py:136 apps/note/tables.py:170 apps/treasury/tables.py:39 #: apps/treasury/templates/treasury/invoice_confirm_delete.html:30 #: apps/treasury/templates/treasury/sogecredit_detail.html:59 #: apps/wei/tables.py:76 apps/wei/tables.py:103 #: apps/wei/templates/wei/weiregistration_confirm_delete.html:31 +#: env/lib/python3.8/site-packages/django/forms/formsets.py:375 msgid "Delete" msgstr "" -#: apps/note/tables.py:171 apps/note/templates/note/conso_form.html:132 +#: apps/note/tables.py:164 apps/note/templates/note/conso_form.html:132 #: apps/wei/tables.py:47 apps/wei/tables.py:48 #: apps/wei/templates/wei/base.html:89 #: apps/wei/templates/wei/bus_detail.html:20 @@ -1461,19 +1505,19 @@ msgstr "" msgid "Transfer type" msgstr "" -#: apps/note/templates/note/transactiontemplate_form.html:10 +#: apps/note/templates/note/transactiontemplate_form.html:8 msgid "Buttons list" msgstr "" -#: apps/note/templates/note/transactiontemplate_form.html:21 +#: apps/note/templates/note/transactiontemplate_form.html:24 msgid "Price history" msgstr "" -#: apps/note/templates/note/transactiontemplate_form.html:24 +#: apps/note/templates/note/transactiontemplate_form.html:27 msgid "Obsolete since" msgstr "" -#: apps/note/templates/note/transactiontemplate_form.html:24 +#: apps/note/templates/note/transactiontemplate_form.html:27 msgid "Current price" msgstr "" @@ -1513,7 +1557,7 @@ msgstr "" msgid "Update button" msgstr "" -#: apps/note/views.py:151 note_kfet/templates/base.html:72 +#: apps/note/views.py:151 note_kfet/templates/base.html:62 msgid "Consumptions" msgstr "" @@ -1677,7 +1721,7 @@ msgid "" "with these parameters. Please correct your data and retry." msgstr "" -#: apps/permission/views.py:96 note_kfet/templates/base.html:114 +#: apps/permission/views.py:96 note_kfet/templates/base.html:104 msgid "Rights" msgstr "" @@ -1855,7 +1899,7 @@ msgstr "" msgid "Invalidate pre-registration" msgstr "" -#: apps/treasury/apps.py:12 note_kfet/templates/base.html:102 +#: apps/treasury/apps.py:12 note_kfet/templates/base.html:92 msgid "Treasury" msgstr "" @@ -2027,6 +2071,7 @@ msgid "View" msgstr "" #: apps/treasury/tables.py:146 +#: env/lib/python3.8/site-packages/django/forms/widgets.py:710 msgid "Yes" msgstr "" @@ -2224,7 +2269,7 @@ msgstr "" #: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50 #: apps/wei/models.py:61 apps/wei/models.py:167 -#: note_kfet/templates/base.html:108 +#: note_kfet/templates/base.html:98 msgid "WEI" msgstr "" @@ -2734,6 +2779,1178 @@ msgstr "" msgid "This user didn't give her/his caution check." msgstr "" +#: env/lib/python3.8/site-packages/colorfield/fields.py:17 +msgid "Enter a valid color." +msgstr "" + +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout.py:355 +msgid "i18n text" +msgstr "" + +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout.py:357 +msgid "i18n legend" +msgstr "" + +#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout_objects.py:127 +#: env/lib/python3.8/site-packages/django/core/validators.py:31 +msgid "Enter a valid value." +msgstr "" + +#: env/lib/python3.8/site-packages/django/contrib/messages/apps.py:7 +msgid "Messages" +msgstr "" + +#: env/lib/python3.8/site-packages/django/contrib/sitemaps/apps.py:7 +msgid "Site Maps" +msgstr "" + +#: env/lib/python3.8/site-packages/django/contrib/staticfiles/apps.py:9 +msgid "Static Files" +msgstr "" + +#: env/lib/python3.8/site-packages/django/contrib/syndication/apps.py:7 +msgid "Syndication" +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/paginator.py:45 +msgid "That page number is not an integer" +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/paginator.py:47 +msgid "That page number is less than 1" +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/paginator.py:52 +msgid "That page contains no results" +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:102 +#: env/lib/python3.8/site-packages/django/forms/fields.py:658 +msgid "Enter a valid URL." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:154 +msgid "Enter a valid integer." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:165 +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +#: env/lib/python3.8/site-packages/django/core/validators.py:239 +msgid "" +"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:246 +msgid "" +"Enter a valid 'slug' consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:255 +#: env/lib/python3.8/site-packages/django/core/validators.py:275 +msgid "Enter a valid IPv4 address." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:260 +#: env/lib/python3.8/site-packages/django/core/validators.py:276 +msgid "Enter a valid IPv6 address." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:270 +#: env/lib/python3.8/site-packages/django/core/validators.py:274 +msgid "Enter a valid IPv4 or IPv6 address." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:304 +msgid "Enter only digits separated by commas." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:310 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:342 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:351 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:361 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:376 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:395 +#: env/lib/python3.8/site-packages/django/forms/fields.py:290 +#: env/lib/python3.8/site-packages/django/forms/fields.py:325 +msgid "Enter a number." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:397 +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:402 +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:407 +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:469 +#, python-format +msgid "" +"File extension '%(extension)s' is not allowed. Allowed extensions are: " +"'%(allowed_extensions)s'." +msgstr "" + +#: env/lib/python3.8/site-packages/django/core/validators.py:521 +msgid "Null characters are not allowed." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/base.py:1165 +#: env/lib/python3.8/site-packages/django/forms/models.py:756 +msgid "and" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/base.py:1167 +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:104 +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:105 +msgid "This field cannot be null." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:106 +msgid "This field cannot be blank." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:107 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. +#. Eg: "Title must be unique for pub_date year" +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:111 +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:128 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:905 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1772 +msgid "Integer" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:909 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1770 +#, python-format +msgid "'%(value)s' value must be an integer." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:984 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1850 +msgid "Big (8 byte) integer" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:996 +#, python-format +msgid "'%(value)s' value must be either True or False." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:997 +#, python-format +msgid "'%(value)s' value must be either True, False, or None." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:999 +msgid "Boolean (Either True or False)" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1040 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1104 +msgid "Comma-separated integers" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1153 +#, python-format +msgid "" +"'%(value)s' value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1155 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1298 +#, python-format +msgid "" +"'%(value)s' value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1158 +msgid "Date (without time)" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1296 +#, python-format +msgid "" +"'%(value)s' value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." +"uuuuuu]][TZ] format." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1300 +#, python-format +msgid "" +"'%(value)s' value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1304 +msgid "Date (with time)" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1452 +#, python-format +msgid "'%(value)s' value must be a decimal number." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1454 +msgid "Decimal number" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1593 +#, python-format +msgid "" +"'%(value)s' value has an invalid format. It must be in [DD] [HH:[MM:]]ss[." +"uuuuuu] format." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1596 +msgid "Duration" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1646 +msgid "Email address" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1669 +msgid "File path" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1735 +#, python-format +msgid "'%(value)s' value must be a float." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1737 +msgid "Floating point number" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1866 +msgid "IPv4 address" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1897 +msgid "IP address" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1977 +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1978 +#, python-format +msgid "'%(value)s' value must be either None, True or False." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1980 +msgid "Boolean (Either True, False or None)" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2015 +msgid "Positive integer" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2028 +msgid "Positive small integer" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2042 +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2074 +msgid "Small integer" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2081 +msgid "Text" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2109 +#, python-format +msgid "" +"'%(value)s' value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2111 +#, python-format +msgid "" +"'%(value)s' value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2114 +msgid "Time" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2240 +msgid "URL" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2262 +msgid "Raw binary data" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2312 +#, python-format +msgid "'%(value)s' is not a valid UUID." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2314 +msgid "Universally unique identifier" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/files.py:221 +msgid "File" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/files.py:360 +msgid "Image" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:778 +#, python-format +msgid "%(model)s instance with %(field)s %(value)r does not exist." +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:780 +msgid "Foreign Key (type determined by related field)" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1007 +msgid "One-to-one relationship" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1057 +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1058 +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1100 +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the label +#: env/lib/python3.8/site-packages/django/forms/boundfield.py:146 +msgid ":?.!" +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:245 +msgid "Enter a whole number." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:396 +#: env/lib/python3.8/site-packages/django/forms/fields.py:1126 +msgid "Enter a valid date." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:420 +#: env/lib/python3.8/site-packages/django/forms/fields.py:1127 +msgid "Enter a valid time." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:442 +msgid "Enter a valid date/time." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:471 +msgid "Enter a valid duration." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:472 +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:532 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:533 +msgid "No file was submitted." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:534 +msgid "The submitted file is empty." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:536 +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:539 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:600 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:762 +#: env/lib/python3.8/site-packages/django/forms/fields.py:852 +#: env/lib/python3.8/site-packages/django/forms/models.py:1270 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:853 +#: env/lib/python3.8/site-packages/django/forms/fields.py:968 +#: env/lib/python3.8/site-packages/django/forms/models.py:1269 +msgid "Enter a list of values." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:969 +msgid "Enter a complete value." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/fields.py:1185 +msgid "Enter a valid UUID." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +#: env/lib/python3.8/site-packages/django/forms/forms.py:86 +msgid ":" +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/forms.py:212 +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/formsets.py:91 +msgid "ManagementForm data is missing or has been tampered with" +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/formsets.py:338 +#, python-format +msgid "Please submit %d or fewer forms." +msgid_plural "Please submit %d or fewer forms." +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/forms/formsets.py:345 +#, python-format +msgid "Please submit %d or more forms." +msgid_plural "Please submit %d or more forms." +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/forms/formsets.py:371 +#: env/lib/python3.8/site-packages/django/forms/formsets.py:373 +msgid "Order" +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/models.py:751 +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/models.py:755 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/models.py:761 +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/models.py:770 +msgid "Please correct the duplicate values below." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/models.py:1091 +msgid "The inline value did not match the parent instance." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/models.py:1158 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/models.py:1272 +#, python-format +msgid "\"%(pk)s\" is not a valid value." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/utils.py:162 +#, python-format +msgid "" +"%(datetime)s couldn't be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/widgets.py:395 +msgid "Clear" +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/widgets.py:396 +msgid "Currently" +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/widgets.py:397 +msgid "Change" +msgstr "" + +#: env/lib/python3.8/site-packages/django/forms/widgets.py:709 +msgid "Unknown" +msgstr "" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:788 +msgid "yes,no,maybe" +msgstr "" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:817 +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:834 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:836 +#, python-format +msgid "%s KB" +msgstr "" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:838 +#, python-format +msgid "%s MB" +msgstr "" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:840 +#, python-format +msgid "%s GB" +msgstr "" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:842 +#, python-format +msgid "%s TB" +msgstr "" + +#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:844 +#, python-format +msgid "%s PB" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:62 +msgid "p.m." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:63 +msgid "a.m." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:68 +msgid "PM" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:69 +msgid "AM" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:150 +msgid "midnight" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dateformat.py:152 +msgid "noon" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Monday" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Tuesday" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Wednesday" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Thursday" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:6 +msgid "Friday" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:7 +msgid "Saturday" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:7 +msgid "Sunday" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Mon" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Tue" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Wed" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Thu" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:10 +msgid "Fri" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:11 +msgid "Sat" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:11 +msgid "Sun" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "January" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "February" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "March" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "April" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "May" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:14 +msgid "June" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "July" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "August" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "September" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "October" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:15 +msgid "November" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:16 +msgid "December" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "jan" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "feb" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "mar" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "apr" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "may" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:19 +msgid "jun" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "jul" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "aug" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "sep" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "oct" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "nov" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:20 +msgid "dec" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:23 +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:24 +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:25 +msgctxt "abbrev. month" +msgid "March" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:26 +msgctxt "abbrev. month" +msgid "April" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:27 +msgctxt "abbrev. month" +msgid "May" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:28 +msgctxt "abbrev. month" +msgid "June" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:29 +msgctxt "abbrev. month" +msgid "July" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:30 +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:31 +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:32 +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:33 +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:34 +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:37 +msgctxt "alt. month" +msgid "January" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:38 +msgctxt "alt. month" +msgid "February" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:39 +msgctxt "alt. month" +msgid "March" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:40 +msgctxt "alt. month" +msgid "April" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:41 +msgctxt "alt. month" +msgid "May" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:42 +msgctxt "alt. month" +msgid "June" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:43 +msgctxt "alt. month" +msgid "July" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:44 +msgctxt "alt. month" +msgid "August" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:45 +msgctxt "alt. month" +msgid "September" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:46 +msgctxt "alt. month" +msgid "October" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:47 +msgctxt "alt. month" +msgid "November" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/dates.py:48 +msgctxt "alt. month" +msgid "December" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/ipv6.py:8 +msgid "This is not a valid IPv6 address." +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/text.py:67 +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/text.py:233 +msgid "or" +msgstr "" + +#. Translators: This string is used as a separator between list elements +#: env/lib/python3.8/site-packages/django/utils/text.py:252 +#: env/lib/python3.8/site-packages/django/utils/timesince.py:83 +msgid ", " +msgstr "" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:9 +#, python-format +msgid "%d year" +msgid_plural "%d years" +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:10 +#, python-format +msgid "%d month" +msgid_plural "%d months" +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:11 +#, python-format +msgid "%d week" +msgid_plural "%d weeks" +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:12 +#, python-format +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:13 +#, python-format +msgid "%d hour" +msgid_plural "%d hours" +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:14 +#, python-format +msgid "%d minute" +msgid_plural "%d minutes" +msgstr[0] "" +msgstr[1] "" + +#: env/lib/python3.8/site-packages/django/utils/timesince.py:72 +msgid "0 minutes" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:110 +msgid "Forbidden" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:111 +msgid "CSRF verification failed. Request aborted." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:115 +msgid "" +"You are seeing this message because this HTTPS site requires a 'Referer " +"header' to be sent by your Web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:120 +msgid "" +"If you have configured your browser to disable 'Referer' headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for 'same-" +"origin' requests." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:124 +msgid "" +"If you are using the tag or " +"including the 'Referrer-Policy: no-referrer' header, please remove them. The " +"CSRF protection requires the 'Referer' header to do strict referer checking. " +"If you're concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:132 +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:137 +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for 'same-origin' requests." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/csrf.py:142 +msgid "More information is available with DEBUG=True." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:41 +msgid "No year specified" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:61 +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:111 +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:208 +msgid "Date out of range" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:90 +msgid "No month specified" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:142 +msgid "No day specified" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:188 +msgid "No week specified" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:338 +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:367 +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:589 +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because %(class_name)s." +"allow_future is False." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/dates.py:623 +#, python-format +msgid "Invalid date string '%(datestr)s' given format '%(format)s'" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/detail.py:54 +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/list.py:67 +msgid "Page is not 'last', nor can it be converted to an int." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/list.py:72 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/generic/list.py:154 +#, python-format +msgid "Empty list and '%(class_name)s.allow_empty' is False." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/static.py:40 +msgid "Directory indexes are not allowed here." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/static.py:42 +#, python-format +msgid "\"%(path)s\" does not exist" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/static.py:80 +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:6 +msgid "Django: the Web framework for perfectionists with deadlines." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:345 +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:367 +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:368 +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " +"URLs." +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:383 +msgid "Django Documentation" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:384 +msgid "Topics, references, & how-to's" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:395 +msgid "Tutorial: A Polling App" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:396 +msgid "Get started with Django" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:407 +msgid "Django Community" +msgstr "" + +#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:408 +msgid "Connect, get help, or contribute" +msgstr "" + #: note_kfet/settings/base.py:155 msgid "German" msgstr "" @@ -2798,19 +4015,19 @@ msgstr "" msgid "The ENS Paris-Saclay BDE note." msgstr "" -#: note_kfet/templates/base.html:84 +#: note_kfet/templates/base.html:74 msgid "Users" msgstr "" -#: note_kfet/templates/base.html:90 +#: note_kfet/templates/base.html:80 msgid "Clubs" msgstr "" -#: note_kfet/templates/base.html:119 +#: note_kfet/templates/base.html:109 msgid "Admin" msgstr "" -#: note_kfet/templates/base.html:163 +#: note_kfet/templates/base.html:153 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." From 7bdf5a4366fe5206420c7a5036215947aa566f14 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Wed, 2 Sep 2020 23:25:32 +0200 Subject: [PATCH 18/41] Update picture path in member test --- apps/member/tests/test_memberships.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/member/tests/test_memberships.py b/apps/member/tests/test_memberships.py index 8ad7b7cb..4852de77 100644 --- a/apps/member/tests/test_memberships.py +++ b/apps/member/tests/test_memberships.py @@ -125,7 +125,7 @@ class TestMemberships(TestCase): old_pic = self.club.note.display_image - with open("media/pic/default.png", "rb") as f: + with open("apps/member/static/member/img/default_picture.png", "rb") as f: image = SimpleUploadedFile("image.png", f.read(), "image/png") response = self.client.post(reverse("member:club_update_pic", args=(self.club.pk,)), dict( image=image, @@ -349,7 +349,7 @@ class TestMemberships(TestCase): old_pic = self.user.note.display_image - with open("media/pic/default.png", "rb") as f: + with open("apps/member/static/member/img/default_picture.png", "rb") as f: image = SimpleUploadedFile("image.png", f.read(), "image/png") response = self.client.post(reverse("member:user_update_pic", args=(self.user.pk,)), dict( image=image, From 177128f59377e87a766cfebbddfee5e865b4e250 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Wed, 2 Sep 2020 23:26:28 +0200 Subject: [PATCH 19/41] do not gather build in translation. --- locale/de/LC_MESSAGES/django.po | 1242 +----------------- locale/fr/LC_MESSAGES/django.po | 2161 +++++++++++++------------------ 2 files changed, 947 insertions(+), 2456 deletions(-) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 0ce8cf0d..63f5a02e 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -7,15 +7,15 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-02 19:18+0200\n" -"PO-Revision-Date: 2020-09-02 23:14+0200\n" +"POT-Creation-Date: 2020-09-02 23:17+0200\n" +"PO-Revision-Date: 2020-09-02 23:26+0200\n" +"Last-Translator: \n" +"Language-Team: \n" "Language: de\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" -"Last-Translator: \n" -"Language-Team: \n" "X-Generator: Poedit 2.3\n" #: apps/activity/apps.py:10 apps/activity/models.py:145 @@ -183,7 +183,7 @@ msgstr "Eintritte" #: apps/activity/models.py:190 msgid "Already entered on " -msgstr "Schon eingetretten" +msgstr "Schon eingetretten " #: apps/activity/models.py:190 apps/activity/tables.py:54 msgid "{:%Y-%m-%d %H:%M:%S}" @@ -191,7 +191,7 @@ msgstr "{:%Y-%m-%d %H:%M:%S}" #: apps/activity/models.py:198 msgid "The balance is negative." -msgstr "Kontostand ist im Rot" +msgstr "Kontostand ist im Rot." #: apps/activity/models.py:228 msgid "last name" @@ -222,19 +222,19 @@ msgstr "Einladung" #: apps/activity/tables.py:25 msgid "The activity is currently open." -msgstr "Die Veranstaltung ist geöffnet" +msgstr "Die Veranstaltung ist geöffnet." #: apps/activity/tables.py:26 msgid "The validation of the activity is pending." -msgstr "Diese Veranstaltung ist noch nicht bestätigt" +msgstr "Diese Veranstaltung ist noch nicht bestätigt." #: apps/activity/tables.py:41 apps/treasury/tables.py:107 msgid "Remove" -msgstr "entfernen" +msgstr "Entfernen" #: apps/activity/tables.py:54 msgid "Entered on " -msgstr "Eingetreten um" +msgstr "Eingetreten um " #: apps/activity/tables.py:56 msgid "remove" @@ -309,11 +309,11 @@ msgstr "Zurück zur Veranstaltungseite" #: apps/wei/templates/wei/weiclub_form.html:17 #: apps/wei/templates/wei/weiregistration_form.html:18 msgid "Submit" -msgstr "vorlegen" +msgstr "Vorlegen" #: apps/activity/templates/activity/activity_list.html:12 msgid "Current activity" -msgstr "aktuelle Veranstaltung" +msgstr "Aktuelle Veranstaltung" #: apps/activity/templates/activity/activity_list.html:24 msgid "Upcoming activities" @@ -321,7 +321,7 @@ msgstr "Zukünftige Veranstaltungen" #: apps/activity/templates/activity/activity_list.html:31 msgid "There is no planned activity." -msgstr "Es gibt keine geplante Veranstaltung " +msgstr "Es gibt keine geplante Veranstaltung." #: apps/activity/templates/activity/activity_list.html:38 msgid "New activity" @@ -362,7 +362,7 @@ msgstr "bearbeiten" #: apps/activity/templates/activity/includes/activity_info.html:74 msgid "Invite" -msgstr "einladen" +msgstr "Einladen" #: apps/activity/views.py:29 msgid "Create new activity" @@ -390,7 +390,7 @@ msgstr "Sie haben nicht das Recht diese Seite zu benuzten." #: apps/activity/views.py:171 msgid "This activity does not support activity entries." -msgstr "Diese Veranstaltung braucht nicht Eintritt" +msgstr "Diese Veranstaltung braucht nicht Eintritt." #: apps/activity/views.py:174 msgid "This activity is closed." @@ -486,7 +486,7 @@ msgstr "Bericht Frequenz" #: apps/member/forms.py:43 msgid "Last report date" -msgstr "letzen Bericht Datum" +msgstr "Letzen Bericht Datum." #: apps/member/forms.py:48 msgid "You can't register to the note if you come from the future." @@ -507,11 +507,11 @@ msgstr "Ein ähnliches Alias ist schon benutzt." #: apps/member/forms.py:110 apps/registration/forms.py:61 msgid "Inscription paid by Société Générale" -msgstr "Mitgliedschaft von der Société Générale bezahlt." +msgstr "Mitgliedschaft von der Société Générale bezahlt" #: apps/member/forms.py:112 apps/registration/forms.py:63 msgid "Check this case is the Société Générale paid the inscription." -msgstr "Hat die Société Générale die Mitgliedschaft bezahlt ?" +msgstr "Die Société Générale die Mitgliedschaft bezahlt." #: apps/member/forms.py:117 apps/registration/forms.py:68 #: apps/wei/forms/registration.py:83 @@ -656,10 +656,9 @@ msgstr "bezahlt" #: apps/member/models.py:91 msgid "Tells if the user receive a salary." -msgstr "Ist der User bezahlt ?" +msgstr " User ist bezahlt" #: apps/member/models.py:101 apps/treasury/tables.py:146 -#: env/lib/python3.8/site-packages/django/forms/widgets.py:711 msgid "No" msgstr "Nein" @@ -676,7 +675,7 @@ msgid "" "Register on the mailing list to stay informed of the events of the campus (1 " "mail/week)" msgstr "" -"melden Sie sich auf der Mailingliste an, um über die Ereignisse des Campus " +"Melden Sie sich auf der Mailingliste an, um über die Ereignisse des Campus " "informiert zu bleiben (1 Mail / Woche)" #: apps/member/models.py:110 @@ -778,7 +777,7 @@ msgstr "Clubs" #: apps/member/models.py:319 msgid "membership starts on" -msgstr "Mitgliedschaft fängt an " +msgstr "Mitgliedschaft fängt an" #: apps/member/models.py:323 msgid "membership ends on" @@ -791,11 +790,11 @@ msgstr "Die Rolle {role} ist nicht erlaubt für das Club {club}." #: apps/member/models.py:385 apps/member/views.py:676 msgid "User is already a member of the club" -msgstr "User ist schon ein Mitglied dieser club." +msgstr "User ist schon ein Mitglied dieser club" #: apps/member/models.py:433 msgid "User is not a member of the parent club" -msgstr "User ist noch nicht Mitglied des Urclubs." +msgstr "User ist noch nicht Mitglied des Urclubs" #: apps/member/models.py:486 #, python-brace-format @@ -936,7 +935,7 @@ msgstr "Schau nur aktive Mitgliedschaften" #: apps/member/templates/member/club_members.html:23 msgid "Filter roles:" -msgstr "Rollen filtern" +msgstr "Rollen filter:" #: apps/member/templates/member/club_members.html:36 #: apps/wei/templates/wei/weimembership_list.html:17 @@ -1014,7 +1013,7 @@ msgstr "Dieser User hat noch nicht sein email bestätigt." #: apps/registration/templates/registration/future_profile_detail.html:29 #: apps/wei/templates/wei/weimembership_form.html:27 msgid "Click here to resend a validation link." -msgstr "Click hier um eine Bestätigunglinke zu schicken" +msgstr "Click hier um eine Bestätigunglinke zu schicken." #: apps/member/templates/member/profile_detail.html:21 msgid "View my memberships" @@ -1046,7 +1045,7 @@ msgstr "Notebild ändern" #: apps/member/views.py:318 msgid "Manage auth token" -msgstr "auth token bearbeiten" +msgstr "Auth token bearbeiten" #: apps/member/views.py:346 msgid "Create new club" @@ -1066,7 +1065,7 @@ msgstr "Club bearbeiten" #: apps/member/views.py:507 msgid "Add new member to the club" -msgstr "Neue Mitglieder " +msgstr "Neue Mitglieder" #: apps/member/views.py:667 apps/wei/views.py:922 msgid "" @@ -1087,9 +1086,6 @@ msgstr "Die Mitgliedschaft muss vor {:%m-%d-Y} anfängen." #: apps/member/views.py:701 apps/member/views.py:703 apps/member/views.py:705 #: apps/registration/views.py:292 apps/registration/views.py:294 #: apps/registration/views.py:296 apps/wei/views.py:927 apps/wei/views.py:931 -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_form_helper.py:140 -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_form_helper.py:150 -#: env/lib/python3.8/site-packages/django/forms/fields.py:53 msgid "This field is required." msgstr "Dies ist ein Pflichtfeld." @@ -1122,7 +1118,7 @@ msgid "" "is not active." msgstr "" "Diese Transaktion ist nicht möglich weil die Note des Sender oder des " -"Empfänger inaktiv ist. " +"Empfänger inaktiv ist." #: apps/note/forms.py:39 msgid "Source" @@ -1146,7 +1142,7 @@ msgstr "Totalanzahl größer als" #: apps/note/forms.py:93 msgid "Total amount less than" -msgstr "Totalanzahl kleiner als " +msgstr "Totalanzahl kleiner als" #: apps/note/forms.py:99 msgid "Created after" @@ -1158,7 +1154,7 @@ msgstr "Erschafft vor" #: apps/note/models/notes.py:32 msgid "account balance" -msgstr "Kontostand " +msgstr "Kontostand" #: apps/note/models/notes.py:33 msgid "in centimes, money credited for this instance" @@ -1256,7 +1252,7 @@ msgstr "Alias" #: apps/note/models/notes.py:293 msgid "Alias is too long." -msgstr "Alias ist zu lang" +msgstr "Alias ist zu lang." #: apps/note/models/notes.py:296 msgid "" @@ -1267,7 +1263,7 @@ msgstr "" #: apps/note/models/notes.py:300 msgid "An alias with a similar name already exists: {} " -msgstr "Ein Alias mit einem ähnlichen Namen existiert bereits: {}" +msgstr "Ein Alias mit einem ähnlichen Namen existiert bereits: {} " #: apps/note/models/notes.py:313 msgid "You can't delete your main alias." @@ -1405,7 +1401,6 @@ msgstr "" #: apps/treasury/templates/treasury/sogecredit_detail.html:59 #: apps/wei/tables.py:76 apps/wei/tables.py:103 #: apps/wei/templates/wei/weiregistration_confirm_delete.html:31 -#: env/lib/python3.8/site-packages/django/forms/formsets.py:375 msgid "Delete" msgstr "" @@ -2071,7 +2066,6 @@ msgid "View" msgstr "" #: apps/treasury/tables.py:146 -#: env/lib/python3.8/site-packages/django/forms/widgets.py:710 msgid "Yes" msgstr "" @@ -2779,1178 +2773,6 @@ msgstr "" msgid "This user didn't give her/his caution check." msgstr "" -#: env/lib/python3.8/site-packages/colorfield/fields.py:17 -msgid "Enter a valid color." -msgstr "" - -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout.py:355 -msgid "i18n text" -msgstr "" - -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout.py:357 -msgid "i18n legend" -msgstr "" - -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout_objects.py:127 -#: env/lib/python3.8/site-packages/django/core/validators.py:31 -msgid "Enter a valid value." -msgstr "" - -#: env/lib/python3.8/site-packages/django/contrib/messages/apps.py:7 -msgid "Messages" -msgstr "" - -#: env/lib/python3.8/site-packages/django/contrib/sitemaps/apps.py:7 -msgid "Site Maps" -msgstr "" - -#: env/lib/python3.8/site-packages/django/contrib/staticfiles/apps.py:9 -msgid "Static Files" -msgstr "" - -#: env/lib/python3.8/site-packages/django/contrib/syndication/apps.py:7 -msgid "Syndication" -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/paginator.py:45 -msgid "That page number is not an integer" -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/paginator.py:47 -msgid "That page number is less than 1" -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/paginator.py:52 -msgid "That page contains no results" -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:102 -#: env/lib/python3.8/site-packages/django/forms/fields.py:658 -msgid "Enter a valid URL." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:154 -msgid "Enter a valid integer." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:165 -msgid "Enter a valid email address." -msgstr "" - -#. Translators: "letters" means latin letters: a-z and A-Z. -#: env/lib/python3.8/site-packages/django/core/validators.py:239 -msgid "" -"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:246 -msgid "" -"Enter a valid 'slug' consisting of Unicode letters, numbers, underscores, or " -"hyphens." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:255 -#: env/lib/python3.8/site-packages/django/core/validators.py:275 -msgid "Enter a valid IPv4 address." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:260 -#: env/lib/python3.8/site-packages/django/core/validators.py:276 -msgid "Enter a valid IPv6 address." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:270 -#: env/lib/python3.8/site-packages/django/core/validators.py:274 -msgid "Enter a valid IPv4 or IPv6 address." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:304 -msgid "Enter only digits separated by commas." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:310 -#, python-format -msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:342 -#, python-format -msgid "Ensure this value is less than or equal to %(limit_value)s." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:351 -#, python-format -msgid "Ensure this value is greater than or equal to %(limit_value)s." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:361 -#, python-format -msgid "" -"Ensure this value has at least %(limit_value)d character (it has " -"%(show_value)d)." -msgid_plural "" -"Ensure this value has at least %(limit_value)d characters (it has " -"%(show_value)d)." -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:376 -#, python-format -msgid "" -"Ensure this value has at most %(limit_value)d character (it has " -"%(show_value)d)." -msgid_plural "" -"Ensure this value has at most %(limit_value)d characters (it has " -"%(show_value)d)." -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:395 -#: env/lib/python3.8/site-packages/django/forms/fields.py:290 -#: env/lib/python3.8/site-packages/django/forms/fields.py:325 -msgid "Enter a number." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:397 -#, python-format -msgid "Ensure that there are no more than %(max)s digit in total." -msgid_plural "Ensure that there are no more than %(max)s digits in total." -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:402 -#, python-format -msgid "Ensure that there are no more than %(max)s decimal place." -msgid_plural "Ensure that there are no more than %(max)s decimal places." -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:407 -#, python-format -msgid "" -"Ensure that there are no more than %(max)s digit before the decimal point." -msgid_plural "" -"Ensure that there are no more than %(max)s digits before the decimal point." -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:469 -#, python-format -msgid "" -"File extension '%(extension)s' is not allowed. Allowed extensions are: " -"'%(allowed_extensions)s'." -msgstr "" - -#: env/lib/python3.8/site-packages/django/core/validators.py:521 -msgid "Null characters are not allowed." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/base.py:1165 -#: env/lib/python3.8/site-packages/django/forms/models.py:756 -msgid "and" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/base.py:1167 -#, python-format -msgid "%(model_name)s with this %(field_labels)s already exists." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:104 -#, python-format -msgid "Value %(value)r is not a valid choice." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:105 -msgid "This field cannot be null." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:106 -msgid "This field cannot be blank." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:107 -#, python-format -msgid "%(model_name)s with this %(field_label)s already exists." -msgstr "" - -#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. -#. Eg: "Title must be unique for pub_date year" -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:111 -#, python-format -msgid "" -"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:128 -#, python-format -msgid "Field of type: %(field_type)s" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:905 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1772 -msgid "Integer" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:909 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1770 -#, python-format -msgid "'%(value)s' value must be an integer." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:984 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1850 -msgid "Big (8 byte) integer" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:996 -#, python-format -msgid "'%(value)s' value must be either True or False." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:997 -#, python-format -msgid "'%(value)s' value must be either True, False, or None." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:999 -msgid "Boolean (Either True or False)" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1040 -#, python-format -msgid "String (up to %(max_length)s)" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1104 -msgid "Comma-separated integers" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1153 -#, python-format -msgid "" -"'%(value)s' value has an invalid date format. It must be in YYYY-MM-DD " -"format." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1155 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1298 -#, python-format -msgid "" -"'%(value)s' value has the correct format (YYYY-MM-DD) but it is an invalid " -"date." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1158 -msgid "Date (without time)" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1296 -#, python-format -msgid "" -"'%(value)s' value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." -"uuuuuu]][TZ] format." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1300 -#, python-format -msgid "" -"'%(value)s' value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" -"[TZ]) but it is an invalid date/time." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1304 -msgid "Date (with time)" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1452 -#, python-format -msgid "'%(value)s' value must be a decimal number." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1454 -msgid "Decimal number" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1593 -#, python-format -msgid "" -"'%(value)s' value has an invalid format. It must be in [DD] [HH:[MM:]]ss[." -"uuuuuu] format." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1596 -msgid "Duration" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1646 -msgid "Email address" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1669 -msgid "File path" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1735 -#, python-format -msgid "'%(value)s' value must be a float." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1737 -msgid "Floating point number" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1866 -msgid "IPv4 address" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1897 -msgid "IP address" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1977 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1978 -#, python-format -msgid "'%(value)s' value must be either None, True or False." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1980 -msgid "Boolean (Either True, False or None)" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2015 -msgid "Positive integer" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2028 -msgid "Positive small integer" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2042 -#, python-format -msgid "Slug (up to %(max_length)s)" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2074 -msgid "Small integer" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2081 -msgid "Text" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2109 -#, python-format -msgid "" -"'%(value)s' value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " -"format." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2111 -#, python-format -msgid "" -"'%(value)s' value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " -"invalid time." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2114 -msgid "Time" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2240 -msgid "URL" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2262 -msgid "Raw binary data" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2312 -#, python-format -msgid "'%(value)s' is not a valid UUID." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2314 -msgid "Universally unique identifier" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/files.py:221 -msgid "File" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/files.py:360 -msgid "Image" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:778 -#, python-format -msgid "%(model)s instance with %(field)s %(value)r does not exist." -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:780 -msgid "Foreign Key (type determined by related field)" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1007 -msgid "One-to-one relationship" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1057 -#, python-format -msgid "%(from)s-%(to)s relationship" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1058 -#, python-format -msgid "%(from)s-%(to)s relationships" -msgstr "" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1100 -msgid "Many-to-many relationship" -msgstr "" - -#. Translators: If found as last label character, these punctuation -#. characters will prevent the default label_suffix to be appended to the label -#: env/lib/python3.8/site-packages/django/forms/boundfield.py:146 -msgid ":?.!" -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:245 -msgid "Enter a whole number." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:396 -#: env/lib/python3.8/site-packages/django/forms/fields.py:1126 -msgid "Enter a valid date." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:420 -#: env/lib/python3.8/site-packages/django/forms/fields.py:1127 -msgid "Enter a valid time." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:442 -msgid "Enter a valid date/time." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:471 -msgid "Enter a valid duration." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:472 -#, python-brace-format -msgid "The number of days must be between {min_days} and {max_days}." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:532 -msgid "No file was submitted. Check the encoding type on the form." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:533 -msgid "No file was submitted." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:534 -msgid "The submitted file is empty." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:536 -#, python-format -msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." -msgid_plural "" -"Ensure this filename has at most %(max)d characters (it has %(length)d)." -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:539 -msgid "Please either submit a file or check the clear checkbox, not both." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:600 -msgid "" -"Upload a valid image. The file you uploaded was either not an image or a " -"corrupted image." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:762 -#: env/lib/python3.8/site-packages/django/forms/fields.py:852 -#: env/lib/python3.8/site-packages/django/forms/models.py:1270 -#, python-format -msgid "Select a valid choice. %(value)s is not one of the available choices." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:853 -#: env/lib/python3.8/site-packages/django/forms/fields.py:968 -#: env/lib/python3.8/site-packages/django/forms/models.py:1269 -msgid "Enter a list of values." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:969 -msgid "Enter a complete value." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:1185 -msgid "Enter a valid UUID." -msgstr "" - -#. Translators: This is the default suffix added to form field labels -#: env/lib/python3.8/site-packages/django/forms/forms.py:86 -msgid ":" -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/forms.py:212 -#, python-format -msgid "(Hidden field %(name)s) %(error)s" -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/formsets.py:91 -msgid "ManagementForm data is missing or has been tampered with" -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/formsets.py:338 -#, python-format -msgid "Please submit %d or fewer forms." -msgid_plural "Please submit %d or fewer forms." -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/forms/formsets.py:345 -#, python-format -msgid "Please submit %d or more forms." -msgid_plural "Please submit %d or more forms." -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/forms/formsets.py:371 -#: env/lib/python3.8/site-packages/django/forms/formsets.py:373 -msgid "Order" -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/models.py:751 -#, python-format -msgid "Please correct the duplicate data for %(field)s." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/models.py:755 -#, python-format -msgid "Please correct the duplicate data for %(field)s, which must be unique." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/models.py:761 -#, python-format -msgid "" -"Please correct the duplicate data for %(field_name)s which must be unique " -"for the %(lookup)s in %(date_field)s." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/models.py:770 -msgid "Please correct the duplicate values below." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/models.py:1091 -msgid "The inline value did not match the parent instance." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/models.py:1158 -msgid "Select a valid choice. That choice is not one of the available choices." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/models.py:1272 -#, python-format -msgid "\"%(pk)s\" is not a valid value." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/utils.py:162 -#, python-format -msgid "" -"%(datetime)s couldn't be interpreted in time zone %(current_timezone)s; it " -"may be ambiguous or it may not exist." -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/widgets.py:395 -msgid "Clear" -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/widgets.py:396 -msgid "Currently" -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/widgets.py:397 -msgid "Change" -msgstr "" - -#: env/lib/python3.8/site-packages/django/forms/widgets.py:709 -msgid "Unknown" -msgstr "" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:788 -msgid "yes,no,maybe" -msgstr "" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:817 -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:834 -#, python-format -msgid "%(size)d byte" -msgid_plural "%(size)d bytes" -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:836 -#, python-format -msgid "%s KB" -msgstr "" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:838 -#, python-format -msgid "%s MB" -msgstr "" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:840 -#, python-format -msgid "%s GB" -msgstr "" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:842 -#, python-format -msgid "%s TB" -msgstr "" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:844 -#, python-format -msgid "%s PB" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:62 -msgid "p.m." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:63 -msgid "a.m." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:68 -msgid "PM" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:69 -msgid "AM" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:150 -msgid "midnight" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:152 -msgid "noon" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Monday" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Tuesday" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Wednesday" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Thursday" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Friday" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:7 -msgid "Saturday" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:7 -msgid "Sunday" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Mon" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Tue" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Wed" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Thu" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Fri" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:11 -msgid "Sat" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:11 -msgid "Sun" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "January" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "February" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "March" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "April" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "May" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "June" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "July" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "August" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "September" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "October" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "November" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:16 -msgid "December" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "jan" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "feb" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "mar" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "apr" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "may" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "jun" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "jul" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "aug" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "sep" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "oct" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "nov" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "dec" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:23 -msgctxt "abbrev. month" -msgid "Jan." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:24 -msgctxt "abbrev. month" -msgid "Feb." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:25 -msgctxt "abbrev. month" -msgid "March" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:26 -msgctxt "abbrev. month" -msgid "April" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:27 -msgctxt "abbrev. month" -msgid "May" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:28 -msgctxt "abbrev. month" -msgid "June" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:29 -msgctxt "abbrev. month" -msgid "July" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:30 -msgctxt "abbrev. month" -msgid "Aug." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:31 -msgctxt "abbrev. month" -msgid "Sept." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:32 -msgctxt "abbrev. month" -msgid "Oct." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:33 -msgctxt "abbrev. month" -msgid "Nov." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:34 -msgctxt "abbrev. month" -msgid "Dec." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:37 -msgctxt "alt. month" -msgid "January" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:38 -msgctxt "alt. month" -msgid "February" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:39 -msgctxt "alt. month" -msgid "March" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:40 -msgctxt "alt. month" -msgid "April" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:41 -msgctxt "alt. month" -msgid "May" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:42 -msgctxt "alt. month" -msgid "June" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:43 -msgctxt "alt. month" -msgid "July" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:44 -msgctxt "alt. month" -msgid "August" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:45 -msgctxt "alt. month" -msgid "September" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:46 -msgctxt "alt. month" -msgid "October" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:47 -msgctxt "alt. month" -msgid "November" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:48 -msgctxt "alt. month" -msgid "December" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/ipv6.py:8 -msgid "This is not a valid IPv6 address." -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/text.py:67 -#, python-format -msgctxt "String to return when truncating text" -msgid "%(truncated_text)s…" -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/text.py:233 -msgid "or" -msgstr "" - -#. Translators: This string is used as a separator between list elements -#: env/lib/python3.8/site-packages/django/utils/text.py:252 -#: env/lib/python3.8/site-packages/django/utils/timesince.py:83 -msgid ", " -msgstr "" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:9 -#, python-format -msgid "%d year" -msgid_plural "%d years" -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:10 -#, python-format -msgid "%d month" -msgid_plural "%d months" -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:11 -#, python-format -msgid "%d week" -msgid_plural "%d weeks" -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:12 -#, python-format -msgid "%d day" -msgid_plural "%d days" -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:13 -#, python-format -msgid "%d hour" -msgid_plural "%d hours" -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:14 -#, python-format -msgid "%d minute" -msgid_plural "%d minutes" -msgstr[0] "" -msgstr[1] "" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:72 -msgid "0 minutes" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:110 -msgid "Forbidden" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:111 -msgid "CSRF verification failed. Request aborted." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:115 -msgid "" -"You are seeing this message because this HTTPS site requires a 'Referer " -"header' to be sent by your Web browser, but none was sent. This header is " -"required for security reasons, to ensure that your browser is not being " -"hijacked by third parties." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:120 -msgid "" -"If you have configured your browser to disable 'Referer' headers, please re-" -"enable them, at least for this site, or for HTTPS connections, or for 'same-" -"origin' requests." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:124 -msgid "" -"If you are using the tag or " -"including the 'Referrer-Policy: no-referrer' header, please remove them. The " -"CSRF protection requires the 'Referer' header to do strict referer checking. " -"If you're concerned about privacy, use alternatives like for links to third-party sites." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:132 -msgid "" -"You are seeing this message because this site requires a CSRF cookie when " -"submitting forms. This cookie is required for security reasons, to ensure " -"that your browser is not being hijacked by third parties." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:137 -msgid "" -"If you have configured your browser to disable cookies, please re-enable " -"them, at least for this site, or for 'same-origin' requests." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:142 -msgid "More information is available with DEBUG=True." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:41 -msgid "No year specified" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:61 -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:111 -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:208 -msgid "Date out of range" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:90 -msgid "No month specified" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:142 -msgid "No day specified" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:188 -msgid "No week specified" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:338 -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:367 -#, python-format -msgid "No %(verbose_name_plural)s available" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:589 -#, python-format -msgid "" -"Future %(verbose_name_plural)s not available because %(class_name)s." -"allow_future is False." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:623 -#, python-format -msgid "Invalid date string '%(datestr)s' given format '%(format)s'" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/detail.py:54 -#, python-format -msgid "No %(verbose_name)s found matching the query" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/list.py:67 -msgid "Page is not 'last', nor can it be converted to an int." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/list.py:72 -#, python-format -msgid "Invalid page (%(page_number)s): %(message)s" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/generic/list.py:154 -#, python-format -msgid "Empty list and '%(class_name)s.allow_empty' is False." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/static.py:40 -msgid "Directory indexes are not allowed here." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/static.py:42 -#, python-format -msgid "\"%(path)s\" does not exist" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/static.py:80 -#, python-format -msgid "Index of %(directory)s" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:6 -msgid "Django: the Web framework for perfectionists with deadlines." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:345 -#, python-format -msgid "" -"View release notes for Django %(version)s" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:367 -msgid "The install worked successfully! Congratulations!" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:368 -#, python-format -msgid "" -"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " -"URLs." -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:383 -msgid "Django Documentation" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:384 -msgid "Topics, references, & how-to's" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:395 -msgid "Tutorial: A Polling App" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:396 -msgid "Get started with Django" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:407 -msgid "Django Community" -msgstr "" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:408 -msgid "Connect, get help, or contribute" -msgstr "" - #: note_kfet/settings/base.py:155 msgid "German" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 1b2f1dd5..96feaac7 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,15 +7,15 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-02 19:18+0200\n" -"PO-Revision-Date: 2020-09-02 21:38+0200\n" +"POT-Creation-Date: 2020-09-02 23:17+0200\n" +"PO-Revision-Date: 2020-09-02 23:18+0200\n" +"Last-Translator: \n" +"Language-Team: \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" -"Last-Translator: \n" -"Language-Team: \n" "X-Generator: Poedit 2.3\n" #: apps/activity/apps.py:10 apps/activity/models.py:145 @@ -661,7 +661,6 @@ msgid "Tells if the user receive a salary." msgstr "Indique si l'utilisateur perçoit un salaire." #: apps/member/models.py:101 apps/treasury/tables.py:146 -#: env/lib/python3.8/site-packages/django/forms/widgets.py:711 msgid "No" msgstr "Non" @@ -1092,9 +1091,6 @@ msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." #: apps/member/views.py:701 apps/member/views.py:703 apps/member/views.py:705 #: apps/registration/views.py:292 apps/registration/views.py:294 #: apps/registration/views.py:296 apps/wei/views.py:927 apps/wei/views.py:931 -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_form_helper.py:140 -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_form_helper.py:150 -#: env/lib/python3.8/site-packages/django/forms/fields.py:53 msgid "This field is required." msgstr "Ce champ est requis." @@ -1420,7 +1416,6 @@ msgstr "Pas de motif spécifié" #: apps/treasury/templates/treasury/sogecredit_detail.html:59 #: apps/wei/tables.py:76 apps/wei/tables.py:103 #: apps/wei/templates/wei/weiregistration_confirm_delete.html:31 -#: env/lib/python3.8/site-packages/django/forms/formsets.py:375 msgid "Delete" msgstr "Supprimer" @@ -2121,7 +2116,6 @@ msgid "View" msgstr "Voir" #: apps/treasury/tables.py:146 -#: env/lib/python3.8/site-packages/django/forms/widgets.py:710 msgid "Yes" msgstr "Oui" @@ -2872,1242 +2866,6 @@ msgstr "Valider l'inscription WEI" msgid "This user didn't give her/his caution check." msgstr "Cet utilisateur n'a pas donné son chèque de caution." -#: env/lib/python3.8/site-packages/colorfield/fields.py:17 -msgid "Enter a valid color." -msgstr "Entrer une couleur valide." - -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout.py:355 -msgid "i18n text" -msgstr "i18n text" - -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout.py:357 -msgid "i18n legend" -msgstr "i18n legend" - -#: env/lib/python3.8/site-packages/crispy_forms/tests/test_layout_objects.py:127 -#: env/lib/python3.8/site-packages/django/core/validators.py:31 -msgid "Enter a valid value." -msgstr "Entrer une valeur correcte." - -#: env/lib/python3.8/site-packages/django/contrib/messages/apps.py:7 -msgid "Messages" -msgstr "Messages" - -#: env/lib/python3.8/site-packages/django/contrib/sitemaps/apps.py:7 -msgid "Site Maps" -msgstr "Plan du site" - -#: env/lib/python3.8/site-packages/django/contrib/staticfiles/apps.py:9 -msgid "Static Files" -msgstr "Fichiers statiques" - -#: env/lib/python3.8/site-packages/django/contrib/syndication/apps.py:7 -msgid "Syndication" -msgstr "Invitation" - -#: env/lib/python3.8/site-packages/django/core/paginator.py:45 -msgid "That page number is not an integer" -msgstr "Ce numéro de page n'est pas entier" - -#: env/lib/python3.8/site-packages/django/core/paginator.py:47 -msgid "That page number is less than 1" -msgstr "Ce numéro de page est inférieur à 1" - -#: env/lib/python3.8/site-packages/django/core/paginator.py:52 -msgid "That page contains no results" -msgstr "Il n'y a pas de résultat" - -#: env/lib/python3.8/site-packages/django/core/validators.py:102 -#: env/lib/python3.8/site-packages/django/forms/fields.py:658 -msgid "Enter a valid URL." -msgstr "Entrer une URL valide." - -#: env/lib/python3.8/site-packages/django/core/validators.py:154 -msgid "Enter a valid integer." -msgstr "Entrer un entier valid." - -#: env/lib/python3.8/site-packages/django/core/validators.py:165 -msgid "Enter a valid email address." -msgstr "Entrer une adresse mail valide." - -#. Translators: "letters" means latin letters: a-z and A-Z. -#: env/lib/python3.8/site-packages/django/core/validators.py:239 -msgid "" -"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." -msgstr "" -"Entrer un 'slug' valide, constitué de lettres, chiffres, tirets ou tirets " -"bas." - -#: env/lib/python3.8/site-packages/django/core/validators.py:246 -msgid "" -"Enter a valid 'slug' consisting of Unicode letters, numbers, underscores, or " -"hyphens." -msgstr "" -"Entrer un 'slug' valide, constitué de caractère unicode, chiffres, tirets ou " -"underscores." - -#: env/lib/python3.8/site-packages/django/core/validators.py:255 -#: env/lib/python3.8/site-packages/django/core/validators.py:275 -msgid "Enter a valid IPv4 address." -msgstr "Entrer une adresse IPv4 valide." - -#: env/lib/python3.8/site-packages/django/core/validators.py:260 -#: env/lib/python3.8/site-packages/django/core/validators.py:276 -msgid "Enter a valid IPv6 address." -msgstr "Entrer une adresse IPv6 valide." - -#: env/lib/python3.8/site-packages/django/core/validators.py:270 -#: env/lib/python3.8/site-packages/django/core/validators.py:274 -msgid "Enter a valid IPv4 or IPv6 address." -msgstr "Entrer une adresse IPv4 ou IPv6 valide." - -#: env/lib/python3.8/site-packages/django/core/validators.py:304 -msgid "Enter only digits separated by commas." -msgstr "Entrer seulement des chiffres séparés par des virgules." - -#: env/lib/python3.8/site-packages/django/core/validators.py:310 -#, python-format -msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." -msgstr "" -"Vérifier que cette valeur est %(limit_value)s (actuellement %(show_value)s)." - -#: env/lib/python3.8/site-packages/django/core/validators.py:342 -#, python-format -msgid "Ensure this value is less than or equal to %(limit_value)s." -msgstr "Vérifier que cette valeur est plus petite que %(limit_value)s." - -#: env/lib/python3.8/site-packages/django/core/validators.py:351 -#, python-format -msgid "Ensure this value is greater than or equal to %(limit_value)s." -msgstr "Vérifier que cette valeur est plus grande que %(limit_value)s." - -#: env/lib/python3.8/site-packages/django/core/validators.py:361 -#, python-format -msgid "" -"Ensure this value has at least %(limit_value)d character (it has " -"%(show_value)d)." -msgid_plural "" -"Ensure this value has at least %(limit_value)d characters (it has " -"%(show_value)d)." -msgstr[0] "" -"Vérifier que cette valeur a au moins %(limit_value)d caractère " -"(actuellement %(show_value)d)." -msgstr[1] "" -"Assurer vous que cette valeur a au moins %(limit_value)d caractères " -"(actuellement %(show_value)d)." - -#: env/lib/python3.8/site-packages/django/core/validators.py:376 -#, python-format -msgid "" -"Ensure this value has at most %(limit_value)d character (it has " -"%(show_value)d)." -msgid_plural "" -"Ensure this value has at most %(limit_value)d characters (it has " -"%(show_value)d)." -msgstr[0] "" -"Vérifier que cette valeur a au plus %(limit_value)d caractère (actuellement " -"%(show_value)d)." -msgstr[1] "" -"Assurer vous que cette valeur a au plus %(limit_value)d caractères " -"(actuellement %(show_value)d)." - -#: env/lib/python3.8/site-packages/django/core/validators.py:395 -#: env/lib/python3.8/site-packages/django/forms/fields.py:290 -#: env/lib/python3.8/site-packages/django/forms/fields.py:325 -msgid "Enter a number." -msgstr "Numéro de téléphone." - -#: env/lib/python3.8/site-packages/django/core/validators.py:397 -#, python-format -msgid "Ensure that there are no more than %(max)s digit in total." -msgid_plural "Ensure that there are no more than %(max)s digits in total." -msgstr[0] "Vérifier qu'il n'y a pas plus de %(max)s chiffre au total." -msgstr[1] "Vérifier qu'il n'y a pas plus de %(max)s chiffres au total." - -#: env/lib/python3.8/site-packages/django/core/validators.py:402 -#, python-format -msgid "Ensure that there are no more than %(max)s decimal place." -msgid_plural "Ensure that there are no more than %(max)s decimal places." -msgstr[0] "Vérifier qu'il y n'as pas plus de %(max)s chiffre décimal." -msgstr[1] "Vérifier qu'il y n'as pas plus de %(max)s chiffres décimaux." - -#: env/lib/python3.8/site-packages/django/core/validators.py:407 -#, python-format -msgid "" -"Ensure that there are no more than %(max)s digit before the decimal point." -msgid_plural "" -"Ensure that there are no more than %(max)s digits before the decimal point." -msgstr[0] "Vérifier qu'il y n'as pas plus d'%(max)s chiffre avant la virgule." -msgstr[1] "" -"Vérifier qu'il y n'as pas plus de %(max)s chiffres avant la virgule." - -#: env/lib/python3.8/site-packages/django/core/validators.py:469 -#, python-format -msgid "" -"File extension '%(extension)s' is not allowed. Allowed extensions are: " -"'%(allowed_extensions)s'." -msgstr "" -"Les fichiers d'extension '%(extension)s' ne sont pas autorisé. Les extension " -"autorisées sont: \"%(allowed_extensions)s'." - -#: env/lib/python3.8/site-packages/django/core/validators.py:521 -msgid "Null characters are not allowed." -msgstr "Les caractères nuls ne sont pas autorisés." - -#: env/lib/python3.8/site-packages/django/db/models/base.py:1165 -#: env/lib/python3.8/site-packages/django/forms/models.py:756 -msgid "and" -msgstr "et" - -#: env/lib/python3.8/site-packages/django/db/models/base.py:1167 -#, python-format -msgid "%(model_name)s with this %(field_labels)s already exists." -msgstr "Un %(model_name)s avec ce %(field_labels)s existe déjà." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:104 -#, python-format -msgid "Value %(value)r is not a valid choice." -msgstr "Le choix %(value)r n'est pas possible." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:105 -msgid "This field cannot be null." -msgstr "Ce champ est requis." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:106 -msgid "This field cannot be blank." -msgstr "Ce champ est requis." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:107 -#, python-format -msgid "%(model_name)s with this %(field_label)s already exists." -msgstr "Un %(model_name)s avec ce %(field_label)s existe déjà." - -#. Translators: The 'lookup_type' is one of 'date', 'year' or 'month'. -#. Eg: "Title must be unique for pub_date year" -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:111 -#, python-format -msgid "" -"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." -msgstr "" -"%(field_label)s doit être unique pour %(date_field_label)s %(lookup_type)s." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:128 -#, python-format -msgid "Field of type: %(field_type)s" -msgstr "Champ du type %(field_type)s" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:905 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1772 -msgid "Integer" -msgstr "Nombre entier" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:909 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1770 -#, python-format -msgid "'%(value)s' value must be an integer." -msgstr "'%(value)s' doit être un nombre entier." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:984 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1850 -msgid "Big (8 byte) integer" -msgstr "Gros nombre entier (8 octets)" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:996 -#, python-format -msgid "'%(value)s' value must be either True or False." -msgstr "'%(value)s' doit être Vrai ou Faux." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:997 -#, python-format -msgid "'%(value)s' value must be either True, False, or None." -msgstr "'%(value)s' doit être Vrai, Faux, ou None." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:999 -msgid "Boolean (Either True or False)" -msgstr "Booléen (Vrai ou Faux)" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1040 -#, python-format -msgid "String (up to %(max_length)s)" -msgstr "Chaîne de caractère (maximum %(max_length)s caractères)" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1104 -msgid "Comma-separated integers" -msgstr "Liste d'entier séparer par des virgules" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1153 -#, python-format -msgid "" -"'%(value)s' value has an invalid date format. It must be in YYYY-MM-DD " -"format." -msgstr "'%(value)s n'est pas formaté correctement (AAAA-MM-JJ)." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1155 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1298 -#, python-format -msgid "" -"'%(value)s' value has the correct format (YYYY-MM-DD) but it is an invalid " -"date." -msgstr "" -"'%(value)s possède le format date requis (AAAA-MM-JJ) mais n'est pas une " -"date valide." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1158 -msgid "Date (without time)" -msgstr "Date (sans horaire)" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1296 -#, python-format -msgid "" -"'%(value)s' value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[." -"uuuuuu]][TZ] format." -msgstr "" -"'%(value)s' n'est pas formatée correctement (AAAA-MM-JJ HH:MM[:ss[.uuuuuu]]" -"[TZ])." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1300 -#, python-format -msgid "" -"'%(value)s' value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" -"[TZ]) but it is an invalid date/time." -msgstr "" -"'%(value)s' est formaté correctement, mais n'est pas une date/horaire valide." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1304 -msgid "Date (with time)" -msgstr "Date (avec horaire)" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1452 -#, python-format -msgid "'%(value)s' value must be a decimal number." -msgstr "'%(value)s doit être un nombre décimal." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1454 -msgid "Decimal number" -msgstr "Nombre décimal" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1593 -#, python-format -msgid "" -"'%(value)s' value has an invalid format. It must be in [DD] [HH:[MM:]]ss[." -"uuuuuu] format." -msgstr "" -"'%(value)s' n'est pas formatée correctement (AAAA-MM-JJ HH:MM[:ss[.uuuuuu]]" -"[TZ])." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1596 -msgid "Duration" -msgstr "Durée" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1646 -msgid "Email address" -msgstr "Courriel" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1669 -msgid "File path" -msgstr "Chemin du fichier" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1735 -#, python-format -msgid "'%(value)s' value must be a float." -msgstr "'%(value)s doit être un nombre décimal." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1737 -msgid "Floating point number" -msgstr "Nombre décimal" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1866 -msgid "IPv4 address" -msgstr "Adresse IPv4" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1897 -msgid "IP address" -msgstr "Adresse IP" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1977 -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1978 -#, python-format -msgid "'%(value)s' value must be either None, True or False." -msgstr "'%(value)s' doit être Vrai, Faux, ou None." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:1980 -msgid "Boolean (Either True, False or None)" -msgstr "Booléen (Vrai ou Faux)" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2015 -msgid "Positive integer" -msgstr "Nombre entier positif" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2028 -msgid "Positive small integer" -msgstr "Nombre entier positif petit" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2042 -#, python-format -msgid "Slug (up to %(max_length)s)" -msgstr "Slug (maximum %(max_length)s caractères)" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2074 -msgid "Small integer" -msgstr "Petit entier" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2081 -msgid "Text" -msgstr "Texte" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2109 -#, python-format -msgid "" -"'%(value)s' value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " -"format." -msgstr "'%(value)s' n'est pas formatée correctement (HH:MM[:ss[.uuuuuu]])." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2111 -#, python-format -msgid "" -"'%(value)s' value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " -"invalid time." -msgstr "" -"'%(value)s' est formaté correctement, mais n'est pas un horaire valide." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2114 -msgid "Time" -msgstr "Temps" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2240 -msgid "URL" -msgstr "URL" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2262 -msgid "Raw binary data" -msgstr "Donnée binaire brute" - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2312 -#, python-format -msgid "'%(value)s' is not a valid UUID." -msgstr "'%(value)s' n'est pas un UUID valide." - -#: env/lib/python3.8/site-packages/django/db/models/fields/__init__.py:2314 -msgid "Universally unique identifier" -msgstr "Identifiant unique universel" - -#: env/lib/python3.8/site-packages/django/db/models/fields/files.py:221 -msgid "File" -msgstr "Fichier" - -#: env/lib/python3.8/site-packages/django/db/models/fields/files.py:360 -msgid "Image" -msgstr "Image" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:778 -#, python-format -msgid "%(model)s instance with %(field)s %(value)r does not exist." -msgstr "l'objet %(model)s avec les champs %(field)s %(value)r n'existe pas." - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:780 -msgid "Foreign Key (type determined by related field)" -msgstr "Relations plusieurs-à-un (type déterminé par le champ relié)" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1007 -msgid "One-to-one relationship" -msgstr "Relation un-à-un" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1057 -#, python-format -msgid "%(from)s-%(to)s relationship" -msgstr "Relation %(from)s-%(to)s" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1058 -#, python-format -msgid "%(from)s-%(to)s relationships" -msgstr "Relations %(from)s-%(to)s" - -#: env/lib/python3.8/site-packages/django/db/models/fields/related.py:1100 -msgid "Many-to-many relationship" -msgstr "Relation plusieurs-à-plusieurs" - -#. Translators: If found as last label character, these punctuation -#. characters will prevent the default label_suffix to be appended to the label -#: env/lib/python3.8/site-packages/django/forms/boundfield.py:146 -msgid ":?.!" -msgstr ":?.!" - -#: env/lib/python3.8/site-packages/django/forms/fields.py:245 -msgid "Enter a whole number." -msgstr "Entrer un numéro complet." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:396 -#: env/lib/python3.8/site-packages/django/forms/fields.py:1126 -msgid "Enter a valid date." -msgstr "Entrer une date valide." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:420 -#: env/lib/python3.8/site-packages/django/forms/fields.py:1127 -msgid "Enter a valid time." -msgstr "Entrer un horaire valide." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:442 -msgid "Enter a valid date/time." -msgstr "Entrer une date et un horaire valide." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:471 -msgid "Enter a valid duration." -msgstr "Entrer une durée valide." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:472 -#, python-brace-format -msgid "The number of days must be between {min_days} and {max_days}." -msgstr "Le nombre de jours doit être compris entre {min_days} et {max_days}." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:532 -msgid "No file was submitted. Check the encoding type on the form." -msgstr "Aucun fichier n'as été ajouté. Vérifier l'encodage du formulaire." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:533 -msgid "No file was submitted." -msgstr "Aucun fichier n'as été ajouté." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:534 -msgid "The submitted file is empty." -msgstr "Le fichier ajouté est vide." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:536 -#, python-format -msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." -msgid_plural "" -"Ensure this filename has at most %(max)d characters (it has %(length)d)." -msgstr[0] "" -"Vérifier que le nom du fichier a au plus %(max)d caractère (actuellement " -"%(length)d)." -msgstr[1] "" -"Vérifier que le nom du fichier a au plus %(max)d caractères (actuellement " -"%(length)d)." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:539 -msgid "Please either submit a file or check the clear checkbox, not both." -msgstr "Veuillez ajouter un fichier ou cocher la case envoi vide." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:600 -msgid "" -"Upload a valid image. The file you uploaded was either not an image or a " -"corrupted image." -msgstr "Veuillez choisir une image valide." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:762 -#: env/lib/python3.8/site-packages/django/forms/fields.py:852 -#: env/lib/python3.8/site-packages/django/forms/models.py:1270 -#, python-format -msgid "Select a valid choice. %(value)s is not one of the available choices." -msgstr "Faites un choix valide. %(value)s n'est pas autorisé." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:853 -#: env/lib/python3.8/site-packages/django/forms/fields.py:968 -#: env/lib/python3.8/site-packages/django/forms/models.py:1269 -msgid "Enter a list of values." -msgstr "Entrer une liste de valeurs." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:969 -msgid "Enter a complete value." -msgstr "Entrer une valeur complète." - -#: env/lib/python3.8/site-packages/django/forms/fields.py:1185 -msgid "Enter a valid UUID." -msgstr "Entrer un UUID valid." - -#. Translators: This is the default suffix added to form field labels -#: env/lib/python3.8/site-packages/django/forms/forms.py:86 -msgid ":" -msgstr ":" - -#: env/lib/python3.8/site-packages/django/forms/forms.py:212 -#, python-format -msgid "(Hidden field %(name)s) %(error)s" -msgstr "(Champ caché %(name)s) %(error)s" - -#: env/lib/python3.8/site-packages/django/forms/formsets.py:91 -msgid "ManagementForm data is missing or has been tampered with" -msgstr "Les données ManagementForm sont manquantes ou falsifiées" - -#: env/lib/python3.8/site-packages/django/forms/formsets.py:338 -#, python-format -msgid "Please submit %d or fewer forms." -msgid_plural "Please submit %d or fewer forms." -msgstr[0] "Veuillez remplir %d formulaire ou moins." -msgstr[1] "Veuillez remplir %d formulaires ou moins." - -#: env/lib/python3.8/site-packages/django/forms/formsets.py:345 -#, python-format -msgid "Please submit %d or more forms." -msgid_plural "Please submit %d or more forms." -msgstr[0] "Veuillez remplir %d formulaire ou plus." -msgstr[1] "Veuillez remplir %d formulaires ou plus." - -#: env/lib/python3.8/site-packages/django/forms/formsets.py:371 -#: env/lib/python3.8/site-packages/django/forms/formsets.py:373 -msgid "Order" -msgstr "Ordre" - -#: env/lib/python3.8/site-packages/django/forms/models.py:751 -#, python-format -msgid "Please correct the duplicate data for %(field)s." -msgstr "Veuillez corriger les données dupliquées pour %(field)s." - -#: env/lib/python3.8/site-packages/django/forms/models.py:755 -#, python-format -msgid "Please correct the duplicate data for %(field)s, which must be unique." -msgstr "Veuillez rendre uniques les valeurs des champs %(field)s." - -#: env/lib/python3.8/site-packages/django/forms/models.py:761 -#, python-format -msgid "" -"Please correct the duplicate data for %(field_name)s which must be unique " -"for the %(lookup)s in %(date_field)s." -msgstr "" -"Veuillez rendre unique les valeurs des champs %(field_name)s pour les " -"%(lookup)s dans %(date_field)s." - -#: env/lib/python3.8/site-packages/django/forms/models.py:770 -msgid "Please correct the duplicate values below." -msgstr "Veuillez rendre uniques les valeurs ci-dessous." - -#: env/lib/python3.8/site-packages/django/forms/models.py:1091 -msgid "The inline value did not match the parent instance." -msgstr "La valeur renseignée ne correspond pas à l'objet parent." - -#: env/lib/python3.8/site-packages/django/forms/models.py:1158 -msgid "Select a valid choice. That choice is not one of the available choices." -msgstr "Faites un choix possible." - -#: env/lib/python3.8/site-packages/django/forms/models.py:1272 -#, python-format -msgid "\"%(pk)s\" is not a valid value." -msgstr "\"%(pk)s\" n'est pas une valeur autorisée." - -#: env/lib/python3.8/site-packages/django/forms/utils.py:162 -#, python-format -msgid "" -"%(datetime)s couldn't be interpreted in time zone %(current_timezone)s; it " -"may be ambiguous or it may not exist." -msgstr "" -"%(datetime)s n'a pas pu être interprété dans la zone %(current_timezone)s, " -"La valeur est ambigüe ou impossible." - -#: env/lib/python3.8/site-packages/django/forms/widgets.py:395 -msgid "Clear" -msgstr "Vider" - -#: env/lib/python3.8/site-packages/django/forms/widgets.py:396 -msgid "Currently" -msgstr "Activité en cours" - -#: env/lib/python3.8/site-packages/django/forms/widgets.py:397 -msgid "Change" -msgstr "Modifier" - -#: env/lib/python3.8/site-packages/django/forms/widgets.py:709 -msgid "Unknown" -msgstr "Inconnu" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:788 -msgid "yes,no,maybe" -msgstr "oui, non, peut-être" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:817 -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:834 -#, python-format -msgid "%(size)d byte" -msgid_plural "%(size)d bytes" -msgstr[0] "%(size)d octet" -msgstr[1] "%(size)d octets" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:836 -#, python-format -msgid "%s KB" -msgstr "%s Ko" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:838 -#, python-format -msgid "%s MB" -msgstr "%s Mo" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:840 -#, python-format -msgid "%s GB" -msgstr "%s Go" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:842 -#, python-format -msgid "%s TB" -msgstr "%s To" - -#: env/lib/python3.8/site-packages/django/template/defaultfilters.py:844 -#, python-format -msgid "%s PB" -msgstr "%s Po" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:62 -msgid "p.m." -msgstr "p.m." - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:63 -msgid "a.m." -msgstr "a.m." - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:68 -msgid "PM" -msgstr "PM" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:69 -msgid "AM" -msgstr "AM" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:150 -msgid "midnight" -msgstr "minuit" - -#: env/lib/python3.8/site-packages/django/utils/dateformat.py:152 -msgid "noon" -msgstr "midi" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Monday" -msgstr "Lundi" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Tuesday" -msgstr "Mardi" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Wednesday" -msgstr "Mercredi" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Thursday" -msgstr "Jeudi" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:6 -msgid "Friday" -msgstr "Vendredi" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:7 -msgid "Saturday" -msgstr "Samedi" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:7 -msgid "Sunday" -msgstr "Dimanche" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Mon" -msgstr "Lun" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Tue" -msgstr "Mar" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Wed" -msgstr "Mer" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Thu" -msgstr "Jeu" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:10 -msgid "Fri" -msgstr "Ven" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:11 -msgid "Sat" -msgstr "Sam" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:11 -msgid "Sun" -msgstr "Dim" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "January" -msgstr "Janvier" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "February" -msgstr "Février" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "March" -msgstr "Mars" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "April" -msgstr "Avril" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "May" -msgstr "Mai" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:14 -msgid "June" -msgstr "Juin" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "July" -msgstr "Juillet" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "August" -msgstr "Août" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "September" -msgstr "Septembre" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "October" -msgstr "Octobre" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:15 -msgid "November" -msgstr "Novembre" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:16 -msgid "December" -msgstr "Décembre" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "jan" -msgstr "jan" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "feb" -msgstr "fev" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "mar" -msgstr "mar" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "apr" -msgstr "avr" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "may" -msgstr "mai" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:19 -msgid "jun" -msgstr "jun" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "jul" -msgstr "jui" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "aug" -msgstr "aou" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "sep" -msgstr "sep" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "oct" -msgstr "oct" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "nov" -msgstr "nov" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:20 -msgid "dec" -msgstr "dec" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:23 -msgctxt "abbrev. month" -msgid "Jan." -msgstr "[abbrev. month] Jan." - -#: env/lib/python3.8/site-packages/django/utils/dates.py:24 -msgctxt "abbrev. month" -msgid "Feb." -msgstr "[abbrev. month] Fév." - -#: env/lib/python3.8/site-packages/django/utils/dates.py:25 -msgctxt "abbrev. month" -msgid "March" -msgstr "[abbrev. month] Mars" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:26 -msgctxt "abbrev. month" -msgid "April" -msgstr "[abbrev. month] Avril" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:27 -msgctxt "abbrev. month" -msgid "May" -msgstr "[abbrev. month] Mai" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:28 -msgctxt "abbrev. month" -msgid "June" -msgstr "[abbrev. month] Juin" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:29 -msgctxt "abbrev. month" -msgid "July" -msgstr "[abbrev. month] Jui" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:30 -msgctxt "abbrev. month" -msgid "Aug." -msgstr "[abbrev. month] Août." - -#: env/lib/python3.8/site-packages/django/utils/dates.py:31 -msgctxt "abbrev. month" -msgid "Sept." -msgstr "[abbrev. month] Sep." - -#: env/lib/python3.8/site-packages/django/utils/dates.py:32 -msgctxt "abbrev. month" -msgid "Oct." -msgstr "[abbrev. month] Oct." - -#: env/lib/python3.8/site-packages/django/utils/dates.py:33 -msgctxt "abbrev. month" -msgid "Nov." -msgstr "[abbrev. month] Nov." - -#: env/lib/python3.8/site-packages/django/utils/dates.py:34 -msgctxt "abbrev. month" -msgid "Dec." -msgstr "[abbrev. month] Dec." - -#: env/lib/python3.8/site-packages/django/utils/dates.py:37 -msgctxt "alt. month" -msgid "January" -msgstr "[alt. month] Janvier" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:38 -msgctxt "alt. month" -msgid "February" -msgstr "[alt. month] Février" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:39 -msgctxt "alt. month" -msgid "March" -msgstr "[alt. month] Mars" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:40 -msgctxt "alt. month" -msgid "April" -msgstr "[alt. month] Avril" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:41 -msgctxt "alt. month" -msgid "May" -msgstr "[alt. month] Mai" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:42 -msgctxt "alt. month" -msgid "June" -msgstr "[alt. month] Juin" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:43 -msgctxt "alt. month" -msgid "July" -msgstr "[alt. month] Juillet" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:44 -msgctxt "alt. month" -msgid "August" -msgstr "[alt. month] Août" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:45 -msgctxt "alt. month" -msgid "September" -msgstr "[alt. month] Septembre" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:46 -msgctxt "alt. month" -msgid "October" -msgstr "[alt. month] Octobre" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:47 -msgctxt "alt. month" -msgid "November" -msgstr "[alt. month] Novembre" - -#: env/lib/python3.8/site-packages/django/utils/dates.py:48 -msgctxt "alt. month" -msgid "December" -msgstr "[alt. month] Décembre" - -#: env/lib/python3.8/site-packages/django/utils/ipv6.py:8 -msgid "This is not a valid IPv6 address." -msgstr "Adresse IPv6 non valide." - -#: env/lib/python3.8/site-packages/django/utils/text.py:67 -#, python-format -msgctxt "String to return when truncating text" -msgid "%(truncated_text)s…" -msgstr "[String to return when truncating text] %(truncated_text)s…" - -#: env/lib/python3.8/site-packages/django/utils/text.py:233 -msgid "or" -msgstr "ou" - -#. Translators: This string is used as a separator between list elements -#: env/lib/python3.8/site-packages/django/utils/text.py:252 -#: env/lib/python3.8/site-packages/django/utils/timesince.py:83 -msgid ", " -msgstr ", " - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:9 -#, python-format -msgid "%d year" -msgid_plural "%d years" -msgstr[0] "%d année" -msgstr[1] "%d années" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:10 -#, python-format -msgid "%d month" -msgid_plural "%d months" -msgstr[0] "%d mois" -msgstr[1] "%d mois" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:11 -#, python-format -msgid "%d week" -msgid_plural "%d weeks" -msgstr[0] "%d semaine" -msgstr[1] "%d semaines" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:12 -#, python-format -msgid "%d day" -msgid_plural "%d days" -msgstr[0] "%d jour" -msgstr[1] "%d jours" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:13 -#, python-format -msgid "%d hour" -msgid_plural "%d hours" -msgstr[0] "%d heure" -msgstr[1] "%d heures" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:14 -#, python-format -msgid "%d minute" -msgid_plural "%d minutes" -msgstr[0] "%d minute" -msgstr[1] "%d minutes" - -#: env/lib/python3.8/site-packages/django/utils/timesince.py:72 -msgid "0 minutes" -msgstr "0 minutes" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:110 -msgid "Forbidden" -msgstr "Interdit" - -#: env/lib/python3.8/site-packages/django/views/csrf.py:111 -msgid "CSRF verification failed. Request aborted." -msgstr "Echec de la vérification CSRF. Requête interrompue." - -#: env/lib/python3.8/site-packages/django/views/csrf.py:115 -msgid "" -"You are seeing this message because this HTTPS site requires a 'Referer " -"header' to be sent by your Web browser, but none was sent. This header is " -"required for security reasons, to ensure that your browser is not being " -"hijacked by third parties." -msgstr "" -"Vous voyez ce message car ce site HTTPS nécessite l'envoi d'un \"en-tête " -"référent\" par votre navigateur. Cet en-tête est necessaire pour des raisons " -"de sécurité, pour confirmer que votre navigateur n'est pas compromis par un " -"tiers." - -#: env/lib/python3.8/site-packages/django/views/csrf.py:120 -msgid "" -"If you have configured your browser to disable 'Referer' headers, please re-" -"enable them, at least for this site, or for HTTPS connections, or for 'same-" -"origin' requests." -msgstr "" -"Si vous avez configurer votre navigateur pour désactiver les \"en-tête " -"référent\", veuillez les réactiver, au moins pour ce site, ou pour les " -"requêtes HTTPS, ou les requête de même origine." - -#: env/lib/python3.8/site-packages/django/views/csrf.py:124 -msgid "" -"If you are using the tag or " -"including the 'Referrer-Policy: no-referrer' header, please remove them. The " -"CSRF protection requires the 'Referer' header to do strict referer checking. " -"If you're concerned about privacy, use alternatives like for links to third-party sites." -msgstr "" -"Si vous utiliser le tag ou " -"incluez l'en-tête 'Referrer-Policy: no-referrer', veuillez les retirer. La " -"protection CSRF nécessite que l'en-tête référent fasse une vérification " -"stricte. Si vous avez des préoccupations pour votre vie privée, vous pouvez " -"utiliser des alternatives comme pour les liens " -"vers des sites tiers." - -#: env/lib/python3.8/site-packages/django/views/csrf.py:132 -msgid "" -"You are seeing this message because this site requires a CSRF cookie when " -"submitting forms. This cookie is required for security reasons, to ensure " -"that your browser is not being hijacked by third parties." -msgstr "" -"Vous voyez ce message car ce site nécessite un cookie CSRF pour l'envoi de " -"formulaire. Ce cookie est demandé pour des raisons de sécurité, pour " -"s'assurer que votre navigateur n'est pas compromis par un tiers." - -#: env/lib/python3.8/site-packages/django/views/csrf.py:137 -msgid "" -"If you have configured your browser to disable cookies, please re-enable " -"them, at least for this site, or for 'same-origin' requests." -msgstr "" -"Si vous avez configuré votre navigateur pour désactiver les cookies, " -"veuillez les réactiver, au moins pour ce site, ou pour les requêtes de même " -"origine." - -#: env/lib/python3.8/site-packages/django/views/csrf.py:142 -msgid "More information is available with DEBUG=True." -msgstr "Plus d'informations disponible avec DEBUG=True." - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:41 -msgid "No year specified" -msgstr "Pas d'année spécifiée" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:61 -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:111 -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:208 -msgid "Date out of range" -msgstr "Date impossible" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:90 -msgid "No month specified" -msgstr "Pas de mois spécifié" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:142 -msgid "No day specified" -msgstr "Pas de jours spécifié" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:188 -msgid "No week specified" -msgstr "Pas de semaine spécifiée" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:338 -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:367 -#, python-format -msgid "No %(verbose_name_plural)s available" -msgstr "%(verbose_name_plural)s non disponible" - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:589 -#, python-format -msgid "" -"Future %(verbose_name_plural)s not available because %(class_name)s." -"allow_future is False." -msgstr "" -"Le futur %(verbose_name_plural)s n'est pas possible car %(class_name)s." -"allow_future = False." - -#: env/lib/python3.8/site-packages/django/views/generic/dates.py:623 -#, python-format -msgid "Invalid date string '%(datestr)s' given format '%(format)s'" -msgstr "Date '%(datestr)s' au format non valide '%(format)s'." - -#: env/lib/python3.8/site-packages/django/views/generic/detail.py:54 -#, python-format -msgid "No %(verbose_name)s found matching the query" -msgstr "Aucun %(verbose_name)s trouvé pour cette requête" - -#: env/lib/python3.8/site-packages/django/views/generic/list.py:67 -msgid "Page is not 'last', nor can it be converted to an int." -msgstr "" -"La page spécifié n'est pas la dernière (last) et ne peux pas être convertie " -"en nombre entier." - -#: env/lib/python3.8/site-packages/django/views/generic/list.py:72 -#, python-format -msgid "Invalid page (%(page_number)s): %(message)s" -msgstr "Le numéro de page %(page_number)s est non valide: %(message)s" - -#: env/lib/python3.8/site-packages/django/views/generic/list.py:154 -#, python-format -msgid "Empty list and '%(class_name)s.allow_empty' is False." -msgstr "Liste vide et '%(class_name)s.allow_empty = False." - -#: env/lib/python3.8/site-packages/django/views/static.py:40 -msgid "Directory indexes are not allowed here." -msgstr "L'index de Dossier n'est pas autorisé ici." - -#: env/lib/python3.8/site-packages/django/views/static.py:42 -#, python-format -msgid "\"%(path)s\" does not exist" -msgstr "\"%(path)s\" n'existe pas" - -#: env/lib/python3.8/site-packages/django/views/static.py:80 -#, python-format -msgid "Index of %(directory)s" -msgstr "Contenu de %(directory)s" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:6 -msgid "Django: the Web framework for perfectionists with deadlines." -msgstr "" -"Django: Le cadriciel Web pour les perfectionniste avec des dates limites." - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:345 -#, python-format -msgid "" -"View release notes for Django %(version)s" -msgstr "" -"Visiter les Notes de versions\" pour " -"Django %(version)s" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:367 -msgid "The install worked successfully! Congratulations!" -msgstr "L'installation a réussie! Félicitation!" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:368 -#, python-format -msgid "" -"You are seeing this page because DEBUG=True is in your settings file and you have not configured any " -"URLs." -msgstr "" -"Vous voyez cette page car DEBUG=True est renseigné dans vos paramètres et vous n'avez pas " -"configuré d'URLs." - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:383 -msgid "Django Documentation" -msgstr "Documentation Django" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:384 -msgid "Topics, references, & how-to's" -msgstr "Sujet, references & how-to" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:395 -msgid "Tutorial: A Polling App" -msgstr "Tutoriel: Une application de sondage" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:396 -msgid "Get started with Django" -msgstr "Débuter avec Django" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:407 -msgid "Django Community" -msgstr "Communauté Django" - -#: env/lib/python3.8/site-packages/django/views/templates/default_urlconf.html:408 -msgid "Connect, get help, or contribute" -msgstr "Prendre contact, obtenir de l'aide ou contribuer" - #: note_kfet/settings/base.py:155 msgid "German" msgstr "Allemand" @@ -4348,3 +3106,914 @@ msgstr "" "vous connecter. Vous devez vous rendre à la Kfet et payer les frais " "d'adhésion. Vous devez également valider votre adresse email en suivant le " "lien que vous avez reçu." + +#~ msgid "Enter a valid color." +#~ msgstr "Entrer une couleur valide." + +#~ msgid "i18n text" +#~ msgstr "i18n text" + +#~ msgid "i18n legend" +#~ msgstr "i18n legend" + +#~ msgid "Enter a valid value." +#~ msgstr "Entrer une valeur correcte." + +#~ msgid "Messages" +#~ msgstr "Messages" + +#~ msgid "Site Maps" +#~ msgstr "Plan du site" + +#~ msgid "Static Files" +#~ msgstr "Fichiers statiques" + +#~ msgid "Syndication" +#~ msgstr "Invitation" + +#~ msgid "That page number is not an integer" +#~ msgstr "Ce numéro de page n'est pas entier" + +#~ msgid "That page number is less than 1" +#~ msgstr "Ce numéro de page est inférieur à 1" + +#~ msgid "That page contains no results" +#~ msgstr "Il n'y a pas de résultat" + +#~ msgid "Enter a valid URL." +#~ msgstr "Entrer une URL valide." + +#~ msgid "Enter a valid integer." +#~ msgstr "Entrer un entier valid." + +#~ msgid "Enter a valid email address." +#~ msgstr "Entrer une adresse mail valide." + +#~ msgid "" +#~ "Enter a valid 'slug' consisting of letters, numbers, underscores or " +#~ "hyphens." +#~ msgstr "" +#~ "Entrer un 'slug' valide, constitué de lettres, chiffres, tirets ou tirets " +#~ "bas." + +#~ msgid "" +#~ "Enter a valid 'slug' consisting of Unicode letters, numbers, underscores, " +#~ "or hyphens." +#~ msgstr "" +#~ "Entrer un 'slug' valide, constitué de caractère unicode, chiffres, tirets " +#~ "ou underscores." + +#~ msgid "Enter a valid IPv4 address." +#~ msgstr "Entrer une adresse IPv4 valide." + +#~ msgid "Enter a valid IPv6 address." +#~ msgstr "Entrer une adresse IPv6 valide." + +#~ msgid "Enter a valid IPv4 or IPv6 address." +#~ msgstr "Entrer une adresse IPv4 ou IPv6 valide." + +#~ msgid "Enter only digits separated by commas." +#~ msgstr "Entrer seulement des chiffres séparés par des virgules." + +#~ msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +#~ msgstr "" +#~ "Vérifier que cette valeur est %(limit_value)s (actuellement " +#~ "%(show_value)s)." + +#~ msgid "Ensure this value is less than or equal to %(limit_value)s." +#~ msgstr "Vérifier que cette valeur est plus petite que %(limit_value)s." + +#~ msgid "Ensure this value is greater than or equal to %(limit_value)s." +#~ msgstr "Vérifier que cette valeur est plus grande que %(limit_value)s." + +#~ msgid "" +#~ "Ensure this value has at least %(limit_value)d character (it has " +#~ "%(show_value)d)." +#~ msgid_plural "" +#~ "Ensure this value has at least %(limit_value)d characters (it has " +#~ "%(show_value)d)." +#~ msgstr[0] "" +#~ "Vérifier que cette valeur a au moins %(limit_value)d caractère " +#~ "(actuellement %(show_value)d)." +#~ msgstr[1] "" +#~ "Assurer vous que cette valeur a au moins %(limit_value)d caractères " +#~ "(actuellement %(show_value)d)." + +#~ msgid "" +#~ "Ensure this value has at most %(limit_value)d character (it has " +#~ "%(show_value)d)." +#~ msgid_plural "" +#~ "Ensure this value has at most %(limit_value)d characters (it has " +#~ "%(show_value)d)." +#~ msgstr[0] "" +#~ "Vérifier que cette valeur a au plus %(limit_value)d caractère " +#~ "(actuellement %(show_value)d)." +#~ msgstr[1] "" +#~ "Assurer vous que cette valeur a au plus %(limit_value)d caractères " +#~ "(actuellement %(show_value)d)." + +#~ msgid "Enter a number." +#~ msgstr "Numéro de téléphone." + +#~ msgid "Ensure that there are no more than %(max)s digit in total." +#~ msgid_plural "Ensure that there are no more than %(max)s digits in total." +#~ msgstr[0] "Vérifier qu'il n'y a pas plus de %(max)s chiffre au total." +#~ msgstr[1] "Vérifier qu'il n'y a pas plus de %(max)s chiffres au total." + +#~ msgid "Ensure that there are no more than %(max)s decimal place." +#~ msgid_plural "Ensure that there are no more than %(max)s decimal places." +#~ msgstr[0] "Vérifier qu'il y n'as pas plus de %(max)s chiffre décimal." +#~ msgstr[1] "Vérifier qu'il y n'as pas plus de %(max)s chiffres décimaux." + +#~ msgid "" +#~ "Ensure that there are no more than %(max)s digit before the decimal point." +#~ msgid_plural "" +#~ "Ensure that there are no more than %(max)s digits before the decimal " +#~ "point." +#~ msgstr[0] "" +#~ "Vérifier qu'il y n'as pas plus d'%(max)s chiffre avant la virgule." +#~ msgstr[1] "" +#~ "Vérifier qu'il y n'as pas plus de %(max)s chiffres avant la virgule." + +#~ msgid "" +#~ "File extension '%(extension)s' is not allowed. Allowed extensions are: " +#~ "'%(allowed_extensions)s'." +#~ msgstr "" +#~ "Les fichiers d'extension '%(extension)s' ne sont pas autorisé. Les " +#~ "extension autorisées sont: \"%(allowed_extensions)s'." + +#~ msgid "Null characters are not allowed." +#~ msgstr "Les caractères nuls ne sont pas autorisés." + +#~ msgid "and" +#~ msgstr "et" + +#~ msgid "%(model_name)s with this %(field_labels)s already exists." +#~ msgstr "Un %(model_name)s avec ce %(field_labels)s existe déjà." + +#~ msgid "Value %(value)r is not a valid choice." +#~ msgstr "Le choix %(value)r n'est pas possible." + +#~ msgid "This field cannot be null." +#~ msgstr "Ce champ est requis." + +#~ msgid "This field cannot be blank." +#~ msgstr "Ce champ est requis." + +#~ msgid "%(model_name)s with this %(field_label)s already exists." +#~ msgstr "Un %(model_name)s avec ce %(field_label)s existe déjà." + +#~ msgid "" +#~ "%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +#~ msgstr "" +#~ "%(field_label)s doit être unique pour %(date_field_label)s " +#~ "%(lookup_type)s." + +#~ msgid "Field of type: %(field_type)s" +#~ msgstr "Champ du type %(field_type)s" + +#~ msgid "Integer" +#~ msgstr "Nombre entier" + +#~ msgid "'%(value)s' value must be an integer." +#~ msgstr "'%(value)s' doit être un nombre entier." + +#~ msgid "Big (8 byte) integer" +#~ msgstr "Gros nombre entier (8 octets)" + +#~ msgid "'%(value)s' value must be either True or False." +#~ msgstr "'%(value)s' doit être Vrai ou Faux." + +#~ msgid "'%(value)s' value must be either True, False, or None." +#~ msgstr "'%(value)s' doit être Vrai, Faux, ou None." + +#~ msgid "Boolean (Either True or False)" +#~ msgstr "Booléen (Vrai ou Faux)" + +#~ msgid "String (up to %(max_length)s)" +#~ msgstr "Chaîne de caractère (maximum %(max_length)s caractères)" + +#~ msgid "Comma-separated integers" +#~ msgstr "Liste d'entier séparer par des virgules" + +#~ msgid "" +#~ "'%(value)s' value has an invalid date format. It must be in YYYY-MM-DD " +#~ "format." +#~ msgstr "'%(value)s n'est pas formaté correctement (AAAA-MM-JJ)." + +#~ msgid "" +#~ "'%(value)s' value has the correct format (YYYY-MM-DD) but it is an " +#~ "invalid date." +#~ msgstr "" +#~ "'%(value)s possède le format date requis (AAAA-MM-JJ) mais n'est pas une " +#~ "date valide." + +#~ msgid "Date (without time)" +#~ msgstr "Date (sans horaire)" + +#~ msgid "" +#~ "'%(value)s' value has an invalid format. It must be in YYYY-MM-DD HH:MM[:" +#~ "ss[.uuuuuu]][TZ] format." +#~ msgstr "" +#~ "'%(value)s' n'est pas formatée correctement (AAAA-MM-JJ HH:MM[:ss[." +#~ "uuuuuu]][TZ])." + +#~ msgid "" +#~ "'%(value)s' value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +#~ "[TZ]) but it is an invalid date/time." +#~ msgstr "" +#~ "'%(value)s' est formaté correctement, mais n'est pas une date/horaire " +#~ "valide." + +#~ msgid "Date (with time)" +#~ msgstr "Date (avec horaire)" + +#~ msgid "'%(value)s' value must be a decimal number." +#~ msgstr "'%(value)s doit être un nombre décimal." + +#~ msgid "Decimal number" +#~ msgstr "Nombre décimal" + +#~ msgid "" +#~ "'%(value)s' value has an invalid format. It must be in [DD] [HH:[MM:]]ss[." +#~ "uuuuuu] format." +#~ msgstr "" +#~ "'%(value)s' n'est pas formatée correctement (AAAA-MM-JJ HH:MM[:ss[." +#~ "uuuuuu]][TZ])." + +#~ msgid "Duration" +#~ msgstr "Durée" + +#~ msgid "Email address" +#~ msgstr "Courriel" + +#~ msgid "File path" +#~ msgstr "Chemin du fichier" + +#~ msgid "'%(value)s' value must be a float." +#~ msgstr "'%(value)s doit être un nombre décimal." + +#~ msgid "Floating point number" +#~ msgstr "Nombre décimal" + +#~ msgid "IPv4 address" +#~ msgstr "Adresse IPv4" + +#~ msgid "IP address" +#~ msgstr "Adresse IP" + +#~ msgid "'%(value)s' value must be either None, True or False." +#~ msgstr "'%(value)s' doit être Vrai, Faux, ou None." + +#~ msgid "Boolean (Either True, False or None)" +#~ msgstr "Booléen (Vrai ou Faux)" + +#~ msgid "Positive integer" +#~ msgstr "Nombre entier positif" + +#~ msgid "Positive small integer" +#~ msgstr "Nombre entier positif petit" + +#~ msgid "Slug (up to %(max_length)s)" +#~ msgstr "Slug (maximum %(max_length)s caractères)" + +#~ msgid "Small integer" +#~ msgstr "Petit entier" + +#~ msgid "Text" +#~ msgstr "Texte" + +#~ msgid "" +#~ "'%(value)s' value has an invalid format. It must be in HH:MM[:ss[." +#~ "uuuuuu]] format." +#~ msgstr "'%(value)s' n'est pas formatée correctement (HH:MM[:ss[.uuuuuu]])." + +#~ msgid "" +#~ "'%(value)s' value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is " +#~ "an invalid time." +#~ msgstr "" +#~ "'%(value)s' est formaté correctement, mais n'est pas un horaire valide." + +#~ msgid "Time" +#~ msgstr "Temps" + +#~ msgid "URL" +#~ msgstr "URL" + +#~ msgid "Raw binary data" +#~ msgstr "Donnée binaire brute" + +#~ msgid "'%(value)s' is not a valid UUID." +#~ msgstr "'%(value)s' n'est pas un UUID valide." + +#~ msgid "Universally unique identifier" +#~ msgstr "Identifiant unique universel" + +#~ msgid "File" +#~ msgstr "Fichier" + +#~ msgid "Image" +#~ msgstr "Image" + +#~ msgid "%(model)s instance with %(field)s %(value)r does not exist." +#~ msgstr "" +#~ "l'objet %(model)s avec les champs %(field)s %(value)r n'existe pas." + +#~ msgid "Foreign Key (type determined by related field)" +#~ msgstr "Relations plusieurs-à-un (type déterminé par le champ relié)" + +#~ msgid "One-to-one relationship" +#~ msgstr "Relation un-à-un" + +#~ msgid "%(from)s-%(to)s relationship" +#~ msgstr "Relation %(from)s-%(to)s" + +#~ msgid "%(from)s-%(to)s relationships" +#~ msgstr "Relations %(from)s-%(to)s" + +#~ msgid "Many-to-many relationship" +#~ msgstr "Relation plusieurs-à-plusieurs" + +#~ msgid ":?.!" +#~ msgstr ":?.!" + +#~ msgid "Enter a whole number." +#~ msgstr "Entrer un numéro complet." + +#~ msgid "Enter a valid date." +#~ msgstr "Entrer une date valide." + +#~ msgid "Enter a valid time." +#~ msgstr "Entrer un horaire valide." + +#~ msgid "Enter a valid date/time." +#~ msgstr "Entrer une date et un horaire valide." + +#~ msgid "Enter a valid duration." +#~ msgstr "Entrer une durée valide." + +#~ msgid "The number of days must be between {min_days} and {max_days}." +#~ msgstr "" +#~ "Le nombre de jours doit être compris entre {min_days} et {max_days}." + +#~ msgid "No file was submitted. Check the encoding type on the form." +#~ msgstr "Aucun fichier n'as été ajouté. Vérifier l'encodage du formulaire." + +#~ msgid "No file was submitted." +#~ msgstr "Aucun fichier n'as été ajouté." + +#~ msgid "The submitted file is empty." +#~ msgstr "Le fichier ajouté est vide." + +#~ msgid "" +#~ "Ensure this filename has at most %(max)d character (it has %(length)d)." +#~ msgid_plural "" +#~ "Ensure this filename has at most %(max)d characters (it has %(length)d)." +#~ msgstr[0] "" +#~ "Vérifier que le nom du fichier a au plus %(max)d caractère (actuellement " +#~ "%(length)d)." +#~ msgstr[1] "" +#~ "Vérifier que le nom du fichier a au plus %(max)d caractères (actuellement " +#~ "%(length)d)." + +#~ msgid "Please either submit a file or check the clear checkbox, not both." +#~ msgstr "Veuillez ajouter un fichier ou cocher la case envoi vide." + +#~ msgid "" +#~ "Upload a valid image. The file you uploaded was either not an image or a " +#~ "corrupted image." +#~ msgstr "Veuillez choisir une image valide." + +#~ msgid "" +#~ "Select a valid choice. %(value)s is not one of the available choices." +#~ msgstr "Faites un choix valide. %(value)s n'est pas autorisé." + +#~ msgid "Enter a list of values." +#~ msgstr "Entrer une liste de valeurs." + +#~ msgid "Enter a complete value." +#~ msgstr "Entrer une valeur complète." + +#~ msgid "Enter a valid UUID." +#~ msgstr "Entrer un UUID valid." + +#~ msgid ":" +#~ msgstr ":" + +#~ msgid "(Hidden field %(name)s) %(error)s" +#~ msgstr "(Champ caché %(name)s) %(error)s" + +#~ msgid "ManagementForm data is missing or has been tampered with" +#~ msgstr "Les données ManagementForm sont manquantes ou falsifiées" + +#~ msgid "Please submit %d or fewer forms." +#~ msgid_plural "Please submit %d or fewer forms." +#~ msgstr[0] "Veuillez remplir %d formulaire ou moins." +#~ msgstr[1] "Veuillez remplir %d formulaires ou moins." + +#~ msgid "Please submit %d or more forms." +#~ msgid_plural "Please submit %d or more forms." +#~ msgstr[0] "Veuillez remplir %d formulaire ou plus." +#~ msgstr[1] "Veuillez remplir %d formulaires ou plus." + +#~ msgid "Order" +#~ msgstr "Ordre" + +#~ msgid "Please correct the duplicate data for %(field)s." +#~ msgstr "Veuillez corriger les données dupliquées pour %(field)s." + +#~ msgid "" +#~ "Please correct the duplicate data for %(field)s, which must be unique." +#~ msgstr "Veuillez rendre uniques les valeurs des champs %(field)s." + +#~ msgid "" +#~ "Please correct the duplicate data for %(field_name)s which must be unique " +#~ "for the %(lookup)s in %(date_field)s." +#~ msgstr "" +#~ "Veuillez rendre unique les valeurs des champs %(field_name)s pour les " +#~ "%(lookup)s dans %(date_field)s." + +#~ msgid "Please correct the duplicate values below." +#~ msgstr "Veuillez rendre uniques les valeurs ci-dessous." + +#~ msgid "The inline value did not match the parent instance." +#~ msgstr "La valeur renseignée ne correspond pas à l'objet parent." + +#~ msgid "" +#~ "Select a valid choice. That choice is not one of the available choices." +#~ msgstr "Faites un choix possible." + +#~ msgid "\"%(pk)s\" is not a valid value." +#~ msgstr "\"%(pk)s\" n'est pas une valeur autorisée." + +#~ msgid "" +#~ "%(datetime)s couldn't be interpreted in time zone %(current_timezone)s; " +#~ "it may be ambiguous or it may not exist." +#~ msgstr "" +#~ "%(datetime)s n'a pas pu être interprété dans la zone " +#~ "%(current_timezone)s, La valeur est ambigüe ou impossible." + +#~ msgid "Clear" +#~ msgstr "Vider" + +#~ msgid "Currently" +#~ msgstr "Activité en cours" + +#~ msgid "Change" +#~ msgstr "Modifier" + +#~ msgid "Unknown" +#~ msgstr "Inconnu" + +#~ msgid "yes,no,maybe" +#~ msgstr "oui, non, peut-être" + +#~ msgid "%(size)d byte" +#~ msgid_plural "%(size)d bytes" +#~ msgstr[0] "%(size)d octet" +#~ msgstr[1] "%(size)d octets" + +#~ msgid "%s KB" +#~ msgstr "%s Ko" + +#~ msgid "%s MB" +#~ msgstr "%s Mo" + +#~ msgid "%s GB" +#~ msgstr "%s Go" + +#~ msgid "%s TB" +#~ msgstr "%s To" + +#~ msgid "%s PB" +#~ msgstr "%s Po" + +#~ msgid "p.m." +#~ msgstr "p.m." + +#~ msgid "a.m." +#~ msgstr "a.m." + +#~ msgid "PM" +#~ msgstr "PM" + +#~ msgid "AM" +#~ msgstr "AM" + +#~ msgid "midnight" +#~ msgstr "minuit" + +#~ msgid "noon" +#~ msgstr "midi" + +#~ msgid "Monday" +#~ msgstr "Lundi" + +#~ msgid "Tuesday" +#~ msgstr "Mardi" + +#~ msgid "Wednesday" +#~ msgstr "Mercredi" + +#~ msgid "Thursday" +#~ msgstr "Jeudi" + +#~ msgid "Friday" +#~ msgstr "Vendredi" + +#~ msgid "Saturday" +#~ msgstr "Samedi" + +#~ msgid "Sunday" +#~ msgstr "Dimanche" + +#~ msgid "Mon" +#~ msgstr "Lun" + +#~ msgid "Tue" +#~ msgstr "Mar" + +#~ msgid "Wed" +#~ msgstr "Mer" + +#~ msgid "Thu" +#~ msgstr "Jeu" + +#~ msgid "Fri" +#~ msgstr "Ven" + +#~ msgid "Sat" +#~ msgstr "Sam" + +#~ msgid "Sun" +#~ msgstr "Dim" + +#~ msgid "January" +#~ msgstr "Janvier" + +#~ msgid "February" +#~ msgstr "Février" + +#~ msgid "March" +#~ msgstr "Mars" + +#~ msgid "April" +#~ msgstr "Avril" + +#~ msgid "May" +#~ msgstr "Mai" + +#~ msgid "June" +#~ msgstr "Juin" + +#~ msgid "July" +#~ msgstr "Juillet" + +#~ msgid "August" +#~ msgstr "Août" + +#~ msgid "September" +#~ msgstr "Septembre" + +#~ msgid "October" +#~ msgstr "Octobre" + +#~ msgid "November" +#~ msgstr "Novembre" + +#~ msgid "December" +#~ msgstr "Décembre" + +#~ msgid "jan" +#~ msgstr "jan" + +#~ msgid "feb" +#~ msgstr "fev" + +#~ msgid "mar" +#~ msgstr "mar" + +#~ msgid "apr" +#~ msgstr "avr" + +#~ msgid "may" +#~ msgstr "mai" + +#~ msgid "jun" +#~ msgstr "jun" + +#~ msgid "jul" +#~ msgstr "jui" + +#~ msgid "aug" +#~ msgstr "aou" + +#~ msgid "sep" +#~ msgstr "sep" + +#~ msgid "oct" +#~ msgstr "oct" + +#~ msgid "nov" +#~ msgstr "nov" + +#~ msgid "dec" +#~ msgstr "dec" + +#~ msgctxt "abbrev. month" +#~ msgid "Jan." +#~ msgstr "[abbrev. month] Jan." + +#~ msgctxt "abbrev. month" +#~ msgid "Feb." +#~ msgstr "[abbrev. month] Fév." + +#~ msgctxt "abbrev. month" +#~ msgid "March" +#~ msgstr "[abbrev. month] Mars" + +#~ msgctxt "abbrev. month" +#~ msgid "April" +#~ msgstr "[abbrev. month] Avril" + +#~ msgctxt "abbrev. month" +#~ msgid "May" +#~ msgstr "[abbrev. month] Mai" + +#~ msgctxt "abbrev. month" +#~ msgid "June" +#~ msgstr "[abbrev. month] Juin" + +#~ msgctxt "abbrev. month" +#~ msgid "July" +#~ msgstr "[abbrev. month] Jui" + +#~ msgctxt "abbrev. month" +#~ msgid "Aug." +#~ msgstr "[abbrev. month] Août." + +#~ msgctxt "abbrev. month" +#~ msgid "Sept." +#~ msgstr "[abbrev. month] Sep." + +#~ msgctxt "abbrev. month" +#~ msgid "Oct." +#~ msgstr "[abbrev. month] Oct." + +#~ msgctxt "abbrev. month" +#~ msgid "Nov." +#~ msgstr "[abbrev. month] Nov." + +#~ msgctxt "abbrev. month" +#~ msgid "Dec." +#~ msgstr "[abbrev. month] Dec." + +#~ msgctxt "alt. month" +#~ msgid "January" +#~ msgstr "[alt. month] Janvier" + +#~ msgctxt "alt. month" +#~ msgid "February" +#~ msgstr "[alt. month] Février" + +#~ msgctxt "alt. month" +#~ msgid "March" +#~ msgstr "[alt. month] Mars" + +#~ msgctxt "alt. month" +#~ msgid "April" +#~ msgstr "[alt. month] Avril" + +#~ msgctxt "alt. month" +#~ msgid "May" +#~ msgstr "[alt. month] Mai" + +#~ msgctxt "alt. month" +#~ msgid "June" +#~ msgstr "[alt. month] Juin" + +#~ msgctxt "alt. month" +#~ msgid "July" +#~ msgstr "[alt. month] Juillet" + +#~ msgctxt "alt. month" +#~ msgid "August" +#~ msgstr "[alt. month] Août" + +#~ msgctxt "alt. month" +#~ msgid "September" +#~ msgstr "[alt. month] Septembre" + +#~ msgctxt "alt. month" +#~ msgid "October" +#~ msgstr "[alt. month] Octobre" + +#~ msgctxt "alt. month" +#~ msgid "November" +#~ msgstr "[alt. month] Novembre" + +#~ msgctxt "alt. month" +#~ msgid "December" +#~ msgstr "[alt. month] Décembre" + +#~ msgid "This is not a valid IPv6 address." +#~ msgstr "Adresse IPv6 non valide." + +#~ msgctxt "String to return when truncating text" +#~ msgid "%(truncated_text)s…" +#~ msgstr "[String to return when truncating text] %(truncated_text)s…" + +#~ msgid "or" +#~ msgstr "ou" + +#~ msgid ", " +#~ msgstr ", " + +#~ msgid "%d year" +#~ msgid_plural "%d years" +#~ msgstr[0] "%d année" +#~ msgstr[1] "%d années" + +#~ msgid "%d month" +#~ msgid_plural "%d months" +#~ msgstr[0] "%d mois" +#~ msgstr[1] "%d mois" + +#~ msgid "%d week" +#~ msgid_plural "%d weeks" +#~ msgstr[0] "%d semaine" +#~ msgstr[1] "%d semaines" + +#~ msgid "%d day" +#~ msgid_plural "%d days" +#~ msgstr[0] "%d jour" +#~ msgstr[1] "%d jours" + +#~ msgid "%d hour" +#~ msgid_plural "%d hours" +#~ msgstr[0] "%d heure" +#~ msgstr[1] "%d heures" + +#~ msgid "%d minute" +#~ msgid_plural "%d minutes" +#~ msgstr[0] "%d minute" +#~ msgstr[1] "%d minutes" + +#~ msgid "0 minutes" +#~ msgstr "0 minutes" + +#~ msgid "Forbidden" +#~ msgstr "Interdit" + +#~ msgid "CSRF verification failed. Request aborted." +#~ msgstr "Echec de la vérification CSRF. Requête interrompue." + +#~ msgid "" +#~ "You are seeing this message because this HTTPS site requires a 'Referer " +#~ "header' to be sent by your Web browser, but none was sent. This header is " +#~ "required for security reasons, to ensure that your browser is not being " +#~ "hijacked by third parties." +#~ msgstr "" +#~ "Vous voyez ce message car ce site HTTPS nécessite l'envoi d'un \"en-tête " +#~ "référent\" par votre navigateur. Cet en-tête est necessaire pour des " +#~ "raisons de sécurité, pour confirmer que votre navigateur n'est pas " +#~ "compromis par un tiers." + +#~ msgid "" +#~ "If you have configured your browser to disable 'Referer' headers, please " +#~ "re-enable them, at least for this site, or for HTTPS connections, or for " +#~ "'same-origin' requests." +#~ msgstr "" +#~ "Si vous avez configurer votre navigateur pour désactiver les \"en-tête " +#~ "référent\", veuillez les réactiver, au moins pour ce site, ou pour les " +#~ "requêtes HTTPS, ou les requête de même origine." + +#~ msgid "" +#~ "If you are using the tag " +#~ "or including the 'Referrer-Policy: no-referrer' header, please remove " +#~ "them. The CSRF protection requires the 'Referer' header to do strict " +#~ "referer checking. If you're concerned about privacy, use alternatives " +#~ "like for links to third-party sites." +#~ msgstr "" +#~ "Si vous utiliser le tag " +#~ "ou incluez l'en-tête 'Referrer-Policy: no-referrer', veuillez les " +#~ "retirer. La protection CSRF nécessite que l'en-tête référent fasse une " +#~ "vérification stricte. Si vous avez des préoccupations pour votre vie " +#~ "privée, vous pouvez utiliser des alternatives comme pour les liens vers des sites tiers." + +#~ msgid "" +#~ "You are seeing this message because this site requires a CSRF cookie when " +#~ "submitting forms. This cookie is required for security reasons, to ensure " +#~ "that your browser is not being hijacked by third parties." +#~ msgstr "" +#~ "Vous voyez ce message car ce site nécessite un cookie CSRF pour l'envoi " +#~ "de formulaire. Ce cookie est demandé pour des raisons de sécurité, pour " +#~ "s'assurer que votre navigateur n'est pas compromis par un tiers." + +#~ msgid "" +#~ "If you have configured your browser to disable cookies, please re-enable " +#~ "them, at least for this site, or for 'same-origin' requests." +#~ msgstr "" +#~ "Si vous avez configuré votre navigateur pour désactiver les cookies, " +#~ "veuillez les réactiver, au moins pour ce site, ou pour les requêtes de " +#~ "même origine." + +#~ msgid "More information is available with DEBUG=True." +#~ msgstr "Plus d'informations disponible avec DEBUG=True." + +#~ msgid "No year specified" +#~ msgstr "Pas d'année spécifiée" + +#~ msgid "Date out of range" +#~ msgstr "Date impossible" + +#~ msgid "No month specified" +#~ msgstr "Pas de mois spécifié" + +#~ msgid "No day specified" +#~ msgstr "Pas de jours spécifié" + +#~ msgid "No week specified" +#~ msgstr "Pas de semaine spécifiée" + +#~ msgid "No %(verbose_name_plural)s available" +#~ msgstr "%(verbose_name_plural)s non disponible" + +#~ msgid "" +#~ "Future %(verbose_name_plural)s not available because %(class_name)s." +#~ "allow_future is False." +#~ msgstr "" +#~ "Le futur %(verbose_name_plural)s n'est pas possible car %(class_name)s." +#~ "allow_future = False." + +#~ msgid "Invalid date string '%(datestr)s' given format '%(format)s'" +#~ msgstr "Date '%(datestr)s' au format non valide '%(format)s'." + +#~ msgid "No %(verbose_name)s found matching the query" +#~ msgstr "Aucun %(verbose_name)s trouvé pour cette requête" + +#~ msgid "Page is not 'last', nor can it be converted to an int." +#~ msgstr "" +#~ "La page spécifié n'est pas la dernière (last) et ne peux pas être " +#~ "convertie en nombre entier." + +#~ msgid "Invalid page (%(page_number)s): %(message)s" +#~ msgstr "Le numéro de page %(page_number)s est non valide: %(message)s" + +#~ msgid "Empty list and '%(class_name)s.allow_empty' is False." +#~ msgstr "Liste vide et '%(class_name)s.allow_empty = False." + +#~ msgid "Directory indexes are not allowed here." +#~ msgstr "L'index de Dossier n'est pas autorisé ici." + +#~ msgid "\"%(path)s\" does not exist" +#~ msgstr "\"%(path)s\" n'existe pas" + +#~ msgid "Index of %(directory)s" +#~ msgstr "Contenu de %(directory)s" + +#~ msgid "Django: the Web framework for perfectionists with deadlines." +#~ msgstr "" +#~ "Django: Le cadriciel Web pour les perfectionniste avec des dates limites." + +#~ msgid "" +#~ "View release notes for Django " +#~ "%(version)s" +#~ msgstr "" +#~ "Visiter les Notes de versions\" pour " +#~ "Django %(version)s" + +#~ msgid "The install worked successfully! Congratulations!" +#~ msgstr "L'installation a réussie! Félicitation!" + +#~ msgid "" +#~ "You are seeing this page because DEBUG=True is in your settings file and you have not configured " +#~ "any URLs." +#~ msgstr "" +#~ "Vous voyez cette page car DEBUG=True est renseigné dans vos paramètres et vous n'avez pas " +#~ "configuré d'URLs." + +#~ msgid "Django Documentation" +#~ msgstr "Documentation Django" + +#~ msgid "Topics, references, & how-to's" +#~ msgstr "Sujet, references & how-to" + +#~ msgid "Tutorial: A Polling App" +#~ msgstr "Tutoriel: Une application de sondage" + +#~ msgid "Get started with Django" +#~ msgstr "Débuter avec Django" + +#~ msgid "Django Community" +#~ msgstr "Communauté Django" + +#~ msgid "Connect, get help, or contribute" +#~ msgstr "Prendre contact, obtenir de l'aide ou contribuer" From fed95675222e6e5a9e26a4ac86d153fa324f4a4e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 2 Sep 2020 23:49:10 +0200 Subject: [PATCH 20/41] Force line breaks on transactions reason in history, but don't wrap dates or amounts --- apps/note/models/transactions.py | 2 +- apps/note/tables.py | 34 +++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/apps/note/models/transactions.py b/apps/note/models/transactions.py index d88be5a6..a4f220bd 100644 --- a/apps/note/models/transactions.py +++ b/apps/note/models/transactions.py @@ -356,4 +356,4 @@ class MembershipTransaction(Transaction): @property def type(self): - return _('membership transaction') + return _('membership').capitalize() diff --git a/apps/note/tables.py b/apps/note/tables.py index b1d434ae..12ec58a9 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -29,6 +29,7 @@ class HistoryTable(tables.Table): source = tables.Column( attrs={ "td": { + "class": "text-nowrap", "data-toggle": "tooltip", "title": lambda record: _("used alias").capitalize() + " : " + record.source_alias, } @@ -38,15 +39,46 @@ class HistoryTable(tables.Table): destination = tables.Column( attrs={ "td": { + "class": "text-nowrap", "data-toggle": "tooltip", "title": lambda record: _("used alias").capitalize() + " : " + record.destination_alias, } } ) + created_at = tables.DateColumn( + attrs={ + "td": { + "class": "text-nowrap", + }, + } + ) + + amount = tables.Column( + attrs={ + "td": { + "class": "text-nowrap", + }, + } + ) + + reason = tables.Column( + attrs={ + "td": { + "class": "text-break", + }, + } + ) + type = tables.Column() - total = tables.Column() # will use Transaction.total() !! + total = tables.Column( # will use Transaction.total() !! + attrs={ + "td": { + "class": "text-nowrap", + }, + } + ) valid = tables.Column( attrs={ From 42778baf20519d3f77de18989778c41a94718d8f Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Thu, 3 Sep 2020 00:02:45 +0200 Subject: [PATCH 21/41] =?UTF-8?q?51%=20der=20=C3=9Cbersetzung,=20Sandm?= =?UTF-8?q?=C3=A4nnchen=20ruf=20an.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locale/de/LC_MESSAGES/django.po | 134 +++++++++++++++++--------------- 1 file changed, 72 insertions(+), 62 deletions(-) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 63f5a02e..9a8e4ad3 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-09-02 23:17+0200\n" -"PO-Revision-Date: 2020-09-02 23:26+0200\n" +"PO-Revision-Date: 2020-09-03 00:02+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: de\n" @@ -525,7 +525,7 @@ msgstr "Kein Kredit" #: apps/member/forms.py:120 msgid "You can credit the note of the user." -msgstr "" +msgstr "Sie dûrfen diese Note kreditieren." #: apps/member/forms.py:124 apps/registration/forms.py:74 #: apps/wei/forms/registration.py:89 @@ -738,6 +738,8 @@ msgstr "erfordern Mitgliedschaft" #: apps/member/models.py:220 msgid "Uncheck if this club don't require memberships." msgstr "" +"Deaktivieren Sie diese Option, wenn für diesen Club keine Mitgliedschaft " +"erforderlich ist." #: apps/member/models.py:236 #: apps/member/templates/member/includes/club_info.html:26 @@ -765,6 +767,7 @@ msgstr "Mitgliedschaftenddatum" #: apps/member/models.py:252 msgid "Maximal date of a membership, after which members must renew it." msgstr "" +"Maximales Datum einer Mitgliedschaft, nach dem Mitglieder es erneuern müssen." #: apps/member/models.py:284 apps/member/models.py:309 #: apps/note/models/notes.py:179 @@ -1279,11 +1282,11 @@ msgstr "Transaktionkategorien" #: apps/note/models/transactions.py:49 msgid "A template with this name already exist" -msgstr "" +msgstr "Eine Vorlage mit diesem Namen ist bereits vorhanden" #: apps/note/models/transactions.py:72 msgid "display" -msgstr "" +msgstr "Schauen" #: apps/note/models/transactions.py:77 msgid "highlighted" @@ -1291,37 +1294,37 @@ msgstr "" #: apps/note/models/transactions.py:87 msgid "transaction template" -msgstr "" +msgstr "Transaktionsvorlage" #: apps/note/models/transactions.py:88 msgid "transaction templates" -msgstr "" +msgstr "Transaktionsvorlagen" #: apps/note/models/transactions.py:112 apps/note/models/transactions.py:125 #: apps/note/tables.py:33 apps/note/tables.py:42 msgid "used alias" -msgstr "" +msgstr "benutzte Aliasen" #: apps/note/models/transactions.py:133 msgid "quantity" -msgstr "" +msgstr "Anzahl" #: apps/note/models/transactions.py:141 msgid "reason" -msgstr "" +msgstr "Grund" #: apps/note/models/transactions.py:151 apps/note/tables.py:107 msgid "invalidity reason" -msgstr "" +msgstr "Ungültigkeit Grund" #: apps/note/models/transactions.py:159 msgid "transaction" -msgstr "" +msgstr "Transaktion" #: apps/note/models/transactions.py:160 #: apps/treasury/templates/treasury/sogecredit_detail.html:22 msgid "transactions" -msgstr "" +msgstr "Transaktionen" #: apps/note/models/transactions.py:182 #, python-brace-format @@ -1329,72 +1332,79 @@ msgid "" "You can't update the {field} on a Transaction. Please invalidate it and " "create one other." msgstr "" +"Sie können das {field} einer Transaktion nicht aktualisieren. Bitte machen " +"Sie es ungültig und erstellen Sie eine andere." #: apps/note/models/transactions.py:202 msgid "" "The note balances must be between - 92 233 720 368 547 758.08 € and 92 233 " "720 368 547 758.07 €." msgstr "" +"Die Notenguthaben müssen zwischen - 92 233 720 368 547 758,08 € und 92 233 " +"720 368 547 758,07 € liegen." #: apps/note/models/transactions.py:279 msgid "" "The destination of this transaction must equal to the destination of the " "template." msgstr "" +"Der Empfänger dieser Transaktion muss dem Empfänger der Vorlage entsprechen." #: apps/note/models/transactions.py:288 msgid "Template" -msgstr "" +msgstr "Vorlage" #: apps/note/models/transactions.py:291 msgid "recurrent transaction" -msgstr "" +msgstr "wiederkehrende Transaktion" #: apps/note/models/transactions.py:292 msgid "recurrent transactions" -msgstr "" +msgstr "wiederkehrende Transaktionen" #: apps/note/models/transactions.py:307 msgid "first_name" -msgstr "" +msgstr "Vorname" #: apps/note/models/transactions.py:312 msgid "bank" -msgstr "" +msgstr "Bank" #: apps/note/models/transactions.py:329 msgid "" "A special transaction is only possible between a Note associated to a " "payment method and a User or a Club" msgstr "" +"Eine Sondertransaktion ist nur zwischen einer Note, die einer " +"Zahlungsmethode zugeordnet ist, und einem User oder einem Club möglich" #: apps/note/models/transactions.py:337 msgid "Special transaction" -msgstr "" +msgstr "Sondertransaktion" #: apps/note/models/transactions.py:338 msgid "Special transactions" -msgstr "" +msgstr "Sondertranskationen" #: apps/note/models/transactions.py:354 apps/note/models/transactions.py:359 msgid "membership transaction" -msgstr "" +msgstr "Mitgliedschafttransaktion" #: apps/note/models/transactions.py:355 apps/treasury/models.py:273 msgid "membership transactions" -msgstr "" +msgstr "Mitgliedschaftttransaktionen" #: apps/note/tables.py:61 msgid "Click to invalidate" -msgstr "" +msgstr "Klicken Sie zum Ungültigmachen" #: apps/note/tables.py:61 msgid "Click to validate" -msgstr "" +msgstr "Klicken Sie zum gültigmachen" #: apps/note/tables.py:105 msgid "No reason specified" -msgstr "" +msgstr "Kein Grund gegeben" #: apps/note/tables.py:136 apps/note/tables.py:170 apps/treasury/tables.py:39 #: apps/treasury/templates/treasury/invoice_confirm_delete.html:30 @@ -1402,7 +1412,7 @@ msgstr "" #: apps/wei/tables.py:76 apps/wei/tables.py:103 #: apps/wei/templates/wei/weiregistration_confirm_delete.html:31 msgid "Delete" -msgstr "" +msgstr "Löschen" #: apps/note/tables.py:164 apps/note/templates/note/conso_form.html:132 #: apps/wei/tables.py:47 apps/wei/tables.py:48 @@ -1411,42 +1421,42 @@ msgstr "" #: apps/wei/templates/wei/busteam_detail.html:20 #: apps/wei/templates/wei/busteam_detail.html:40 msgid "Edit" -msgstr "" +msgstr "Bearbeiten" #: apps/note/templates/note/conso_form.html:32 msgid "Consum" -msgstr "" +msgstr "konsumieren" #: apps/note/templates/note/conso_form.html:43 #: apps/note/templates/note/transaction_form.html:65 #: apps/note/templates/note/transaction_form.html:92 msgid "Name or alias..." -msgstr "" +msgstr "Name oder Alias" #: apps/note/templates/note/conso_form.html:52 msgid "Select consumptions" -msgstr "" +msgstr "Verbrauchswerte auswählen" #: apps/note/templates/note/conso_form.html:61 msgid "Consume!" -msgstr "" +msgstr "Konsumieren!" #: apps/note/templates/note/conso_form.html:73 msgid "Highlighted buttons" -msgstr "" +msgstr "Hervorgehobene Tasten" #: apps/note/templates/note/conso_form.html:138 msgid "Single consumptions" -msgstr "" +msgstr "Solo Modus" #: apps/note/templates/note/conso_form.html:143 msgid "Double consumptions" -msgstr "" +msgstr "Doppelte Modus" #: apps/note/templates/note/conso_form.html:154 #: apps/note/templates/note/transaction_form.html:159 msgid "Recent transactions history" -msgstr "" +msgstr " Verlauf der letzten Transaktionen" #: apps/note/templates/note/mails/negative_balance.html:43 #: apps/note/templates/note/mails/negative_balance.txt:25 @@ -1456,109 +1466,109 @@ msgstr "" #: apps/registration/templates/registration/mails/email_validation_email.html:40 #: apps/registration/templates/registration/mails/email_validation_email.txt:16 msgid "Mail generated by the Note Kfet on the" -msgstr "" +msgstr " Mail generiert vom Note Kfet auf dem" #: apps/note/templates/note/transaction_form.html:54 #: apps/note/templates/note/transaction_form.html:174 msgid "Select emitters" -msgstr "" +msgstr "Sender auswählen" #: apps/note/templates/note/transaction_form.html:69 msgid "I am the emitter" -msgstr "" +msgstr "Ich bin der Sender" #: apps/note/templates/note/transaction_form.html:81 #: apps/note/templates/note/transaction_form.html:176 msgid "Select receivers" -msgstr "" +msgstr "Empfänger auswählen" #: apps/note/templates/note/transaction_form.html:104 msgid "Action" -msgstr "" +msgstr "Aktion" #: apps/note/templates/note/transaction_form.html:112 #: apps/treasury/forms.py:141 apps/treasury/tables.py:67 #: apps/treasury/tables.py:135 #: apps/treasury/templates/treasury/remittance_form.html:23 msgid "Amount" -msgstr "" +msgstr "Anzahl" #: apps/note/templates/note/transaction_form.html:128 #: apps/treasury/models.py:51 msgid "Name" -msgstr "" +msgstr "Name" #: apps/note/templates/note/transaction_form.html:173 msgid "Select emitter" -msgstr "" +msgstr "Sender auswählen" #: apps/note/templates/note/transaction_form.html:175 msgid "Select receiver" -msgstr "" +msgstr "Empfänger auswählen" #: apps/note/templates/note/transaction_form.html:177 msgid "Transfer type" -msgstr "" +msgstr "Überweisungtype" #: apps/note/templates/note/transactiontemplate_form.html:8 msgid "Buttons list" -msgstr "" +msgstr "Tastenliste" #: apps/note/templates/note/transactiontemplate_form.html:24 msgid "Price history" -msgstr "" +msgstr "Verlaufene Preisen" #: apps/note/templates/note/transactiontemplate_form.html:27 msgid "Obsolete since" -msgstr "" +msgstr "veraltet seit" #: apps/note/templates/note/transactiontemplate_form.html:27 msgid "Current price" -msgstr "" +msgstr "Aktueller Preis" #: apps/note/templates/note/transactiontemplate_list.html:13 msgid "Name of the button..." -msgstr "" +msgstr "Name dessen Tatsen" #: apps/note/templates/note/transactiontemplate_list.html:15 msgid "New button" -msgstr "" +msgstr "Neue Tatsen" #: apps/note/templates/note/transactiontemplate_list.html:22 msgid "buttons listing " -msgstr "" +msgstr "Tatsenliste" #: apps/note/templates/note/transactiontemplate_list.html:73 msgid "button successfully deleted " -msgstr "" +msgstr "Taste erfolgreich gelöscht" #: apps/note/templates/note/transactiontemplate_list.html:77 msgid "Unable to delete button " -msgstr "" +msgstr "Tatse kann nicht gelöscht werden" #: apps/note/views.py:36 msgid "Transfer money" -msgstr "" +msgstr "Geld überweisen" #: apps/note/views.py:74 msgid "Create new button" -msgstr "" +msgstr "Neue Tatse berstellen" #: apps/note/views.py:83 msgid "Search button" -msgstr "" +msgstr "Tatsen finden" #: apps/note/views.py:111 msgid "Update button" -msgstr "" +msgstr "Tatse bearbeiten" #: apps/note/views.py:151 note_kfet/templates/base.html:62 msgid "Consumptions" -msgstr "" +msgstr "Verbräuche" #: apps/note/views.py:165 msgid "You can't see any button." -msgstr "" +msgstr "Sie können keine Taste sehen." #: apps/note/views.py:200 msgid "Search transactions" @@ -2775,15 +2785,15 @@ msgstr "" #: note_kfet/settings/base.py:155 msgid "German" -msgstr "" +msgstr "Deutsch" #: note_kfet/settings/base.py:156 msgid "English" -msgstr "" +msgstr "English" #: note_kfet/settings/base.py:157 msgid "French" -msgstr "" +msgstr "Französich" #: note_kfet/templates/400.html:10 msgid "Bad request" From cc7ebd2d8a67c57983af31495d14236845f3602d Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 3 Sep 2020 00:35:25 +0200 Subject: [PATCH 22/41] Sometimes the nk20 is too laggy --- note_kfet/static/js/konami.js | 38 +++++++++++++++++++++++++++++++++++ note_kfet/templates/base.html | 1 + 2 files changed, 39 insertions(+) create mode 100644 note_kfet/static/js/konami.js diff --git a/note_kfet/static/js/konami.js b/note_kfet/static/js/konami.js new file mode 100644 index 00000000..e9f6c17d --- /dev/null +++ b/note_kfet/static/js/konami.js @@ -0,0 +1,38 @@ +/* + * Konami code support + */ + +// Cursor denote the position in konami code +let cursor = 0 +const KONAMI_CODE = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65] + +function afterKonami() { + // Load Rythm.js + var rythmScript = document.createElement('script') + rythmScript.setAttribute('src','https://unpkg.com/rythm.js@2.2.5/rythm.min.js') + document.head.appendChild(rythmScript) + + rythmScript.addEventListener('load', function() { + // This media source need to be accessible with a cross-origin header + const audioElement = new Audio('https://okazari.github.io/Rythm.js/samples/rythmC.mp3') + audioElement.crossOrigin = 'anonymous' + audioElement.play(); + + const rythm = new Rythm() + rythm.connectExternalAudioElement(audioElement) + rythm.addRythm('card', 'pulse', 0, 10) + rythm.addRythm('nav-link', 'color', 0, 10, { + from: [0,0,255], + to:[255,0,255] + }) + rythm.start() + }); +} + +// Register custom event +document.addEventListener('keydown', (e) => { + cursor = (e.keyCode == KONAMI_CODE[cursor]) ? cursor + 1 : 0; + if (cursor == KONAMI_CODE.length) { + afterKonami() + } +}); diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index 3381c78e..fcee608a 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -35,6 +35,7 @@ SPDX-License-Identifier: GPL-3.0-or-later + {# Si un formulaire requiert des données supplémentaires (notamment JS), les données sont chargées #} {% if form.media %} From 76aacaf048f3d12c8d34587405ae594c89e96c80 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 3 Sep 2020 11:47:17 +0200 Subject: [PATCH 23/41] Definitively more usable --- note_kfet/static/js/konami.js | 25 ++++++++++++++++--------- note_kfet/static/song/konami.ogg | Bin 0 -> 162347 bytes 2 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 note_kfet/static/song/konami.ogg diff --git a/note_kfet/static/js/konami.js b/note_kfet/static/js/konami.js index e9f6c17d..a430a4b6 100644 --- a/note_kfet/static/js/konami.js +++ b/note_kfet/static/js/konami.js @@ -9,21 +9,28 @@ const KONAMI_CODE = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65] function afterKonami() { // Load Rythm.js var rythmScript = document.createElement('script') - rythmScript.setAttribute('src','https://unpkg.com/rythm.js@2.2.5/rythm.min.js') + rythmScript.setAttribute('src','//unpkg.com/rythm.js@2.2.5/rythm.min.js') document.head.appendChild(rythmScript) rythmScript.addEventListener('load', function() { - // This media source need to be accessible with a cross-origin header - const audioElement = new Audio('https://okazari.github.io/Rythm.js/samples/rythmC.mp3') - audioElement.crossOrigin = 'anonymous' - audioElement.play(); + // Ker-Lyon audio courtesy of @adalan, ker-lyon.fr + const audioElement = new Audio('/static/song/konami.ogg') + audioElement.loop = true + audioElement.play() const rythm = new Rythm() rythm.connectExternalAudioElement(audioElement) - rythm.addRythm('card', 'pulse', 0, 10) - rythm.addRythm('nav-link', 'color', 0, 10, { - from: [0,0,255], - to:[255,0,255] + rythm.addRythm('card', 'pulse', 50, 50, { + min: 1, + max: 1.1 + }) + rythm.addRythm('d-flex', 'color', 50, 50, { + from: [64,64,64], + to:[128,64,128] + }) + rythm.addRythm('nav-link', 'jump', 150, 50, { + min: 0, + max: 10 }) rythm.start() }); diff --git a/note_kfet/static/song/konami.ogg b/note_kfet/static/song/konami.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e84f32ec9957ceb38188dff81327bcd170b45f5b GIT binary patch literal 162347 zcmafaWmp_R^XK9O5AMO;-DPoicX!ud!QI^n1YO+S-Ge&>2rL>BToSlV-uLc*_v!lS zndz>s`At=IbxluiuZoS01^^oH-_)U@Me?`UN%n{bMGEEZ;%;u|@s|Jv2>g2i0LZ5Q zbM1gq`FrR8Nq_Hzg4FDlf03iQy!}s_g!vB&H>6S5&efJh#odO?(av1^A2yjB83!9X z9~&n-4;h`Ziw_w)8yhv5wUv*RJDG>Am7}8v{a@h-AO}!YiHy$N%FNS)%oWn=WasSV zX+7{ej7v*CJJkr^vbIeoaW6HvD zfwBc2*cw&*Yxxc}ovmcy7=#+C^l&-qiRyL)7Ri507z%RKo|po(1r`_va)Ybc4sqgb z#Nnv9>WcIu>FTjSL;{U?`q6ZCc_0F{Cz`-9u|}diop?vSM|EpvrbS$pXR17y%2E+1 zOCOOck5Hze43y>0Tvewh@laQ{7e9eK64*u-jvD$kS?jNy17aA2O-KM$001Ml7(W&| zJhBEFzyScjex^%2pi89WPqmQ4{l^07E(`!*z!h6A1$(lQN@0;nVM$U=D{zHJOY7dt zXzgz%`{X3cNQ zFna9iO0jY*=ge??obNHmPn+j4#s|*#h;_GSw}tTKdtioH)BmSbIC3oR0QyTCaisSP{x>bQIhwpY-+hX{ z@-y^5b3z*bI;+s6hop?>|KxEGA(?bZIY@3%oL*K`!O>dDFfv%R+PQZ!!}H#G@Y;Lu zI$Vn|)62B_fAW7+hiDRv0YvDt4QXA>zxnNE8Tf&W+bBKfDH zEC6sKn^-CvTPmA)$d^h5RZL#}Zv;XC=wiR{B_7JAQpshO$`u~zl~~)C+xUShq5rXn z{dKs%i7xkmzWji`m!2RDH006L24XZQBcyP(3YbvyADz$1V;Ap96 z{r?iT(h`>v#IV$mQ3n_>PZ&HRBd4Z5+Qk^cNGoKgA#XJ%hfPV{WrxQJE959aao+_V zVuX=GjP@QN3DJCvY#hU5f?uX84U->S>6uOlCN4`x&Up4hq63vF~mOr2#^E0_-ir_lvq9>SUgoo<(f+^ol6BXl<2yDg7_o4*a zkC9I1Uu2z3uN_Yavz6YXmd?GG-de`~s?$EN(b|KS-hBk5^)GU#ml3{*)?J{6cf`J@ z)!tgfo}RN2U*>{Wpjn2~zOI!Xf6%^e=FUTg6$DDxX#-zd4>!`}vEb-F@X*)Gh`@OV zJkxcBGJaM1GPShgWRVlNqi1d6lemvB(EszW3RIt0$B0tFgxr65KGyuw*A!oO{sMwj z8McNR&--qE_cbtmxp*G3pB`%1Y-8W$pC00z%2p!A^1xTWbDY4Wmq0`Hxi!DFtDzWXVE43@HvMaRH z&$Y=dw*xiN=hiXV<=W{NTIc3g~jAiGv}2vOxHQR^qo&N zIfFos5ajc}LOM?OT}^?0{8R9s7){JuO;^)RH81}Z{3`~CWxC0UsM+zQ%QpAFQZZ~b zF~4*{q`I1_bEyW^od2!9j?_%p|a(n^RJa;i}b-A2#Y~2S+LR?UT+i43jP9QFL zZv`POa5))hX&LY+t+ZJ|To4jc&A5~{AfsqiX{E(UFVo4a&1xUQ+-_xX@3ofUWwI7{ zxMsC;+|_hB-BkMms(xcIo>8a|7kdi(!d3w6K0~`Q=%?F8F>!0Zx`Tlwf zRInxh5O$~wv1f_>W|M_nA;hXxpteidOmIwz9i*oWAe8bdP~G`??tdufXAt}RqjYt# zx0+RjM*x4f@B5Pm)>X7Jb0hyDr zgUKZ0`N!}*G38?+VUiu8tW=X9fjnGOf4bHk4TwnJs%dbXpS}u-uVXX(^z@_J9&^R% z%jPwm$GL*^wJW&}vz?i%_OskzLl2hP(Oic)Zt%AI4EMh$kNtOA0tMhhMFV_B_GC*TP3&r*NOq0$PYskfN1ws9KwtN zq$~6Ir!1!Y|JL{~^ZzkGaXRMTsw$9<8XGc^{%5e^GelY`i;#aN2nGPiqNpp=In9zS zzFJ5myDuw-VQ46OmS?AJDvn}XqDj&gNJi?bwq?yg{EG|`;%`u}@Q6r#7|`Y@05J>- zPD!8{4ig()7<|-6z)!$?^7jBL8oEUmme11wJWsc^5R6caa57v3RWWEZ<&OOrUZH(> z+7s*y0muXe5~pAQz5oCpU2t^tF#{6|8wVE;pMcQc*$^NY9v}h$B%+g1lyoA+A;+U6 zpeCXv{cZV|$jHe4T_ICE02$eTJ}Sfv|B(QHKPvuW5KzhWQ9}JqC?qV*%$?O$jWy*> z4Xwoupk4|_#+l*%o~HJ;#)f}))sRBI2*~~x$)HL}dp{r(0jx!?3P8Ngc}L-I2_K4f zwa2YM7A$Bz_;#sBZzo2pUcq%?6XdIN08_#CH__^T!af0snE4$qu%b)d#O7c{*3)90VY#cOy=H5D3W&PiwWhxRaM4=k7n*?gX(;YT<7 zcsAs$&=j9GyNhfJ8;3quPY%}m(BA2R!K3*pN_|u5d_@%jkH=WK)!3wqWWgyz{MSM( zMZeXge%&^|l3vb6BXIFp)$7yGtMD=b(o>WL(?2=9o@-0{Z!Z4M_fE(@tfSm=LRnkt zZI4A`5q~#atMJ{LVKU$Kc?7fJYYH3OAHo**KinUxzO0P**xeg#3pO8V8pZ|>5%?^l zzTf)<*y}!Pcxq!84ZP%q{g!{)j&~edgLyP*rznyVQ+{YCGx8f9=-Q7D^=)^ICrqrx ze)@*sOfiJX4#D)D>+l4?1nMO2cu0eyD{?PiY`HvTvL5OWFzvvOce_>FY{#NdmP~7&|J~A8170)ZWn27m1en+hRMVwZO1+$ocYVK#`LH7M zWl&=Lu|C^;gR(R>V8Vy?;M+caq+21jNBJf~Fq&3#4;2&;PYU!vV5imqX8T0t$9T%? z&k#wL=-!zrQs0P=aeW@S>OT;3vh6;l__LR_;|Pbk-lnDn)`~E!PR+CM+s$Rr5O@km~R!ISyJPc#zBCirnhqpGCP}&#Gbb{t)>Lj+|oHSy0}<_nlK+iMc>LQR~mGL zT|2iFb19kzJYmN0isLV7cH+yd4KH|LM?#BGg=IJE-&vO`RZhQ$`6;r1%g=92tQZ-5 zjAU<0#@CZLC)JU*N`#N$Br>2K=~Qn`h)IEZJC&}&vP9PJr6T^lhDe9}%Umq`L?7yS z5q?e8K&eVunVNiZ^Q#ok7P8H!p`RqxmNJ8ewdWr{NCuaFsTDb!0@1uGpii zws5L0h`?$5NT!dM29N%9@q!*%j2&+9AP0qPkVqErXKXek%~Bu+{$WTRvuFE_^iB;$ za4*&cCDkn01Ki~KWkjM4V9XScCXS5I2x}m&{v;s4tH;YDX#wRX4w!h?)`ebbQCS_$ zztG`VnAeGBM~j>bHPDzorth6N2Gdk;I;GqiR=Byx+>;! zg`S=JzNj7SYaEYoXz6^ONNhSDKZY`VL$DjRMC_ghau5+$Bp@8X&W>R$vOupx1n1y_ zIKhJ!mD;88)S$q%C;Q%S$ql|x#~Cf5X6GTOQ_0Uu-aJ;M4xVv(!KuFxCFNH|-!JBL#O`}()7 z=dZ^I?j^*W3%MVcRblQ6#9)4HD|P^{cTIV_7txmwyFl1nh(bzPKyE-#99J%Eb}(#- z&72;ladZvm*L6#D#+!7{kS`o5yuvTv$ErW}{6XpOuWbm~{9}{nahH|YN!eZG93!b2 zTgnJ3H+Hs3`Yb?~uL$Zp&AMJnX#Zr)Eb~nwAAjZ)N!Vn?UpjW4ePNCXT-w||@4vX_ z|EDNrqDmKLc#O8Ujz5qvoqKMT|i53;$P5$mWjx* z)o_|-LCcPj5?wc3O<19J{%L1Sg=nM0fL~J_%2te8p6tB{v0?^7eIymtF|1PBm6e8i zb#u&p7*{#;c8&>^-{>)A*n zPdN;o8LpHUyq=Kla}J&WTcb56E1?JY*JYGqzz1Vx+Nw@ zKBg=bw4%XY3f%uOR%&a78K}&IyyVfe!3o44p5=`&m>iW6rVO;K10~FmRj;@ke)QW?X{x!?T=0Z`X+Z0M9z$9z^q&zMANv_ zhc*^tUuj3uqhz^I7UlHEDCV~vga{by9A zg)v$;8Be?6iZJpF7cxC($LVwmxjR01SXm_8Ng(01em))Uz8bqEu^1YqmM%51~(wT%Pn_k}lrZp(WAFG%)w4!?~sL zppq~|d8&-7N6)4j@WIdr+99W)P5cll^lDSa4T(HWp6117k>*t$mGpqy36)@FUp~ios8ueuUUn*qkM!dl z!*{%)ov8HHCyTXln047O*Ho44q>}4R*_&8MNhtfJH#(Y{@J#lt|6;2B)kWpTS2c0F ziz0ByGF`73;3yULvm!`yCYM5e%cd!*u|lVg{@KvVvp0$#{DSZlXPg6C5>Nqp)-j-3 zj;aQO->&_6CHKT;7L*X8Tf=~Mr>P4Fh9l$7W~U0OTP^&m)AnY2!>gha zm6wxS>pHa>^UF>N$%hphpwz3;GhlHge0=!Pg%UheC39@*W72SyQe{|=v$14xn$=$B z*BDuWNm{<0l(Xz#(M;-X-OhE=qr8-VSR~Su?p{y1ZwR(H?eiz=Y0S`(x01+;-`nbR z8io5R8{wguF=sQ;(|b?tvFzKsKt;%AKWmF}gHjf3H}fQKDGM-0xkUlmL6Kw}U3+=f zk#-qeNv0IbB=<7v47N4R?5B?hpACN4`1900O?maa$LwHx6A<~M8o1Z#QZM0*UENg} z>0CQLd-S30o6X*+*pSL6_B^lUjr+}N6K8|z;}!$DSa4Z&jKRlWIf**z33j7mH=B3X zk8=IuWjEryAUEsY*#_=9 z;!Jqg15mF+Sltp=Z2B9}d^oQ7pEN}W(P4s^YTiL(lLe2QcX&|7Kwj$~kLOz1MnE|0 z+RCn;kPn2MNZ9XBXNvQD^*tO9=}Ya>%u6>c1< zQ%44`2QjRLmlYBTDBmm5_CA@EEcM&XHb4~Ww+sE9JZq)ee1j{lv2VDvP7*g z&2>-x@uR(Z!!-xxrgW_+K&SvAX0pf*>}Rz~`5v6KLiFo*P!1N#N~2bQL+D~lnE0tX zeT>}b+Bx~4SP(I7!yrQ9K_JnW6X;0SmazKL-kmb`?U3LKv+NKk3Mf)VMKnmDvz+0m zk0a{EFz(F*8nF)6g*uh-wE+f_6UkGQC4e_7AC##eVdU!5xPCPzG=~7y=QUPO$KHOQ z-UDldhzu5`zCq*j5HLqQbTzl4e&YzpqBoyv!{;!x2nu;UNjXp?C|}*TxR7A?ZAS}s zKc?%C+HdA+xI@NAUH_w6?Pp#q()kJ8u6tjr;#0E<%r6mpp^Fl?-8i)G7GG8`=Q2nP zDNoY?Ar-hxxv&7^Ezb1dBeW*dgP)RV34#}s(N=BZ;m4B8hWjU~Ru!4R zqI@5Qq)bV__~h8R6W*c^Axbf#U%jiloU%uV+xZ_cl*5D%51#jcy!P}S#(lD3K!sY9 zdRvCV!`MYqMyQk`MAX}dHfDrhZ#;jNr`pJ^H=nQpRYS|JA%Gx3uFxShoYe@V4uW#6MXQCQK_HZd$jl{le z_Fr7?y4tQ6luL7x4h!8L0w?DG+)R}%I;RDs>~ig76!C&;Ce~=Hdd8JpV7X;7o51Td z{&ue_{rSD@!(nLxkq!se(^eYWU;3Vdf4?2^`=i?tov9ipe z8DX&o&~zDmUcZk+8(&mwg?vS8fsPsS-{1;ADd8rvv2B*_9oC*vFe`<3J+R|)Lp_-H zV1C}plN25~EM6`(`LxuDdsqjTIOM9K@)Wq9(R|Q~p_r+&GY&RfX6S}4Oqbx|toRXV z*(PC!OhcS@ahVT`FCB2I7>>nf>qz1U{4idFV8S*i)Vm<|Wc<^m#>9Y};vHkCl0WUQ zs2B|%EN-P?8O-8$@Nf1HV8T0ZCH|_RbUgfrsVFt9y5^c)J z6Sy)(AVyyp+MCkJ5o{sG{{KZapLa%svYQ90-}4Jh8`!b(CYkGb0WARR|0=T|Onf`)pP4kf+rB{NSQz z1m#pv_wv4tGQ>TVOz-!adFj@yu2S$tMWIJxTg!ofzg~E3FJlkhz;D=br*QY7?i$7@ zR_)dBl#Xh)KNkaNU|dfV$KWg|k>AyDVxPqn;y*&KKrviMj3?&MaJ|%EHAfiIsR6`S zu3Pizs|R%XMaxDOoQY`VSM;G?VEXXCDTn8WsSp+p0T?#GdQ03JG5cgFbNc(v1QcX1 zc!RFdYpLsm9F94bPwqTu2H?aQZm4y<5K1Q6eyq>1K`4$`VPXJ)OLMb$LC75Wdy4Fb z40DEYeZ5kWBt|QzIguNiX-N;Rfa@sUI%gG@noTE%6$5HE$#Q8V&Q&ihm0mU&*8$H z8_rP`w7a14%7e_K!)t!EMj?K-0+i*o;c(#(^{=1OQ8fYs>`_z!+h;gbuL6&N%t;Ml zsR{D$1Q`mlbEhp9)2Kw?|%MmPIhZy02D)H;@IcrnlA;QYsJ+7?a(fNqO-V*&(0 z6U&Xs_b~P;la0#Wvwo6u>Nn{m`M@EONj{>oSSm2{*dUQ5i!1FYBA zJ7r`7444sjKR@l*N!{i3Nwr(Ou4_D^7gPu<%u%#y15~Aq@)J9b?JAxLGp+{d-7rOH z9}uHwk#yHsnU@=42kRJi>X7fOM;_snxnrhHdAQ}|JTzX2VdRN~I6iqCT2VK{`I>5E zJ~YO_qN~ilchxv0FDub08Vl^aiJ_kDr&1FA;+tn|;-PQJmp{RcC6gx+jC2?u2MES= z$^Vd#W_;nG7C}mq%tvgD$E(YGf*9A9;=Lk2$u}^t69wCz#C+b-bJ`epPEKX^sYH^J zlZJ9r%gDBUP3@Q^OAI>#B03&7Ir+NR7d1p}RP3@MiOED<`-BGMzZG=+ z%*qaw9TYZ_J<$d{Q5ljn<<+0y@ZSvVeEjL`5X;8bwG z4r}A^LhjKQPTu^E)))>tNiunWl53gm+#k~@`*Z+mQ|#uD37-RLt^88j9bl|@fitv> zW)4hET^m)xe2Q)3sq$b)yMbzpDYQohU=@FnKWPauQ}jR@p*i&6t`At5izUJ+zyu)O zpRGNz&%j?k2hmFx1GvJE=_AT#wu% zq#l*|e!y-iQ)2VS&W*c)EmTj2#}SbI_`of>3|lC8iwaX45>Fj7qJh|{Pjy)gwiGLO z6Nj15O2#LH7JC44pvLsQiExkVzc{T4J#D3Nkv8_=lZrhNYnH{TXAB8`@=`_UT)3G; z%byC(|9p9g#l}!$`N5w>E1`|74qGm{j9_x+$n&H~-%A9K)*@y7E9~0*R`9_1hZO*8 z)bB};e8iT}uiFWH41F<8cMAkc2QXj1scrRyme%jgPfxbh=4Bm+mxpe#0CpLf zFoDBRP$McvS3*FN4Jcuo-Z=i@I(@D58iwXD`2oV4PoOvK7#Fm9;w>VJKLG|s|~pwuJ?6a4XH+WKZ|EgjM*GG8jCs zR1(wKJOMKshr)X#UHCD62V$G+E~gu=B_gY(iu)}EHx}jzAo0eM5ow}!;%ch;Y4`rS z0otYf53z5^5rEgggviVncuCiIXg=IX)rMB6T_Q!yVN57@ATTs)QZM_R@2sQu8ijzr z33p0?{5`qZEUtUr`)!+_SL>GWH#^SYU3DSU9k-Y+(#4#*@uv1_TlS2EMDiI2iTn)R ziFmd{H2LEY&RpXl554t|3_3*Sk;9>lLIEvYG$Sr`L41L{?gm=@lM z2r>wyX{$E{$u~g z_QH{>O1(pq*g%t(*Zp97nf}sRPW`)fYxy7-U6A1Uva|BqgHYQ3yIA$5&B#D|fiOC|?e-^=)F| z?8fzBIRlrAS-|VOKe-$B>v|8uw$@tizox`Q}2ZGs=nt}+~gPH zw#J&$6gF)iKQ@KlHN(|sg8NyH3k4#_S_6D;IQ9t{o`=Hla&C5k)lks7{M~(P8@LSZ zd)N#dPkK}FU1ncYX9E|4-J@+ej!>VNe~&g`&-RD&b}b_K3P1Qk(kaGrD*CmV--0e! zukjDeNSPT~)w&{Km#qxnleig|jDsnSk^I;szkbh!a*Fo+jVTdaFidr!<-ZmK-YtB_fY8LaimFKB#ahhI8 z%?a#oRBZS&M6((s4pA$U^qS~qyM}2ih28LkGT;0*ai#>6+lA%kCcLIe2M%+IIyW^R z4%sMeY6SW~_I%o`pWs8S{Y=IMR`7f5#okcrGNqDn}@=vy1wymOJU`7$@Rs!Z%)tnEXZ zsV+wISyxM$BqQgFS?lmn1-Ma=V}$9h%Wtq#UwwE1vz*r=5|sp6#MWO>g@d!$s+tzw zp6f~_j!4V0;`l*@{w5vUUnaZN>&c!6kc{6q;SoZ+a^CyFwpf>OyaAxM-H2VOHY@Gj zbd(ObFj?)?Vaw~Vph?99-o?hP4*$rF*U$0jqz^q;>BHeNF3RC{t?B(X;uCgAM$84Rx$2%l z+|T+!WvkHZ#JrsMZAro@RRX{1tn0&vn}##~`xYK~gw|9KviO3lH^H$Zyc1rrq=KHfZj zy0OdNJrq2WMyVVAm6fM+EyH@eY;z;CV>qr&aSouq858?DD*2lL1$|O&@Ci+)uypD^0)xFi7wJRi!JT%V#*&j>psCYO& zRZx-d)}bRBnnOKAaWp-$qyDsw9TcO+$M)j=l6gpi^KQ+H@iK~b>D{e*#Zq+%T(Vc$0L7eZ@w>g~EvjXEF{Sp?wy_03D4jg4`|D)gf zfG|OmrWmth9+&V=)J!`U;q^Q%7}^}*_xZ8Qc99}^BV20O%&Lq}+yFo)U@0aC(|)(| zb+>}|rf+fU>P+ert4Bo8NmzK;KEJApa5*tJDtbL$XGBtY{Dv#Z5)~INgd<^D;yEKH zmXTYF2`A`>E)H#K#@fjXd{0r=bGvp9vc&S8vLS*-!$WndC>9SC(zp{5B_X z{Je_tEVP4}ii+s$Zm9;r%NrYgi9=ku!@O5wr{w@VX6(?kbkH5PRaRpChe2l2gb*3hxlHeX< zh21r>3)o233JiY)VW2o{$HBn~+uf$CNsT#WMj)x6P=1^SRc=($Fv*0UCVs}kOBZll z-f#FB&Y&Wjx=FyDFX)@ZSY0Vd}=Rh+z~+1WPz=rk|$ z=1D7I?+Ucn?+vK+#Z9YR4^k@_9;YB{ZM{iuV{06NacJambkfnm9dS9A^^H9Tj%zxL zetOsjiqZ*rHG9OCU-17~N(e@WYFh;iwj^G>*O7ubp zWz_vTov>(Cj&w>A(5(5LeO}31-da_+PaG?Ed|QmR$I_(R;?Jx!Di%wmc^#**lr6ZB zmiMeD9focR<0=!{+n8TM=X%_3T|AnHpX*OHQ`kx+Z+h zdrhMu)pa4A69@ZBK2)a&^?7nPljB{s8&>T~#0NypLBotUK~W4q!OC1_krs%_eS`amgmwE0KbUji1cf9{Wa#j!d1B}ltLSd3k z=iz}aU`72d(){!gs6o=ECUpu$uW07P=Zm!6W$>1PHLq1y2Vb7NA&{S?db{=b{EwrB zz?zE2e7R}l!P^^X=f2YFMugjmRv*xw#-OA$Olqu>=;#X7Y z?~a_oTWM95heXPXUw*~)#^p=1df3`TJG)qe8Pejw_^O5O-`UNRw`##WjP zjB%5`7FV^-!I~n}D7(V*d=@axYWd#9512fxOA=yQs_2QzlDD6A8uxk{Y`xfc=roXw ztHgA-q1%fv-e{>jFZ8UbK`zp1>e-)*?$<)uAAcVRd+Dx_b|(d6+C)I$L_IQHFAp&8 zQ78Z~Kw%>8G79GD61j)dsnMni*QD2wv#VFYyMpZYVOdu|zH<-#WAl0j7_1ttD_>{_ zKO6NY+Q7AC7zwl27NC8#mtfbLNtftZ#lw5TK#}K3RFi)TEGX$Y{p~Z~<%6F$wiI7& z-g^@*DO|bK^!>A%bDN*YCA7pN#b#s(X}ovpRW^3~vH)WKd(cdCxc$(^2V6a9hHevB z8B^B9v=Re&JMY}dzz8@lISYXOkRTu^zq$ah918stGTq`4;1%R;vx)!QJPOKSKeilU zsA3k_EFn3T8=L8D`{ng+S$N+|#8&x#qGO)h6f7I_lICMt zh!Mf$zTB)BI7>N1na|NP%$K&GGT%sHLmF|_2!KgH{V2hhjr=MZlz&bVLS78@lgGe- zys7q5)>!vD{bwkC6Dc17_+m``5pRhGPOzSd$(u{x56=xR_;)|1wzz-`vLfC}v-)}E zNTzq@1f&ed@<(Pj{^)~ZpO)8)m7hO17F&d3H?4?UR@IU@t{%d*GLNYKAix?vy-`{E z@P1oEeX?iM)0Pm7;c;kbtIR+GCpZiLZnQSp(A#r3VC$rF@vut~4Q`AfB&kkWL!p(4 zGg#Fz8sE=F&CJ8rIR2a@lw3vwuxpK#H(ao6SKlhIL3S_k)Swd!TkJYR3KkjVB4N(k zWBcU4uq^v@$XYHFDD>NCBQo*DcPH5jUvTEBZetxtj*=IKwtB2_&B=ZwG;1jDGWnFq z@5+WR>3#cIQU*&%CrWNwGeCQ#)6*d1Wp-YCc_QD*)StjBgd(Q%;yWLw-AaQ=3;c)P zuW98e&zS>F7S#O7b+5knG07O5P7US3sCk}DR6>BZ7)olfUnpD@{7jP-zdA;%@=yAY zo+o%?CcEA}V>5COplyi|XP~%o;x0*dam~y*TZqVWzwTKpeSK#eXc4I{qkT$q6ob38 z8HnJ%r-6UM{-^@~dPI3(M=j*D{+q5yJCvBww0W}Aax730ExWiDbLyK-S=g<6hVL$lwZPN}OIbs{RNMlP9~;Q;XvZNCKc zyaWJ~l;>w9O%Js7+sPam+e^QGflh+<@xC1=Y8o(&dgJJ}BnPpmV)37!Bc1@|e1(I@ z+v5@@p)IZNdVeXgkw1BzEh5I`Pq8a1idRj%XlXIYAKyJLk@9Z*T&}w2KR?al zcW2Xf+wC~6elgH1atz|B7jAYGpPS1C*wRt^wTscUZymcG?`H%-9eEafymvv{7V(Ti zFL-^D3o$-p_qh23L+FUDRnh?~jzXz+e93yawD$e?AE3Q3O@Tx>ncY|S9bgrz9a$!*< zO7PbHx$8yAI7Yq9L6CZI{$UZ8{~UzFruaEI>DbEwPG$VJtBwX|{pvbBue|=fJ29&S zTiCZFSYu|#2ytH^*&&}_W{VxSPs!?Da?6HMa>Z z`#+p>R>JTZeuf2`?}8FjxO!;L9SJ#!g0fFV(74%VmfEhmm85+zj&VqCzJApr^FJ8fNdVqa8AmqSo^CZM3*S2pNv;?s$T42}TLdhu-*0|7Cu$zDKPo6zVP06T^s!{N8He_6;UQ9LMWpPz0NVJn z+%GW&`#ti{F~2VM%RRMP8TY=~bRmyLaIJDI+-?6Zz1C)2sr}rXza&HM0a_w1r{9pF zxdS|-4tX_Hh;aJHWHs8(j?1;?#k84r{Px8LlU4EORbNr6)MR7wI2PDloyc;unnRHa zdFqobz{<~25&M$TdFsy+pR{0(ytjjzP^&w)`w-UhRo%;uvd!Cxz@Cbv?>z?{D{n^F z7_{({7*&l3l;|KNK+!-IqTXs7-_Vps% zTOG1boMekSIhZ9-wuYcZP;LwNec_iU+R2BdJdZvtMG(JhOdBCHFK3w=#_{ ztEDsH^XMi`eXGAxV(LKvV^*}rR$yaNRNF+@y3={JG}m|we>m!*u~uWjwHZs?spC@~ z>6FD*-EsV|*6fETcVRk_eZ<3g)Fup_T(q##Myk=NA*sN~6K~$Abmoe7tECpE)4j?f z*D*;T$ms?=RaweQGnGZ=_Nov6`Od~iR0xq7MeS4v>XpF~pHdhlbbVZv=3Sw(#7%ij zxH{oCFUw&-1N&5z7oq>>dLcP(kn2zSdOsxR^*( zB1QY&I2Q*7NE&k1c?pOkaK~u+r-vHZNXDZ>WjcTDtfklOJx~1k!Dt;}U>ux#QQRRP z04zi{7A%Dh5hRnf4*BLq(k-xT?VRk*S&Tjn%6KQEWo~31e=~-$^38lrp7|Vg5(8Ea zKTyb8LZ-a-p2qQ82LQidO9RZ@`TgRH-F6vbGVgz z7)X_PADPRCvd~MAxTqWL69caM%jV0c-eC4pg84iLG$O~#S?iBBRUHCYor5`xF{I-z z1Z5j_Gg1EJRRh(gWZMbDU%s%#^i(ON!AR>CqSQo4OjFAe0)+^2*^o)0Jz`hFpHx|- zV>GN5Q&oC*v=F}^MJ2eX$hUlYu#&?b@voDmPn9QO&|3>l%WAJ4-zm`a+4WaMW;FdH zXoELe?#NrhlJ+>=wbc4n?HVB5Urph>F(OpI_O;TcUwCtgoR>|oYN@iPmYj8l;`^5# ziHvN_1UJllsU+gKyv;d4&k=L`sD-4`evOp3=zccQCHHuP#Hc;GF>%d1$kyGd46z%#fEhnBqQveyLgQ61HDx9s?4;1{wN;hmfN^l?XN;_8a^|q@RGjAwG7ZS zZ2QnJ{ee{;{(AnsxXQV5uUFSekSVrqh2i|P$MoLLCTJ|E^s;<&->JsErpex<-||&o z46Q~0Gvx+oNol6h99nA}9BO_cWqL6x^CH6g*}ghTa` z#=JBC_gTLsPwk2Uotht!9POpon_VKKq~DEgG@P9P5rvfHWgpzoR12vuu-2NC&@Yo0 zz!_|x=)}DxJ*(7AJSJYfMSr<A>|nIcn3t{^&r@ zu!g%&Oitf2=1mtVP-@Vcv5rMnIvwV9Ll(KnLxQqL4aMR9In!8%N4`|fg+Rn@rn91D z5%us*KZ5_^3vtMOM%6?1WZP%vtF0P~F9L~0@uy9}+%q2AFlwKwmi?X1Wr?=Zmba=s zm$^TfSMvI!r5LJSP+i@@^URCB{O$vutN$spthmrV{+aC*MUM3A`!{q4v!Lwtjv!QV z214x}I{A{-MKbby)WL1HlUezOT8DUVCY(QCZZx3Ap>boI1TVfB_G+ZXFQ?H!Sp!6f znPSA>v58UmmU|4BV^=LT4vsCXgDp(Pdcx&!YV>I7Pwl;z4(Yg&+Zj7QA{4*e`F^aQ zPA^c8zO;9CWg#02?ea_zz4pT$zS(xeq2`wCt8y!>rOB1r3_yXJ3M4(8hyVFe0L_x> zkgSO3g~>0=l&jLsyVJ{L$`0KpQCiZKuLPdn_H;th$7(TzLs)waAGNQp%OD@zl)kU@&=}B@*A469`2}`7-N)#^- zaK#L?6Ii4}S58crUIS#;HeOSdUJeL0G2@Wgg%_5ZF?j_vODP2D%7T?q1YQSDv%Y>Y zl;nfc9x=PM+%gC3*`t}vQ16}K=`q8WxY3h=`ea zp>wdmt-qzM2~<@ltF5KH2JacJs~UhGVDDimZTPFV)}e zH#gW3#Lb)OIPRpqR|ty52SAuj!Ee3Z&! z+;^52W|xmYRO{GkO$oxfBclpmJx4rBF68}SsMig6u&tb-u&|E-qlQ+ohE_mTo^)Yu zrR%v?v(?*gs9=Cd4JQh4e36niF%FCh+u#+yzNA;NLaeQw87NPs)qAMt(%~a=v(w#k zDELq--2b>=H2rIb#iah@iyM}uqFzr&d-bLAU4D!%_aw(>t{Ae-xw9(l&NwXZy$cxG zjgU`NX?1>bcg3P97+h&}D@uv=E-(B!6n@4z9%(y7R=ZZRY*qoTuP1%#;N)()rZjuj zGlGUm2O6T}8==kkfZs=orv$lqzTtU>X?FW%0x!R&s_Y^Ue6Ag2zv`sB!0|8bw3dejC--ej}Tqo>I>ARF0c+`16_~tuXy1plE(q&BC zY_0=LH)9uW3-gM`Ock~BEfFZ+Iuca|i84 zAuAW0p>I5#gH|$P~!h!)aR}2p+Ym6D9S?*e;Z6DCUU9;Ahv!5*9dV6yYj|nrX5<@vT z(p&I!7b#H(+YS*TaDmb`4=Xkn;w!cV~+ z8=;>(35T&Q{=(arcSA_ILPKVqI1R2&oLSvDjR${`D1_}qYNI&kKqc40=shGzdKk2I zpMTUrPu(e#<5kXDvGDRLyd^FmJk7GqzA%2M&=ZMoY!`Gqwqd5^!}6QaOq8>_HV3$< zk`#UF?Luwz8xj&^VsPTm>X&p8P8)F?dM|qM)!hYpC-yhZ>kO~f_nJUB-$kW~Esn`pI5NaFjZ{8 zy7x}y;Lk@MEYUEIo>oTIn81Kv+)oD>qC!bTbL}Tv8~2mZ(ouHbcx?{Vk_K$ z?4oDuAiJt?tDrwom}aAPUtO*{kT|f{2Ii4E$I*j09Nu+B9GGiR!A2WBkWBczpYZqh zP4hPqu4(mR+s9Ud@32xL`G$Qku(`MvROfi4uKgucKYO+@iB!cL%h4zOn6FUrnopR! zNHdu@p_Z>8F6#ZxAs@e?2XON%RAIM=FNqExo;CEU ztcw!MM1ou^-YRplVrv>(w04+Zo>Y%??NwQr(#O=@BN6j8jC=Zu&nB^*1|nuqu>Nv* z|1Yig$e3&O4%K%$Ot}8~I=~a_o4!VKwajPV_@Dl(& ztc#Vy;DDqAgz=NH!?H4=4yo56?W$?jD6xc8CX#=<=Chk%a|<>O#vB#raqLwl+9`{! zFNUPJ9SX4-8~g8pipFk=UM#zLk1*+4Vd!y8T6+!#rcP(3>kFsP!8&iU^_8POzi!w} z`YFqDqjW4ibz%7YZ;zCC4)ke9Oj=PUnOH7iOZ*!<#euoTYFrRTcY&L1h2!Zl!sQ+) zkG9L%>7uR10E3rzrX}(&txXSO1Kh7U0Y2h7c#fU0wA1E@l$pDd6o-{^ zEd^--gk#03({+2<;_B-E{2~%9QtVO4l$)-4PLM zeDC-DeOUsD^Wrq+=8xL?XhM}Ye@IT;m{c7gjt}OOF@*3rpV5RnJU5R1V-7sx00hcX09r3$`XgGpv}$nEDqd&vUu)Z$7$E(fixN>2~GfB+^ZD; z01z;EZhb5>VQET44S6^eB?m%Lf)ZEkRhuPYn>^iCs6={ab*$DZ*iw(>O@ZiwEqcI) z$xsN)glPy-AH-(}TQG+)69(h)x1h&BZFPkAfFP5e4hmY>D!V;jP|0IY;R0 zC~;+oU3+%LnfzX60{@;v$=Tl{iOaj7Kpz>5jIDU>ftV;Lr4N!GP z|Gec~v(3%qCbqZi*T#X&x!=>N`P2y$3m%+H2_u{Y5D=EnbZ*0J(5Z71kudfJwlS-t zBB@d*zI%wcwC--Ii-&mKW-4k3Ri<}bb_z1%dqy_?GWkGyb_6PNZR3g>lUl(U2vjtc zH&G=DcVlI7_WJFQ4Z_v8S~cCdpOAaD((N-~S8t9zlV*1hsnOO~Gn4F$mc2ES38ETc zVrv~acbc{B6PJn&FF@vK^XtBnnpw?K<9w_)k}4sp2^1&1nV>EJR@wuRp$G#C;YJcX zpHDu2;Hcy3oK6qOSF8Jg*9DCR#x$)1Te-1%F7upeKd+W#blCR^QU`q*6OdE|s_~TD z6-sRF0TOpQ2>s8LzqIZ+D{2_HsfHnj?xyGRAXjH*aO`O{4>9RWO@(WilKtGO<9cA3 zhN5xs=gmPLhc~gnMykK@7 zwQ|**8cY5JEXD8zp7-{rmMzTJbjh{0s8|n`ac;^8viA>9{{48Lp_##+TsHtT zMRT&}SG)QwCA=UXQU`)n@XSMxv^C<5$DlPzh<}E*M_|+QW(Gd{@s6~_nhSdS)1!hT z90)rUC&_m{t=K1X^M0HCVET1+%$>VsYkDtMJix#S2VFVqCb9K4gsM6YyT?4%5!ijS z+(Ws0ic1Wqh?|}2CRXn&r1t@~C2#xkLqH{CS#heEWF2Pe>0%HhxOV511=~`8L<0Rk z&Kt|YfR@a<|6J(9I^0G83BVP)asZ_P(385j2URU-ZI*K)A5m5USC=ZC*jx|}8!c-- zon_4dn-x>*;$$JDILB9c%bA$Obu*HNgiI{+K+96@w zcy4mo^>E4;k48t?6o^_TC;Nj*q#g;r2im_nD~be5pvh{!MdBK?MEbCU(E z4#yBtPfRYuF(<7uHtpHx?RcG8g?&o;jspraO3LX+j*kIIQobAaF+;*5wK6bl=liaC zwQm|q>(AfxDYvxs}4kf2%5&$q9mUTv4r@xj#ah;I2_FaD*fISku@u#|* zvX(c2Qy+2>T}CY@r?qLHGVV#f;kQ9LTF?6#YHTyFpw!2EW`=9+<)9#UPUIeBZc)?h z_cg9X%@?f>|8JLe2E~*qe^-Pye@b)5Wpmm|C z%s~jZ6#E)6M;;Ge?2C;eGz6MJz!N;rOcPRurU@rwAr5ocVB2AW#35q;aH5utt$8-& zu1J(~)77a|(aqRxx(>uv`QSKvOo!ecUodxUr%%?&;qc8H4uSV|qI->7Go^4m#bdf0 zy(jNDbjJ{d<1D5)9fvqf9b(k}IL>R0ovvh8g3I^|Yp_$8e(^zv$rE0Uzg_xOqTn_< zgLHm0D;B#k;p-JG%EbMs4bD{NFq+&}Z1~uXU2YDFPNmyK1HKE4MS=;*NWv10l8iie zN;m>xrqBZ9p&r~z=HXE-|JU9KLyWrBKH2&?F*%+nwL;imj|)Bv#D;4thqUl-h_Ryo z4AswV!*zZ~rst|2%ron4BXBB?=(-J_lRHUs+tw{r_2G4u*u-=uO=2z;LAv}CYpT|{ z2N}mzs@}{?MporE780yC#2#z3)H?XB4;6`f><~wF*Jq^Uv~}Cq&DHz_!dmr^BE4rX z99DBKGKuK|8{Z+p1b+M*BY*$^;HhKoNb`tnGd(-mb`fz{R;D!!mC2Ch{r%(DQ-Cp! zXrArdZkhd9&z>K*MRnyg%m>k%^;BPPFCXg4oi{^|?fXIT7bE2cke}ix$GLe5G zrlvx=Hd=qVTQw@adlPd``4kE>^>`%*^SeHdnbwLm)$$orCy%CLE`mZ@+#k1f*(aZQ z!UXXUZgqKW0A4*oumpUqAq{2VM&x#DW-EZ_&rh@4ks@zkw)1RrJn55gi zmg~mew6o2^k+r=`ZcA@5=t0J|6-mL1%oR*}h-^u;zzui;?&TR@gvY7;n5k6v^W&xG zMJor84wc3gwlr<0+n3XX=feEq@lo5DK3~X&CIQ0a@gZ)%DnRc+sIWTlG=dqv!jVC7 z42n2UjJ@zD9HIl2qcsp_YZLwqoH~OtAOrA3sx3&7LWf!gVGrvvozYR5RQ)~kn|Hq8 zZG+d?_hHf|)}4DWd46TMRFxdU$wzMzu6AZ7WONykJLOSVI6Y<_<+K{A6sO4N1#2lO zVH>{)DMPQ^>^)=?7f+sA8zHhk#JoU=Kfqk(9>nP8XgEJNG)V~*>LK^|wnN#X(V1$@ z*8KKFxC`KzW25-%-vjIAkz*b=HG4NE%j^&|4P29kGy@z1PiJRS002P40RR91006jC zRUHNZ008(9?7SSPwXee?DK6H*xVgTnva__GxV^(8EG)pPuC%f~{PUGzN&-d_7dL+K~=PwZ|vmV>9*f?om?k#;mQS1GiNP(YgPA&jNTq2npjoGI~bY zsqbiCKF3&A9I}PFc7kN35t67xUjvgOF;5vi$%-Ds5vfX=J*y8^nnRURhztZG zGLwXSKA+sxQ}XKa(VSZt%bc$|AWsZfNHD=Rt-zTyq2`d*I3&GeUw4@(;ah1OLH=O9 z;YmcQ`J_@noO}G;m#Q$_Xg1vM?4Ji+p;smzvk}h_7ZpZko1xg%v(DbVwgmI^3Ykw!Xa++{BVmTzjjxL%SVEyH#^rGortcR8LBc|P zb79(YH#+@Lk6o2W(JsCXSV(g33tt zA{no;Zss@swqDx32~0ytSN*8sq9&wByXq(Q)cH!)s*4u=SLU8yOS+fcgEHjBp}BFL zn*n@9E$7!)DG#kwq9)t9OKX3uT~?ot<975ZH>ap@mf7{VlY{6`J!Rb1<;|S~8v8>S z5J(F`7HJZWe0sRFU;5K>($iOEo!>rOk8<>hxx>I=S(f!wDM9AZDxGKB-|f85*W_9L z4D&SqYmV3LM`fv4UD;xfuV=Y=JLeXsjJhxHQpP-z$Ib)+Yhddx>eQfC4>F4FN-@RMj zW+3;tBbaccW)owiJ!G4(b9%Uo4Qmv8n=k$ur6Mcb`vgNQbTrp%G!#P5lM5DMLZxDf zK07|_lNdmVgan9qUOH$AopY}fLN0dI$|~5_@Q{IqGWYqj=jRZAxBWYhGd|P)o;)qq zUKp8AhjIL@Fl}tclX+9HP9<_MKAG~{4T~qZmUBw=M4OlB(-e`+A3R5kPLsS9*_*dp zCa0f?=Dpmpwy?8}hmU*JPvdE-D=q7pVo$!MXkS6SjbRvKhbq;xgvWrw3Ga`=kP&6b z?2bc|uaUV_rx;HOM@IWJE3KWP{Bnx1hRI--i{tYNHp%vUs51aQ4D^(Q5P*nrc&qC`zG8BU?Y&6l)yoOU$jCEW$X4~csi9cJ7-|H@9y8`K;$6WiZEqi{5K@l1^ z`@YkPRtZ+XgfsgW^$v8dX6r5m!b3YM+`99NGTcZWF6~*-fo^eX8+Vfxx=%Gc{#u*3p5OX0tx_68M-D$pktpk#0qv*QOx%;7EPwcQ@l5JvxaWj zc)lL0Mw*(K5;SrsI$uvWy`Aaa3cF@wxnytj(;QAdYhTH6-sq0xsyQjbKF@HBYl5jy zl7Js{N!6HyU2Jxz_YL*P{yxRVuGTq7-V8=}Uef+s3HnGNS~xcmH^A~SYU>9A#pmo8 zf_%TC06z{)JaEF;MQ}p%(TH=UX<5U*$bgb~H+jG&lygbubG$_}#`mNEJ{(LEKmZCz zf#*3DurSBqPPs99aS9FqbY*2-ru(2g@O{)dZhv`g(k+Ob_ap838J1sXKQycv*{8b( zUej2&6g)?xMZDIpWj{@o$Kc2KbH&nn1f-mGj5{f_$&!xX%DqEPV#vRjhvB@a=k_Hl zmwMUWh@BZYlOw3_^^XdYz=MW@JY4joB*y@bgW?mm@tD|bEg?3|W~|UeV{4*8(vHLU z{E4v`@j>=`syKG!gL==9wzYf>+GrA1BmXmX(8!DEmqAb zR>Gvqzu@OGtT1iqaixXvt-9aoLeq`4uTFFs5Ab7XsemZXnAOKQKVndCMC7DwM?8Y4 zXc2h|7O991u=;@xXaLnBZE=DxUZ-7efgdVeURj)pu@h9g9@cyx9FIoR5y74NBOJwi zu8&FmwF?*&cQoMu@Dt8IN1Sqpz-Uu*VdZ&Qg7LwV{_)7GO{G?JKKm0w0Z>2!(d)XV zXS-t{jR}b$*}c5Zgz(y1jSFUgqaeUDasx28m;O&$F0wmENqZ@sPXS541% z)dmNWp37brPCiinTEl76_|v^@ceEYTq4O9gV?KrxS6NN1e)5AejmQXkW4n9iNyh85IO7%;lwvYsW0tet&4Or3 zvhiU>r{Qv4d_UAVX^M!@2@RjrQ&gA~6-67Ar*0BM9Ui>%PJsxCA}RqTNL2YLa+@@3 zI(AdamK({5U%J1n!oN zii19<_KTt}K~tgfjYfMs2e_ywo}+E1tVC`;=A z(RiBaU&%89KCDX>AOQg2dDE-qOnUKPA!o={E3nZWhKyQCQ(t>@V;&nZl^xZ#7=>sy z|J1kch`tx$G&NfZtdH;+!~{Mt@i$uW-Ok@l@|Rp>&0usdtwEwhHYkq|({5Jj6QY4* z+FG3CFUbZ|@7ZCj4(2{M|0lhcFusFAihL%)xyre!51L=639^SZ-_OAc@%z@seq=-K zn5rV}S8%+`;>M$DHqGd%qM{U~>Rz@h+~E8o$q$opQhrhz{+lZfK>+~pOr)U+8x9*w zn_IGn1-4pg#-NmfM*pQ_Tq`%5?@)E_zV-dFtm$kvR9B_b+}GF}%?n#OzD%}rjG-{( zlg(PakkiNeGJ>&j6cXRAPMuF@Ve8uPZszV6N`k4t#9KM%&zFO~R3PpOd5ekWehK3l zW37RciRSBD^N}^MvoL&e#e=<fCnxB~BQ$>k=G z<+Ea+>65OjZ|Yhdx9%p}9vmr3KyYW(4|(0CBku@?WmT*m|4mD`jIP9cQxfNa6P6Ir zoOYC;(LgBDVEVko;r^@|^V4IH_II~9O^&rrLOC#&&|K^_KUiY4`i;t@v5S%*GIXyV=5DDL(bs+&!-z4Xk@h1`ZHqnh7#Rx zXlad%seio=d()2>#oAg<{kij>)pZ*hJoJgcr7;vj3jqx`@H>ys&sEQ*v!C#gcX?tZ zD;lwC1-3pF769OmD%tnBn);UaeZ`QTd&J7m9A#^rPa9`P_Y#650Q7!*uXDck_k%q= zwgdY2vIyBdaHnSfULLZjhDiDxUM1*9y~}<~%Q0JJx1{s5lc}U1Ngm0Id%Bn{?zWtY z>2NJOG?AT0r|-E|`B7n9v#D#R>7K`9l<|Si1e|Y?dm-RlTu+xOFj*8%o?*VtFr71~ zPVge*BO?L+Tx}hIecj4SQ9^r@tQU2@diZq7gV_~z6fsxr$((M zH68BJ!drpa-)sja6f8)iLoMeRVgW&A8G``#EEKYjsDiMuu0Hwl5zcMq1jf z7E6So=_6q>nR2jUgOY$np6>i$_+@An!r0{6MR8V)kEWFLeTuoP8=H1J$Fp11m6|4y z$>{!+9B_PccA7}D3^kFm%5&6>#};v&)pglLcNySC#1m31QW<<+9m;%67 z14aCOR&1J-ARRJ#rBnk>B1Qi}ee&dis1y7E#^z5V4NqrhQvd)!9s&RW0002EQ&k-Y z0000C3q?sBr?<1Mt+cJLxyrt}wY#{ou&uPX!XqRjB`h$$-b+jdK>&iBSaI_m!4h$^$bEFoY3v`YL z0FWTqZ|BWy^03);2LUN?SXnQySqzjMT9*8AuRhGqwtm~>(bE2F83|rTeskSfKjzcM zwp?AXBeRr|l$`B&Kr3OKN&LC6H8YdLG#>M*@Xbifrc9UmV~;3;lS?VfQG&;*#4x?v zA4D8;DfBXQ+zzEXJ@xjKGe&{Nh0TfP_-L-@bPyBy7y`ghqBTA+6mLi4q#HSRtSg*jI{qDU8$r z!p9J0^uyC)OQ*!JkNKKDGCg(n7Nq>zGG2R?>^Q{jbXrr^L&ag_ERxZiY-Y_4$vu&f zUoWS;vro4Qu+L@iu`Xhne=; zXrHBcy+X!pPLgD60z{@{1@U#R>%bsroOlv+CPwLlaZ=$ojlr$4L*qUe2A=$zD-VtU zQL;ny+Cp2gRv;idaEhhb1c#LY8%|cqA#S;JbCwsqbIpF9*+aJ)e2R2kc+1!OTBKJb zn8I9o-CD%vXyibw-8fk4%mLaOk;xIjvoQ_+-twFRu{lNA;X3I@!fG`|bgeRYpuXkf z5?ihWJ8^M9r8nyVLkZ)E@78WVE} z&sxuZ>RD#X>%NREMt!Y&>?TR+S$YmE?yIRvGB0k1A>^UnY+#&jS9vw44d_}WOE89B zzEcH~O4XkEVk7<)YS>bbbK3enZwwJS%Mb$jlO%_HiL(8T|6(fIx-O_;$}%tjB_KFA z;{~CzjXQBBHB8RHp9l*)8hx0b1i7uCNn{jW%xeumxB*!J=l2#b$;Pr#Ct7P0oj66X z2#J-wt{S9AkBP+2SbC~`PWPX_{jI&91pD?byDYB9pthn9?{%${LyOxdfe)gw5&s&> z^~t2HLAfIFA!Ji$Ygv=Vo~}qAmHBkGVdc?~FQ=xRdTVE+r=*tJf)0vJS1icwT^Ie2 zJl|cV3SEg!JSaL`75h_998~H|n7P*Yp5BpSYd3Xfpp@ln9CM32Oj; zyc;_P#R3BGj)qkW(bnjMS+c9cqE340)S3maMqxqCJ=O z>BtVlo>ky$l~Av{2iA<}y?9|YZ5}67&(S=5bufh@p$|>Eqa>#QT2A~WSekqzHVL!wH>uD0p z8IAj3K=~8VG@cT)#Y$iv9^2Cfg9`(is0E%9cE8Sr&5bz=(24DZeGXg60Y|SLV~=j+ zb9rxTY=9rnkqP8%+$w zygQ-5F&e=kn9`vHS1;Y@BT}IAD4P8i1ANdVu}en1{6hpD{F79oE(H>qHyf$h%$LD7{_KXP5K=3poOU4nb-P)(<)n@2p!@P!F{+DWdN&MX zU5|S)CF)~RQVWa?qKDO075sk&;<`vv&$ksy3&usC7sm+)bD8XkpF~-9K}S#^EpW!L z_*NkYFuen8@~46ZUhH!MK%odI5VG$fnjTpCm9J;!*0g5T%F1DfV}@AydSl$A@ogHP zZa9aP?|Aokx1aX%@c!d2o*BDSZ|2ti8=cxS{fB5O0BZV8U!^(BQJM|w^F?dt9gUec8@19Rq+&50N7}N|#8*ced1-GCG_xQ+c zCUOsTg&02Ub3#Hl0*o4sWl9Qfj+x9xvL1K0nbtzDT3MN|f`?Y817A1V+v+jMIyR&C z{&%c1j&u0$|Ec(NcU8q>3+lO7Xovfl%PwXr*eTM4+Jty|y@v?nCQazeeY#zQbXo?k z3;+EGft|5w>jFp($1>C6jeXH)hRC*^hJGX0zd}$e4HCeR`nIY+3x+zNN~$-N`#W?o zr96;UV#$5-=n~#e2fvC(3{MpWmX-N9LD!4+07Fk2j~YvW=Z206UffF>!;*j^fW&)c zjbyhQ#au5t$M&+TR*IF=UXcKR^Jwj!hShUCoA|ZQ&1a9_#<317-!WL%^g6^Pcq=MI z#XMH^c6hE3Q{=JV1dsU0Ybp;=TC(Gh--^k#PE%VG0X6Gs^s6SX59Koa0#VFr?`l*) zOwCI6#HO`vzulB6ZRG&<%?5-fDm`058Z&}l$DYC4GX{6c(wJ30I5(*KX4ZIHRidNk zlNjI%1KbByc^_2cxsp}552`-AOCCT|34&w48vC+ zz8VeuaT(>ITg-%M&Ps}|CFm}(Bl=iJS~+QP!9-5eH0|z9VL(%TI-m?q547Cf!s<;> zybbe)VfDpAP2n1hQ-OPcpmB|_kQJ|6REPkJZaeB#UN6+kpGd8PeFG96?5ha^Qh*{_ zIq@EZQ;(I0?E1NP!*-{n_Y;8x7TCtRJR3xK<#}9lzrlUDS=&6zlbuJsX2>i=kmFN)$w{Q}CsL>R7bxrWpib}rHUVDz zYmEnRKmp#|Z*p_Eg!h)~>h&{UG+qrJW{qI^Loc+76(QSTV zdRgtg?}y7Avc;%Tk(w@w0|ICE(Ddl|sO){-I$Ubb+zuyhTRyc-WY2HL0fNO<-5FUy zF<$*fn_bDD^o(uqpU4+e)|8a(>sw0ij!30}+gMKlGzKxT%H+<5zeZ;gnmiTb#KlvZ z-Xe;iFfqzUW*g;owD2!5!#Vr{p1d0?2}=Pa5WHu#0nfm2rC0G~T|>d)Kz!{jO66s~SsN-~8Gxy?VsFeLS$!9d~e}XMfazK#t&J z@ToZ0S7pF1G=KmjIg-+t-PsC$&Uz|EFX5BMYM!g-(dj!w1uz-eNxh~RyR;vL zynig_B}7rnWUoz51;QI>4G(V-3^;KF1%S>!Fayt^KAJ?Ho}WMge!Qy{K?Oi44B}Vk zEZ~;14cXCB2=-9GRu6g${P(^Y~>|S zP+AN$oeS~2xjhcNt=Gx5E7N_#00^A}wV_fU?)bUn=GCZ9m9X_n9l>!(LWR|XPj#)zOELuQofmQ1= zEmN*#X}Jh{qT*)Oeqt{kwuQUIu}>r_xy)k=$3?}nE(~CPnHO?>THzTPF?85(-N->X z*~9LPB$Xf>AG%^FJJ=JKt@avH+hNShx~Xw8eC)5Hg)=-QB|XM-RGUtxRw9~98Yoos zbWkJ7y5DPKL8Z11#sr?2)n7sfofPRkMeh}ekAIVwvXFmjyJrH%^ zAM|xAAr1XZ*`ltK5yxtv#^w1v)*4({Z^O91jqY;~sfW9EoUf|h*>R%*7go%N3_<=p zg56A%W9}8;U5g#AD}?t&y`JZQInF4yE(S#oXSx9FkKOTblMVV6^F#qWNn!x!uR0r| z;kT_}`enLC=VnR#r5KLRUFRvN{s?(>iG9cky45t^E7^A#6HAqQww)H7>g%`js`SAzNr3*6FE1E#2>Bmq;(J<^*rbE zJkRN=(s*&`LpZq^Tdup+f=Iecq!G{sATm-wCo1C{NPo*qQ@*+9ImVCVE8onUn|gJl z-dC3O0fvy#WY~?BmI;2($DGGZ=Il=@>z|3H)j34#sJs?$%WRGlKPrXQ0`+OxiwVo= zeFSPrIy#y!c;gJqm9jNutn{*oOb#-TF-bp1D;asb=R0Guh6oXKGhSiuO_K?10l)|Q z&>QV;lx*lF&V_NEng=o35bb*o>RqN}I5M7A%E3bqAnTLsaVC3l8vYeV`ONOYTWNO( zPiJRS006*y0ssI2006jCRUHTb002EFBcU3wqpz;Av8%bWvZJshE-%oxxVE>oy|t*h zubvEyj)dkG5Wx3dj)0ntmtfiBbTY#R+ZYGp7-O3rZ<%@?=h5UjO|>%z-t2pCMYMtB z-mQ-ff7}LGdwj2vJs#->Lidu#qFCcSgp|?W+r7!Qq3R5V*4AO|a=LaiWn%{Q^I}q! zThs7M{ly&9HOPVPRe4yyCx+a`363eR$2Knw4QQiWmac<*V3dz}Z6zoxd@(L~8$>2> zJ)_h4K+?cxP=+{32BKX8L%_ZhEFMgB8bLz=0eF$4cbAflI#X`YQb`;}9Xp|9BV_a5 z{qF0C3=xm7Kf$aPj$g$`r&WbC-5#t83~_}zdq>0~)uo?iQTj<$6@_uR63?d^hkIl2 z=!Ff9w-Hx8KjzPP8egW=HVoKfTjE;$5W3#8S6M@sOo=01mKy@&h16Oo>9F4R3ae=x zByB=J21c=18k(P^aMInj_>KWPfIl<15GP3oek`mGKu8G^#&CVNBOwsCRSN@>u^(u8 zA3O$66GYp+4=YWzAf)=6hf%e)_$u{0_ZFs#9yeU2o;po-v`XwoW<}QexIx#G_XO!0 zhV~Y+=bFrj%|MD<_oTKG!HI@(&m#%mRQGsYXFM(09Nv9 zfGL$RV;~3|ponwD!CF?F^LFb7UK>)FNRAwrTVYu(vSt#PfKM>M_^1rPj|3YY988*k zaswj93GEv()PjM?=m=qh#o1vSC5JNC!M*#Hqy3eCx(RTI&gl~RuR&a^7mI9A3-;(TUr2y+fZ=B)^q4~NWg53W!& zuSXgu380^WJ?ROw1O5want+fNMA2a*>3$n4)EvYs&>>Q8(EEH^*$W;!4^rQ2%wNB9 zOT5r0Y`5g~@W_ttn0DuJ*U_o#u`Ya^MR0zE0t^`7DsPl5uR5*n)K8z2ZoOcmP})d+ zY=l>#lt~1qrUcjWyCM(=d3=8B%YR^M{jRabz9n&K(ji01I=;pi0|^P7Boo0X0_`M; zIc0o^f(}~>*u@vQ1r-6wNK3VBn(Wmy~U%kg##93HSs8cuR-`9|?xy zk(@CAp8P8jLJx_iVHjR*^ej4N=p=h%4k1MEs|NglWEzf1cl-Hm`*?@TAtF(wZGW=% zSWV`aQxE5>&Ti+a!X)NnP4$~>HZPdMurJwq^E3=DN!VX4Qfi$MH~URimMPkBDM?;% zp_IeyjGdnNn7PU8(E8C>KtFdtrzl70g88S?F}jA)drmP4xiOh2wLp|fHnmK0WowFn zd$2ImNy$3j%G&W6-Te#+9>69Z4O}J+r4l5J;e9=hgr?oSbUJD7VBQa7z9W-v9!v6R zpXZ2T-aOc!txntHIr8Cund5p^kDsR77xm+ae)N=YpF8yU7}b#-M*^M4J-1bImFXJJ z#`5$deWJGwdwq^ZM~qlTT*YrJHEu3@o#@a!Wj$qG$T38>{9IpbzN(daT47=v$cYh7 znhawEE{Mhj%L^6j{kiUl%NHV^!KqMVziz$YnaAP1IEtYnjB;*tz7EAOUxPRdp8T6e z1PB2-5kmJyG1RPCv@E(;>fP=n4n@f{rXD-PcW(Ca>a!7L#pyivT$S?fUB@-MG1>j` zIsTYe#?UzBZC#cdEwG$_dWa2{sE1Py^UyU@VBpn=-uhzKgAAtW<WKA3zLPZ(u}z65LT<>}v%g6=@12hH<}F9N`>cJ6}Oca|lW9^F#R*kN}m} zqsLTzzWn46)Wx}ZdenWC>()Kz!wz-)0w6Y*>KJXw zxtw(J5>r?@O$6$4W21Ylw2<~z8@p;D7DpjP&>9eXA3InyXGa(ON{YB>KJyqrTz)GI zSvr6c7zZyLp{#)^#MMukGA>Q4*>HY9LL#$NQW8$Va0+8t>$H!o{`f$l}03mc4F&g~^GH^9)}D5-qI}1$m{RCTJgu_i@X7c`H>B|)s(zYjI2ld9 z+PKcctJpd8=?Y2%#%HSHnvenxMSzwKI&9ew1z6DFb=2`2QcOxfC%PZNh%|{5_WBcz zQdXd80*2uG)5-T@;=VMg`bJM)m-EJ^#-b1VyacvbH^=mDb5`c3Z&t_e6|!~DXJo}Q zu$r;W%T?ZWuvLPl52Pehs(a}n`!igX>u@$xrl(Jg73TPz4DT%kNcp|YVB1dQ2N2_c z>(S;$7A0wTR+FGEeGU)DJ?3Silj^KTZvh4ntT9fPI*<7WjttFKXKQL@uUU)|hyrG4 z6h5PcKam0D|E8H6`T=we977CxJ+%h`(R|ml*dHlMowkqOI-^9 zL_Gn5etvw1O+~@E>d7rX_N@}_e|{oAsw$`H1|PzUu_!6blLEc}R{f5)c4DK;U;S zDo^?Eb7{PNRa{I=j<7koFoIPpD=R1TN&qMswIbL~*iFRw-Ulf^voN6EEk6e|96&5y z-IW>K*Pz6P-qkaXwwFStAG8^e!?b|KOB2#uGsRBr_?~oQ#k$30h%sTSQBQfAn@b)= zX*!j;kgMn1CXf8Cl8&T*-4kM%mPwFfo7Sw!Oj_#(M)OClZZ0Us0bUH$3;{xd0%D=} z5@H>e9C5M@uy9yeDNegB0FK~^w00bal3|?f44EZ)^j_gS*gY1P<)Iflhm1U>lNm||K zx&2Rfv!(HpYODPd=4)DLO|tO6&hF7rF+#4`&b*$RNeb>J->*epWo-+}9}Hbv!W6p+ z599O&EP|{uaougqriK_44eN}DpOUxzB*@hpNsDs*k&|$g2&(ZiGZO>@+p7zaoJDW( zNvxv02%MSJ2m@m~sh4N8q?&T@urA3agQ z+m?3%?^gT#DRFcKKR4uFx6RH-yyu8|Su%s^!HxsjWs9vb)V*8u16kgub4hkrA>exD zP%zviCk)|An$jK0fi`=bJ>ATcazm=X3N4lcn9*Z(TyuM8N8Y1bey-OD06y&V4uLCR zqAMCopUdc~WcZjap ztBjx|9iesKD=0~Qht8%Xy(w27Mx3IFTbN0HL-QxdC$Z|)E1}E)wOZyRRy^17RYB&e ztizTT%1xXg=awS{;)9;xAlTlpP~J)#I@bxEA1*7Z>>S>sXT()P^x8VCdL;#BQ^>h3 z9zE@tEgiN=QkK6$4U@ckRsbHW!&HHA37TYp_|4hv)FL}q>!5&ubCF0 zH{6zCFLFDx;x_p~i5 zZ^q-oX1!#8Xc0{Fe9U&Q&98y8{pB?ynUG+%ta?W#j7#&^ai50~y+gehui_zDSjmC* zC!vtTYA!&hKCJ+tDphgI@?4@6x5ZRah=)DwLU2xevGT0ho%Ok7C`iFA;oIMiqrus*+VhaNw|@; z6D!KKk$v?WSF3FW51UkIA)*S`>y)S7JrdbDo5e@DEn?a#%BzvWmPm;%XI}z-596Z( zb|`k23w6(DdxkCZ+2ppcKt$d5A-x%$#Hrrl{1?x4h5$!$GZi^tc5KXPGJojHbpSoM zM}{3SL{;>-(jCJhUCc0UHV+9Wdh62Kq3#irNb$f2Jx!LyLxnl{LxM* zzW2(k^Ep}H>e2MY)xu*FMXzTXiGKjgCqpI0vGGA4aO**<60hf2c_VIFNr8h&Fn+i^b_n6mh!Sr zQbTwgW&;TS)u0Di2i6@do;CU`GLh|U%QbPDE0p5g#N8Md)1p;gkvw!eygZ7lWn zp#FxZTkBhLkAbi)^r5YeDdMsnLiqVksNz*Yy5Rt$4(Ub{!nR^}riDWQ6?Ran`zbv{Phr|d2dyQcBEHWhE(SwYW0p1bep zWb4Lt^Ya|rj(G-wX9S9ua|P`SOYKIh?TS1JB;FYft9;OrTrIVCK2CqNttNKeG2OuH zL2;CF=8)~0mpT$oj%Nj%nwr_I%__a5#h{oOHT^oq6`UiE_6c+fbDSbZPJV?nkIjdl zZo3ASs$&rV01{>)zJFRy8>ahw?EV<7$n46sGJ=N0paIAQwlN=-P8L09Xy!Y&yqjUn zciF3+eqR537nt5qH2|R9&C{O~&zGNsgUn$;rGX6+KP;mv$@xrG}bdI1qrUe7GCv)zYU2JVQKV z8<%>)rgS9FX)>GdHW-SU`@y}TcE2(PwLs4fBs+v`&h;Nh1X7~pkNR7y9;S1{JUmW; zlD(&H-+A)xqtvxQgkJIw{x^aGTtf+@es}qmP%?eby3s5z)5Vo)RYuMi#_L@F$()AA zYy3Rp&dd{0H@pgbL5kkz>8GDT9w?Fc=buB8H7enF)Y;fkRo&Y?_FLJvVTe($_3BRn z$q!A%P667>6A1t^0|?CV_+C>cXOoo=-!kS2&Gcc|-4d(HqxWerOqp@A%z8txJN~S% z_0#*m_fcuPVsFWQKKq}Ho=h9(6}eO(kc}_Z?uj-y&j&OmOx_%zZjKtOH*-7Zz*!>c zuWPo60Lef$zn22s5B0js7pH^FxjV7+fN!1~8I|880&7F#)mT+t>Ae_D1{7Ws){Q_ET)3 z3D_)x?`|3}!%R~I@^9j8-vXw#B6|CmGh;aQ%{X5|mH2Em+{2KiXY)1NWaqK7i|V;V`YBYO#q-y zOsd51!d%zdS%Jyvt5(3()sI#zu5ttyB5>ywe{C*xP*9{vQ$tj@jonS5 z94IgtH?z@D)pV$b<#nRi8XTK#Ds^rZL&%-=^@|23MVFENooI0nCG@|#Y3%ON-qpl0GW_XIsZpF}pJBoRl*&_d8PiYay!aQPf-oOk5_j= zd5jB+;~Kh|QpFlM@V`8MNpe@vDCu5PpC4)Sdsom0{5aERoPTV830oI=;8Z5oUZNj8 z%n^YhHgbvb|8aPwc5ra1n-6R~sHz1vCecUK5;Z+D8qW7qq3MZZlTYKE)nclX8>#BW zsuCk}3>O$oPr#g_mB$hF_?!V=n-dJci~$`TnHNXTYD+je@rovJ&$u`OfO0YB(Tmt^mx< znlOcrEfzixYCfh6i|h*)_1v9sAR}r+-8UJp71ay?5?}PJ-kQ1DM*yDNi^2v+B4Plq>$;9jGueq} zO`{WsRr@koP=|Vpea2-pYc%du-+Rr?(Dhmuc0RpE#fdrV+HKKXcceWhM&{UdQroBO zMW?Rg?D=oJ+0dAWtoQh4sJ_#aHDZ$a*^|5Blb;g@G_g7TzBv<)JpRmONdi1wB7T{M z=KHFFZw=Rv@UU5@1u$C}wF1UVizLEw;6$%$OP^chM{aNN`)YIic!g|q|R0OI? zSO|JDV{*o3^Qi{wa7PmrHt>O7h+5HtB&?~{;$|+s3~J3T`ss_^+V>Rudde$L!F{Y)Bq%9 z3RL*10G_PtjQ~`FBpBfMnNa7ZAwwx;NJCp#wNlJsBV~#MO6qy#H6mPT&^Tzlxh}aj z94M!JSNf8jJE-?sPGYZ5Ty|{e2q_EMpWLi#y5u?f?e{pU$Y&bym-u<}e(jU?=QYivXa@ib=S{~0~ zfeoup(gfWr(Il&d>Zl2%s7(p#65T`|Tyq{k6BffHARJ%63`lGwKjdJToorNyjV3|Ui%({*RN^x#%)}0RHC$t|F^q=R%`3NIRkyGbq6k^4m zuUj+eVHnv_V|v}!=9VAC@-fz1ewg_~%xC#d;8aEJ0na)Rlq;QN@;T6*%*Nw}jqDM^ zOsV4GPm%6T_)ro9*p;rOutt*D&g>>i6(Q0iy@sf}DF}npyw&Fl@Wm89e5(mTkw8); z61jLLZ7j3MO{p$74arIug#}<`Wg2y*;Yz6B(@O9||0aurU;NnR98WmpkSMNfvdemC zw9(sa^r0-w1@YdJN{Tr%3xdF(JhXcpoyGtyK2KcJ z=G@Dh)MG=%uBU%Hj&sJAr;k#YUxs4faSoJEMOE-UP6X75Oq+1C>5e&%EatVAcy<$K1$MS0(e9R49 zQ^P5p8bHp=u+4k4LvsqP?q}p2Vggpe4SBWor^=ls)V=jYSPJ+Uyl%CpZ0$J{>la{5 z(Q1-~q;t|L{!Hp~DI%!ilHg4MU(Ct%#I!4HSek>|P-mLDu+UE-pRtg^HHfi-JzCuK(_0(y7~%iL(ZtP ze;zX-OZ+C?w>o_~Tv^vwea!x?dggEHx##PXHAvsP#-UTC=5zpbL+!-RYpUqQfkfiB z^Z56)-|@yO*P4g@h$(w>xda*dVr9wUT9Cl4N4zx2JuJ)TguYnA(!nbOxLL9xiTvz8R+M(?D78 zyrZ8_=kXiHEEhSm_t6|r_n}1ZGlAqA2Mz)-?S@-Mrn#6r`4E=`4_*vx6N&5?UpgzV0K z5~ggJ5^qF3z|$%$FnS17P>BDqX)ggDT$7X`o6JDzlginQ@u{`mX4lGxmmVYyhn1D} zs*+GL8vTp-YTByg)a|q5h+mq^g!#YkW9O0ozBns{c?_;S6i>OU$FFb3;@UOhzM0=g zuxU(>t9FwiS?eXqcn=6A5KyBWsl_ZlY{xXLSi}w}iyi;!$80xhyTfKdtbn`B-SIJlWMqgE!ot~w{TP*3e_nqk3_$;X{EyBqCYey;eA*)P#lJbJIK zo{`gN`P926vwXr)js-qE^B#d90bP(n68ZHp$H^vj*r)aOIkpWlRuwBNr`j9{BqG)8+bNzNcd8B|;>TW^a7L8g>hb9Tr>i=nmVLWlgE@YhcB9a4euk7Zk#$ z3k+Udiwq!k3?>pJnV!rzn{99?bJJ*Y*EyUcXN3<1=c8fg)9&I(vrk2o<(`X^sCH9Uh~y zvBHzJ4m1}M_tfRl7H%ohS;IZQpT)*B<_9PyL!^$|I+&w?U2GGrsUX~$o_s3_AVRI6 z(5G_Q)zMR{$y_t;R}8585IB(x2|%$zH(G}%sRz$z5s!YDto<}*^jP1MRjqF(^>poc zd=XiCBfKh;o-f&!ZFjZJKxBzxz~Rh$f06#VkDZ~%A^K6)saW!3x&Q-yZ8NxgoZhW2 z{t`WP=FT!?Z8rO^`YP^-D8G2&_s*@}qL?-5Bn&%s#8^cY*IdnHJB9AUbCRUHF_JOF z927B0x4}{mz$ZS%U8gh&nnXT3lN3O=84$8k)_8iJ4I`zKWMtyZr@@{|DF7JLd@m}& z5+?YPC)}h*_3`E~ZQIYDXuKJSF9ep^gosXxpQFE;@w`&EiVbE^Yw+t}GOXm8sxcATX22D5$XU9@l zYMKeoPnND+d3@ugpdG`m(E@>xtqE~O9{^rVD@6rI0$RX0y*@G`(YessCFsN{N(&%I z(U64`7Ql%!X?S}to7E>WnrZ+2whF2q!q;dviB? zwTF4GLcz4mKM2`3>X7kb_QP21utfYw~!jZzCWnkuKU;`QKo zilxxfqpj12PG^^9wBu1G!V_{kcOT6dX@Z`fchM!}xDAI_nwidv#C@1!p~6?q0KOdS z)xt{+D5)WtC_SYm(XpY{b^wV3%or0+4Iizm^V!JVeYfuZ#(Ny|i6NT|o!fX{!q47B z##Z4SYpW=4DsFH*zhiya+{sk~l)rw_Oc@o|>+cJO9nEj=mz2}s8AFP9kG$_m*4>`G zJTx}5(J+dfaMemP849Eh%ovV~KBi%4G9=BYqOq!)6M|eyV9;3T!{(l+N;|Duqls*N z5!&m9NrOV@O^GJ-a35YgOO=FDi-Z&~&QGmQDa;}4ZY;-!6iycw0!)KfS;FwDG~+XM z_-l9@A#VF$WA!uIX~fp58uYI@R+_rlJeDwN@BMCw+iJAAE2UYc5p7efGHH($X6A-q z=nbJRNT-k6t6QA<#qMU5g6&8PeZ=?ZPZVlOIMl?^QNd0?gXi>i%BdG6!eFt}RH1(?YX^0Q0M zQg5xN9XS3Pm6_Rz!FW+N+v9`VFCFruwFcapm>G%Z*@ST|5W1=na!0pIwIlQ|E5myA zI&!X7!k6!Kt@BGMhpO&HH*i8Y&`roevoOb*#{R9a#^X3Ctu&*l7Jz0}MO9z22uA^4 z94jLL$QUFb|yS&bN)_{?qj zNr!663QTG;rUkpU`(g+J_ynOEF-dW}SO%&(qMVNGw9j^@H%?qR`@0H_IS4k80C1SL zK{TEhrN?NsXF0Pf!pZzW_XMv9? zG!!`0X=5xc4TV)NS(AaaP(iU_NFN~Kr zXK~`4`b!5JzBS)U7D?B2h56JZPIF}L%J4X)p$L=6&AE9tuA!Q{cRa-yerMUc^S+an z8LB#)PqVfzZS~?$cFmFLVoBS}qu`9cMkl&Ytk0V8`~seDKDJo$A|P>^ zzo|xPB~s4eox^XA=Z!&LFAo1Hz^gdexr48vvDte&0(y&c%|@{fv>A$Hi6XuD(WzG1 z(Z@IW@nRg6`3f1k8^Wq$&Ub4I00hp2KqBHZ$9Tr7=fOk&Y+T#u-)m((r&{8Vbjlq- z;l+A6+;N?Z|+uR(Fl=I}b z+3gy!OLQLbZe&jO#<}L&ag%XRQENsR-s~s1jlFEn@!n03Rpm|H$irDgmBV>W;<%rxRNCm`RvU6lT^sPwGnfgBmz`(QJsKkA}Xd^tvH3`_DLUS@I_%npqt!nIO4 zcd2qx@MnvWc-GcnMHNlFMfY$GEy<~8k;s$~P|@M|lukBljxD9An}a?$ktP6GRpvDe43i;xe8F7nd)4Gn$|p~6 z{ZKtwz9(a3QdO!dmSx~zFGt9iRMYiELO3j}$uNS|0$5MA^V_&LrHWI(n}lVkexRuQ@sV+I-HPM*^msivYZ;l0kB1-Ag64(_Qy=+@+@ylD=W z(R!Q2AfKsybM_7rq*|Iabe*$J6Z$~;Vupj>O|`>o_X0lLD-}Qh0PyzOO=`yyIy%)_ zC-#*Ry2D9Z*;;oCYmGi-|9d7fJlwbZMh3UC@aomY;hG92=S^DT?_ks`kfLk-jmU+J zDtKt*uoKxQei8XD*NY{9Z|~e`q|cx(qHym*h)ccXu{)3KgXIO-aSi(xfq|4 zr)Jx)=MzlWI&$`i&^NO2)dVOyVeNd^095KVmSj-gNUCbYDgzbdXh0 z9VW!|F8ci{JLq3n?pY?5<|oUC^M1)`=9FhE1>7lQ9E8XTneCpQd%-O6D z7D&AT7v1TAS2+hWg!dEo`$f)Wl!M4R7cbLPh#{3c(OT0j#iAIISEGssS4M4engmZv zYED|h5wL7PX@3)hRq6HyemrZDfWQDiM)|2@yJpUMXOhs7*u<(eY_P3i3Pvij;?iG6 zJnC+7GoQZk#@YS#ewrBgc*NOBN1BJ9L4J&L>`l>;(_z=gX>XGp>Kn>ab|5!_O$quF z9f)h?T(0M_wcR+s96*&~C>>QX9~b+S@v!@4{-{vw@kvt`yDFc(E^sT=eF8o+{Jko= z{lbw)EC;i$7&!8qCKqAeHgq0$_rMAF!8<*NOyqSl$o-`FbTCY5v`JqLRIz|;Y&*Hv zKMDjv){NpD7eN2Tc?lhdp_yH!`gyvj>K&iG>CHEnQWlvhNst!wp-rCm;k% zASrBXtIbzkJ>*BmVa&Zl(zTm_7dMeN*&i)-I(dHj9*xZ!C_|}0e}qKzzDCaT?)l(K z0aTnxY+HH?sSo6#B#x1dwC6QRLbmn;01wh#YvseT0e+o34hUcY33w)RS!-<@IyjGC$bFV&e7p%K{eTyU>yta5u9R;Vz%k&sS#80#1S(7l-Va<*} zStM!gL99Yhzmf z&mFRf=h}TV2E)F_ep@uC*P(eSH_zxZ32>X+8$aseiRWqoJ`-9qY;G;2P1h+E*avJY>rt7^+j>>Q^VoVc)ccx2l5G4R zhmpwSIqDZZ&+9aqU8_BgB^X@~D(B~Z2hxr4f1U%F{gDIIv~&S;9i@3=V^WDez#Dc5 z;Jl%|2-<+vx!b1$XsI5Lo7u>W_9{+RoK63o8`Sne10^lmkXH99wVD-fO#7}Yek4Ri zhe6+3CI!uU9DJ;ZW6y^n%+C8`IC`!r_LN=$re(dW%B_)(0A4JMg`(jJNfHf`lo_Yj z&oZRYDWqdbO@Y0_Hr7*lh6gEkv8ApwBe}Odd$Z?iu;Xy zvcwv5<&*nf@Yn|R65MQ;0G@nnBPEI)6o9AnB72Pxg0T@t+9MrSrun+AH#lhmX9#$@CtL5W zV#RMi{$RIfizJ_8zrPr&*7dxh3**up(@In6hCz@*rZ!Yx$-2qtyqp^6!}!{|7>Bk^ z&s!~e&2>d!@4Xs622CvcTfHBt^`3^297N2d9oE|Tsc+xfp1iW18CG#A?j12>(S;Rj zrnkS8)9jX!G1UWo@0kc^KAQvXQ}Gs4(KFk!AX)&)jaA&>IRRFgvo%HLZZ*Zs zB4gyl=Z3A!>-$OGs-T$&p>e}0HerEPu#J(?I3yv33c-dy<;o^K-kN)nw|bvRgN7Aa z^=06#*gTY^r)=HKsSVymhE%c84>1fS*G3D9Y>ic@kV`nLo_5ftUbT-K{EpG$>zID_ zy5F-Ie(syG46DZ4Ac6}!B&W^zS69LaV(9U1oTzT)sq4G+J$@eFF#Du3BjTxE29dvBhzUn$IXPL==ZN;Qwxe^5L0 z1#H7mfajODZCa7|kyA!Y%;dgpzsdcC*vR`T`YL+_bLkqa3a)>J+=do5VeG)swUsYl^eAz3 zz<)Jlm*yzl6EvYmxm+BZRIIuxUa3Jw7aXcvfgWx z2tt<<=mW=+PHZ>DA|5>CFg7w(qz@qIQ^HjWJU( zU|zdEShb&YN4h-mA|4j>vY(?P9MhC*tLWsBrc+PlG~6W^j$BRe{UU%oKd1L?750)P z5ZrQfwnhBYgs1%u5L%K}FDNq3}$5ZEdqg zo}O~Do?P2HaHW6-tgNh@IywMALU9*md0*>wU++6#EZ;2s?(*lIr{<)oiyPt-aS{Bg z>NI%HA$SZ?b6>nI8Qj3FRa>8oKKXK7u@jFVR zBuQs7dt}cvK~0*z46;G3bf7RoD+K|Ym;zpGD+0kHAz%cAkodU}Iv^264{Xqxtbz@; zl@qiDfRa#x68D2_qkYm{yK3xNua}LF3g7XcV#@{Oc|`!G?0a>Uu8PWODDL@F)2UhD znm8BPNx7>iOKl%0bWd%lV>XLVYF$~FxYe^QtlX)~!`UEun|nN+mnEeBm9b>w613W# ze{9Wy-PX#)e?C+|onqX96oZl5_uk7aTDx(x>pc7HNbOuZKCanw9YZEpEOy~eU=F>} zX;Y}8Ok-$jcmQ9>Q8kMM~oi)F(3X)WxEjC|d@5WOT;6)VLF762fl z$-I$^pT|nBK24T1=Xy!Ozw*B964zkVRH~{@!PD{kF}*14O-$C_?Z!zwd6n!(ZbBrT zQu?|4QbTX;mmPb|tDWd z9Lam7aKD>1d7~jo(@sCKy^DKN=f(58pU_`|mM#iHpaEh(G3h)bX){(QqB1|wcGTBF zM+j*a0G^A}48u_iN(_$Z$TJ-abj!$i?dCkhF5)&R0a&$C*Tv3cKuOh{$=DS=#Ao~R zBYy7UK<6ITQ-A(M@5;`%`*k1h*C3&bW(#=e;VMMnXpQkOqBbrCh2OFGRR z=7zeyJu7_esCS4I`Bis-ci~GdQ%mADG4M_wyO9dw7HL#fsA3`5gSqyqrvs{)C^ zNDUyIX<79B;~mw_kZhOfV^{0D_B;~xJ?n-$*rO|M9=7gfYq%@0#GxPGGv;||Jlwz% zOFFE(O6quvw^rwQueBIx+hXYaA(o-Tn(<0bF@)K^$9dP4!GvS zejW_3t?twqks|+crKjmz*1TxwpJT~wQ8k2hb(LWfCbGk9+xh2UrZmiywt#d|Hd=( zYze&wpP)lmv6K*qP1%H9K-5kgXD4(?1TzUrZ+OhNdcTC|TpK z+Q1wp%^qUx<+kVA;gm_?r~b-l-|h64oCNfa&;yYWXcC)k0A9>%2|xn?;O(^=Xj^I< zI5IYIA`1XkO3DXXLP>Sh`kl2JFV94$#+h4Rp1vM0M_qk_T6d~zeB8(}(364&sP19^ zMe5P~l{HRi|6kg=J00;-BGl_<%3*w1)49!^Je#~v_i&o>0)TvAjZeYNW-%XAPjas5 z45zD)>k2evjGjM^`+q{H$Pg}nb}V)hhPF$|tRfpkAhCBl;zi-Qp-&WA)J<0+_(PUMo~~`8A-B>{v4dE4vp9H5=GvOa7gag-C+4OPXgGP!(QB_? z4^Qf`MlNFEOrvfAeL8_s;Tj`Vw(Mn9URmpnsZ{|`c(SrHdXq~vUL0#P0!V-eH4IPb zX?7xG+i;g7wUB`m$piv&Wd*i93ng<=mkp;pRYQ_BdfWE3V3r<}wlKQogzhN@wc@;{ zdNI1GxB6&TW12*H(|a}Zm-+t59z6w?2Es@+r+4DXL>EHoJTf7C0+2rffHMj@L0a0&;E0Fwzf7AEom6;pa4jf*!bDC1Drz z{6{w=^UQJ5s+IEWR9FBg1&|tg(1~;AuVM|^)0o$h@dnDn z+RDa&-D0UWX+!|P6|G0(loIVw$e5;!(=S5m;WEghEdrh@ad=t_vvbdc@mY%5Rb6vK zb0qJb*aD1aPGP{+-F3_eBx03jSK9T5ayJ?q&+@8?oTtmyaB=e`j~#FL8MblkgCn(B zv?6(JwaD5DO0T{SU-9nE-ihsGtzd;zY5)~D6P8pmzcS>Db3Wah8UY~zW8lE+y2WmA zV-Ba2(QFf^;8+0Irm+`f)FJbjS##}W+#OF{*OA_k?&53FyMjegT$1Y>oxj_rroO52 z1qO!a6qM?m${U+(^spq|sZPmB$2lVk^IzMR*9!3@^$09YK6>@r=O|RINnjvX)_ovU z$mA3lt@nzl>Zr~IO{bnuNW6~5T7nmKV}>C2l~uh@WO8tL9`c<8^mHd^ zWtXO(K~}nKg~8;dCv9d+CN+=b>!kej!%BY1Lv(5j9pKdSq@+KaL7R0#;q*tF$-$IQ zI7dY-8!A<=fAi;VrU+(kRlVEQiZ&Ws^Jq$hEyx}-a(*W>M>TCNT8t-@c5+WCg^fTH z$j$eO+Z0AEYG_$2v8l=fo^+mo15zqSQB(3mtzs$;KCJVOfF>j;DH_2W#Z7LejV60M zBOo~YKH#U?0s!3M;L5Oz`hv(xWBq?5f~t$vbB zZ^W>hg0_#E;kK%9GA3n>yR@WGk`Df>VxS9GTTAF*YM$*%X!wC4uh$w1yABmjn5P+q z85_F0QPh>w@R3&QP^-wEkDoRa_i%detb#R;{FVW#`JZkHmi2y>x{M&c>gS%GY z$+czBmcYm4O=Eb!gs#54D}N$xaEG0JVlI>Rc``Zpt{`uCi<^>Qba>mIDA=V?J! zPEJxb9!+d!sDCjSpcOVWXX0t~+?3@yFx3>t5(_;jCdH+xWE-6(m4%RaGIrBsfROF3 z5XR00Jdd&hUV9T7L6ZhNOXk4a>DD^aVuT%-(ItDqIsmJtX)iOy!KmwmS6fy!ws=5V zuHu`1M^ObE_(|DJJqF44=wv7M){rYg9sZ0NIs*mpSgw#whBK0G0~E&I0q(&JZrGfr z5$vGfLEUG=_Q3|w%yzG@1m}lFP9~u+9?Y_v+CJ;1rEeW3&NrG#SpB^b#YA{Z=0T}J_;Qu?6&%@x`xz^Y z!f^*nM=yEC!)#5(({%3?X+7pstx;`P**x^kZch&aAfZ&r&q4B=*XBKT9jWtL&Lwa`%bj|kGXB(SR^vT~itd8} z#iUR|y>W>VwNLp5(75*~-`8cU;5j6H21!yNMb zf&FfMoJNoAIQcA8kXe*2%_p14{+Ar6d{4{kx$5bq&qpxp8rVZImT|d$ z(U&k}QIFR3hp6{E$D26{pcMT&Q+1arQb}Hnt5pG40a_|Zh~A5A9kY#9r>!wVP@e)4 zU`(Tgz$sH!-L5Ql&W$`>PFa6)RbxE7zK54vw;j1agti%Lo88DKO`>^vo8y0f=SM5f zUv|}*YkN)_EjOLm)4n%1v1#8n2j>buo|})n3?!1bTWKTWQeWoK2?~xi_B_&z*K?xm z(EZ)k$+8j~pMibqb;cU!&G*!Fq6a2j(P*&F&w0eVO_{P0mzV?^SI~6dq;ATJA^?7T zD~*5%Knkqky;3%DtkpcXcC3w+YuE}p5mG{iTp?|g#(wsf&E~N7Gc-AM!>~wo?Ocu* zM#&&g6HU<`V|LcZ%G=IWIJI<%V0u^mY6O zd4tdjwu0m43XI~mG$tLTdz~I=Ienf^&)GX3p`xEqqKDQw*wEsPd#n~p+|LTnZL?eJ zP|mQ;-Cg$9=LAS5hm4ZiE=9`@PiJRS006++0{{R3006jCRUHff004qL(Sahlu)HHI zE6>2ey0*wBA|WI&FvPC4ueQJ=BO)m)gJx4N%S65~2ntiDVwZ11 z)1J+1*#ll&>x}>eK)}G;>};!3wwLXL8I9h{Q}#*+9h@5O^0(MX8*ktG+AmL~cGml6 z{tgGKrfN_;9fa|Xk%ot9-E5@xap*;l^#J;@K0LW`m=2MH=J4*?m>H*qR5!X3l*kc;|Qa6~*_{945GixxXe&X_3<1^WSXe%xU)A-zL>NSFv$i zunfy%z3+9K)p<$S`OUzIl9KGnx zfT3O%;sFyZr*z`R+_vy9h=y2qrGu{NF+oppo5D2+Xqdk=wXzf}6p0=wt#ukOqQV3` z;=`O8RVO?_c$o7%CA(qMF5CNB)nSOINAH*=-nF*#xEAVzM@l}I0ZchE=KXsAaS{mtd-Pm9iKJ@s{B zVxGk0+9^l$EJ9%R1Wv(JBPt3)UEuoiw6regNf{wW$^gu@o9>B zwJ2Qk{gUy{HJDVk>LI|+rt8BiN0Z0+9^hOAV|rH^7*n#f9@rivatg?&;XW4G0o-?tKE-jjTTFCMI3O>X!VXH?##SKS3d~=jSNl>Ubr-)w{jb0X=7tOJ~+Ole1%vZqz z5I{nztk*8;FX>&MSohuAU%u#^4Ef%zWiErXDSRaW-dlAqFkXLbf{E< zMahLWrVwbF;bt_kqP=9a?99K(AU^p?k@^B^;LbVa%-ln;#AAt0z}iW|_V6aB`I$f*FU5KUO^I9jb5`>Zzgg-2v_raeRY8i@mGm zxO#YxaK;yA9roC3{R4(s_uMf&(87KZI0vymWty}1`SyCYc{7;d(6mH!5&Pxz99z2< zVyvmp9j}U2Muc`vNcY<)vxJn6{@vNm0CKgCb9)SvU!ggv6qs{$#8G4eK5X-nLRXOJ zD@Z;?N6&#hM9zWeK&D-_Y6Z3(E;|e!<9zm0=CY)kL0%L8>DS!^(_@@%7XM9!u_1*% zU!yn(7_~s;b{(67I`-3)f!bEJK`=@ij%}q*1320=wxwG$@Um8bLmt_k<>r)Avq^_?~t>vgoa*4@5JqhFPkQ3Zn_iqiF+VbdQLZ$Unk_XwSY<&cVkmk zlh>NMp)4!3`<(zHFyW}g!h74QW&~briw3|1GytBW1JU5YP+MXKv8LtnUe1uYqOqw5 zUq72pPqkjV2j1PdO~tu|yF=lZ;$a)kD|CIf5Zx^gc}WuU-Q1SmH0KUfUu8Ez$fv;0 zx(NqKpM5;@ujwI`^AilC>D@7o7XHrD)}6a6H?&^<1&r524^m#G$hB1tOk!M}+xOXI zGoGu0gzxFw z+v~f1)Bn4CA+tmGZJ9&X+5<9a8Zdc=nSGgw1VhjZMR0De5D|7v;(xI~LtyRhuu(BbvH3D~Itd$r`@g_R@<&wgRkTP!xWtNtZL) zm0HT+nKU1YH}gz5JB*5vk8f4Ao9oAI%lSmrG(wb^@I{5*L&-D&Og|uv{{8Bm13A+! z_Sn;G(Xf+=d8((wm@-<>T69Au`;{mI#sEY;76IwkADh6YXZL;Or*fx!e$_GYCs)kP zQ+NGD1{C}PEIUUb_V0&!!6qHP|AxfNv!2K(b+uTggBwW?N0W3MhB%dq3z3apQ=D-{TQqFHTeM_plAfq?!PQVW8 z)6e^Nt|#iguQTbN=)eqi*K3}sEB|u#+L$9Q9{-u+Q6C;T|Qq!Z#4Kuw&OxCEnV)T>EkG(*ifW zd+{6GhYuka>l^{f)HOxZbbknFx{qL3JL03dugWThUfgz59zcMS7toOp<5 z_qOBJ41Y<;iYu_Q&XMVmoYiL))lS*#>E{>Sbdz%ohY>kPW)tbzX_MZ)QR(p-WLzst zJ!zcNb1z``b2xQ}`d9m*EUVAQQ=B1eJJC@n6>!#!?Inu6OO04t-6BDEDu#S~uu|2K z6-%YQfuCTzoAGX{43>p#j`p!A`>O&ze5;9vt`4LEygNFV-00kBJ#Wan>73Pjxnw$! zB4m;sV-#&>%gN7v*gCwEo^H#ut=T;F?ex0ieyg;7Y9@`RnbbUb)BE`6mgrXAlkyLT zynDGZQmIB44tJ^t=*F3LyA2j*J~Guhx9_D%&g`@?9eN(96ZD{$>yMy?aR!Weg)arV zQ<(s8@GExKhQgVg`l`<1i=K5Q$6 zfTjuxz`KjiCDUzn^lr8}AmqM6vlCKzaQw9a&oaJrZxd^K4U^lRg&FL8qA2>}bWQH4 zACt|0o0abT9ZlC^`0-OI>h_~OAO^zZr=Vz8%zK_SSwT3Fx*XsradRro^>n?|+@Oi? zzLmbhfQjx6$HTY99K?Wvxt}n|CLyV+@SK-_2E3or$TcHJPaTwYV#SrvN9UhB)|e6r zc8G;O3xY9EZbLQ~7^4_IOlu3te)%!;@I=2e(ikb}Z#doG-d+PPe?j*5~?`!iVT1JyUM(d2)vnIA~cKM8|CMkET zKC=)D3a6|)6@pFX6;C}pn)lt_Xe^qf(}$nJj#@LDffjNs6dxZf7d%4X4;8UpbF1lP z_A<8K4Wxe~NOAI18jy)08r(IXXipU6K@7S2U~d4-Ca4(WNe$KD^7W^mo?xc}H?(;)=PDiyeZ^3RWSU;NmO^VB;wd%6>`=r+7sF~us zj}$>xH#?K6$O2cgmNRm1q{Qav8HJCj*bc*4sm)XGIup6x^>dHa0beM{!-r2}0Gzuc zZOz!wrHiL%1J9MkI3tfD%`BHKHXPmYq4(-dj=kqgs{={pwh^zicg@=x*~&I)5D>2I zJkrJ)TPedmNZ2|T259(0;oQWq@n>$+CM-X)OwLtZWsH-byibd&RH`PA^8hTth!oFNKiOTW6#3RpJ=Z~hsk|_EVVJj$bMyPGm}BL+kEZy) zQE&XQlG}uwT>*OQqe&sa!I&+KgYdg6{=Us7kB9t&{( zz36FE$nt7$MVSF6G)BWGpA!*){fc0~ zrm&tHi7b{8Xf+C_HX0EFM42nsJ;$E?r0#&1?3`IqM~er9$xnL(Clm>O-1ClyD1kz0 zTA(FQ(In{=H1k1YC8t;oz`P`@2!TtKy)QYW?9ro*;d5hJYduP>dptWazX~C6a?6!kr^HQkeG3+e$9~#}}1R6enq-QVkZ-x*{0t9Z1 z2beDa%{bs^z44{Q;RKMlvDsU-D%J0_CAufv0LB~Wo*Sp~bt;_LOq8_VBBe8cRQlSc z%tvVxUd(eEKo1ZI81WPx4PFUNvJk<(aws`P1Hd3V87)!WHtTxyZIr0-cMnxlB5qz{ z{{^db#6=p&`0iiMu4?;OyGN6J?*?rOsu+YOfTuz!?N%9f9jsc|J)DLVV}+_NPc;}H zE}f2OfT`1bP_Ot_Pk*@4yk>>!x8(IIE2hT^>8)ABIr{~h2A_dI7mcq}7zb#gY<#%1 z&1>^9DCLVpXD>|vbh=-;#R*kI0A9>X4nP9{;3+y9M6$9T2#DqsGyqF^Tmb+~Fat{( z=?J1L+M4;6x9_*|;g;t#uQzSH&6FrS_%2VIGf0<$SOuDXlWo(rt5FG!_s7_w2u*DH zX4UUuqKm_4xE}&idA#4QOCs{MrlWUKztf&}4rXlW?Kz)8oBGCZt=ca(32Ll zKp)58)o3H#R5vxru;POeTv5=KCCN- zfF?i@6K6c;DLPtfcfzX|1HH?tRa4yu8Dy7v?F}WDS3W6y=CY?*(TMxATzpSD|5vAo zGm{dW#LD2}mc~WS8?47^69i@I*oTxEog(|h?6tvekBmQO29txZdp?>Ys*+83T%10x zwlFhkBs@yIB=w16BaFy-xAwr`q4>BGOXJ()GV2&R$(t4*M59238-(=+;s)M`0ut14 z;wPjIAXR9N8I*cvnhQ^7XHx(GKsE#b00000xKmXf4FCWDdP-299k9NxxvaIyy&^9x z#lo|*v9h+Vt(>f{xFaMkEWp02a~eT&&`h+LrHptRq7iPim=6}6%_*n>#2mJ<9%Lhv zjC|Z{mia}4^QaB3&imo7odKY6yQ9W{PQCZUrZ|N|T7(W~rPEpdn!%=K6SdETzfpJW zSXb6nrx|zOtq?Yu>0SC``<@&RlY<+Q);^huHm_%^j8JSnK5v3PSc9}Sqtaie6Q@8V z?;_pvW+ej%$J99+$1`_K{zUVHPPUv(p=MgxK;{HIe*1Yj9O1MR0?! zlp7O@15p6KKtR9FA$1K9vGU{WFZLv3_0-Yey&_{X3i~`u>|PF1!?}@6yNPE%*S+jv z8oD@T*4yEcsAcVv)_`$^hGfUhN72_CW~WM;&#J7g@>aOvs6CthB$LZ$J39tYY@O!X zWvuqLZ#ZEGp=aN1L}n3CbI+qKQ6&`#`8o?9*2_v4U8&O!0AyU_@I_)bZV^p$} zgUqs9@4amP?KHfbF;ffsraE?OWEfIiDvN0g;?;%Q6-S2bpRBP{ldjbn2m`-O$$azb`zd8O}j z*o;Qf4lXMiYw1;0hqI+XMQ11qo_y;aK>!s6Bi@cSg4WrPfxs4rRVyWQ4kv>QnSQhV z>{WXPf5qr=ZZp}vmxw)!K6w}$s?=F4TnD)MIA~jq?wZBd@Cf8~$BHsw$WSf#RLuNf z>=)ARG~5}g)Im;bM(tf)RHw#!%~4I>XniuG7n1AVP1R^FNoGGuI^m1h;>1eSn*-gL z+c86B-6&CSpxBIpNgh!(-}GL*Re6h4oGqY2CiJmQWiCaTK5Xle0GA+9NQSrNP0((% z)>@;66R`lWOk+LZB{+4EbW3gN7n*dr?sp5X{34p4g>%tkM~LQ8rppD_^!r9K{y33r^1^Ko-5a_jdp(;K;^em**+)=4ck_#oX( zZc*=zA4el=ZdZwV6O!%40ms_KbIch*Cp`g*cSB7UP^pkO*FF;!4_XF0K-AtbTLEI0 zv>fqs;j5`&0Df$n8vpA5nQ3i{9#zk8IR>3yytLToaTDBbPHIl_73Fb7v zKD|u8k$-^_)UbIjyj5^(`|=)$Ic+aH9#x8`;&x=0t8(#kQ%rq-%z*}W`6A*)1m&>Z zRnwQAdv)CJe2(IXFOsm1$EN+*tTP;by7V;f(%KwC&3KO}G>5TurH#0aF@E?rpu{<( z-3%U81go-P{}=SC{Yi$!CO7IpqM<@*V6S**16#z%#1_*YA<%<26-Mr3mTo zZb%*0Swn_EaSTKvGJwx`{6s&y-}RPNL~QYVWWpawu0IY(om{&3^n=QYen4gu+ zU!oy@8b)dIWO}34pi#oPG9uf7~YMiG%K>O~%(-+g1b%;Gx zyx3KlNpCo0um#R@;~*yfbw8o z=SdHGX$IpO@)pmVs94s5~@B|0n0U>rw_>j@Evz zKU3!#FS`3J*%;;X{5HNc%V=OZ%L6SjJuQ@SjEqOhrbjcMJR{`t z`%F%EHyEnUhI$JxdyJaZH9i8vK0b>RB6beLP2U?3b&EU2kzAK&zjgM_$*gOP)8(Tn zZtJaaKrEvMtYBmsqM^L)xHQ${34oAkZQnEbX!fW89=wwPKnElMo}#0ZCRtnC7$L}H zRV`Mw5_)hzD?OGnj#wV`?J}-z%MOqCkK4vB&b_@_*2R-h>{6 z^#+TsY+jH5d$oc$N50rE3OhGzb*z$Cm9M}kLDBh|&3*7Zx;|Hh;)H2;uXHmweksv- zSaeyT?v-&IU#6@V6mO4A!XsABfif`!PLND2oN4Yx&@LxFSMDi$`$)s(bYy+S=&W?B z&qfJe%<~Mu0RZ4_A$m1ESn?1fh{T>1*!D6GoV51rTIsxlRNYtK%YOSzKS6D;t2kWD z->DjlnN}v~14C86y<80*g}TM%Qm_8=I2d0AF^F19Ko|O77Zuv(#`6*#`LP(Bj()*` zBFflQa_AP%&2DflgwB-SBazno=5Fp8oB}?qbB+Laur*A=ye$Nz5rkq6X(f^qX&Qj} z%Bpq6CPQV}Ae;1PY%$`TQ+YuD&oJ8rJ=cf?yQ z3Vx9i${97g&2H-+K>l!`*mnEbuLW5KeYQL{fO?#Wp zrXE{e9I|cD9(DwyuABfDlrQ9b5$I-EOK-m2k)DqOB@8e_uF^IDiw0rC4Jvu5zCOEL zCijqIc59MQAwm{Be*5&rC|+A*Oo37`GyxKXw{wPo`5?9v16?#Hf(Bq^Wr59NIJD(t ztfXju+7g|Ujc%VuLTD46@h+{hO+StLOwwbU;{Zvz{6!t4jH@L84Mh^d zv0TyHY`wJA)@y8rl~`7-tc*!m=#&7411dK!dyiXtKjuriM?1Dj!^rvB>WYhr#}li= zRHk0>Sx0A%*)4%J4yA8YB~MJwi6*nbj@jh0WnXhw%JIY?iK)Wy@Y-OF40!QQCAnxW zu$$WRPd@*g=+Ap!bM5EoJYUHbXePsgK7;`GGtq=tc@@LnA}T;}OHFfwA-o~{MAg@R zgytH>A!(W`6+WD6(S}e*LE%c@mQ4^E^jxE@o`Zd5rNSnBI9OVW5iN2E*@u}&yPF5o zx4Jp@+HzERG2fXghrw{2OEccc-+sd3jV6`vV#Oe)c9B9`z{hM5AP7^DI=H3WpE6+P zw~YykJQ0{W^}?mZ=f*r`ybippy@dvbG&|0X%W)csPJbWvIeTc^LT+i#4Qo(0Uc-P} z^$(Q42{#zIu8BrX5FBg*`%>|QXqNZ)CTYkGszxM#K*5N&Wyc0Wv6y4F7Bj4@r*R5M z07BYH_OR`FSD)+5cElNyw`3Vil0Q;fk>)$D>QKD&cCG#{iEZs7TYo5uuH|^oT@ZE9 zak?M|YSiW5VAN~P)YKAh#UKiW;W2Fi_aHR%B#`vwElNK4o~b69I-c-eOt;)F(RE)A zrD+asl{}Q4pJKYkp8)BpaO>)A^WZVl8Eh3g4Qn2lDIJ=Vc9r?7LI*xf>xBbVkkm+K zT6xO0*w}#WgBlw+#R9+rTQRRl$xwfc9WE=n?b_^ZhYePu-7jO0Jy34Se!)eG@mRmS z-o$%;6M(MW@#wd)m9E&N%e@Wg<$ya<5H=*82S-jJBqpmnC4OK5V4Qh6i@3cdIxt%S z+9Wh9tSo9gFJmBst>u_AAcSRJIpm3+fr}?|YOdJ95SCV}k&X8pZdFD}P+{wvTU9L& zKr7BvIJP^I8dh4Pjv#O}gdp6M1mg9SIi~kvOTR~p?8%8F>y|~Z&taR-ssktaY)5x% z&AWf@-})uJzfsQj!Pp7-+4qgisv@`!zCAxz+sxXsRNnzji)o0Jb8cxHFsAlUesB65 zA@;dEx(B;!`eW#YdpQ(cAKU%YS3;t@MN|MP-I^6b^M+W~8qIF9b*@91sCpY(yPM}= z!fL4`?T4%ZhDF+A-+zLdY{Og%A~A+dHOFQFLX3y@aGnZ707O3$o#(px45CT4#x_gx zrsJlgj8;3U9N!SFXX&c6Asyxc6g_M}`bpF85^PF-nm_bXh|4OkEIoAy?;m}Ch(IYazvSVRja#;U@wwx3<|S{%Y5M=3?K^1s7Vc~;I;3TIOaOE} zm~no!rnb7l!hwXTeP{gteQM+i^)+oZmD=fMR60IwhU@UYy69@@kOEr#(;ZN#0W*Yv z<|Ds(|HJ3iANEXq%Qu`BeZh(m?5gsr>?O1gN$<~?^<}%=T{``3b4-{8w!DiGj;$j$mt-qksnivVtCcI3l~z*GwHIGBKmPM(kR zF_vivz)1ri0n2aiQK$z)WM7VADc6bcat#uLW~NosPMLmQIB7Dcm>5#8RUVAbFR|$f z+3`@&I7!);8>&aCGG)bjtal7+-f2uz$p=qoXHx(Gz>fp~00000xKmXf4gdfELDU^l z8LzUgxU#OVv#+kO$t)@@)x*fYyu!b?va}u@Y!yHR3{r@LC5gn_A{l62y`U1ZgT4SZ z!BoceggrUBx%SviR*mZEE_{gh%rR5@%Y%ojRWETZ?Tu>fToJmRVwASzpX2978)mwa zQeyM)uC1zc__K2aQgj_f=L{KA@PToQUH`}TBxb-jY18^{Jy$6!u0=n$YZ%7k+>}X( zWQY|}!Dhm`8;Vb;5t7DhHV8h0?PNP$c^8HTlWvXNHjLr24Xyq}Epn|*0Nxx-1%f-E zVS=|=%TAUJge_rVz6h|u*o$=?w1g@9cx`Q0Jl`@c)Avfddz+G0*9t}7V8pg>m0ex( zGY2U$hI&%Hq&E{=H>i>~yy_-{d9s_tfrdB6b>7@~1UhW-^v`U(D(b6W1 zZ=WkJ${(UigD^VX`t|awT0xSclFwY&s2g`1@xA2bp4jeIrOl?Sx)>*&dC8V=ORB}8 z?)PhzBq-Nm4tHWYv>kxato7ZIUO!bqq@~BERzmyJgNeWENWH|hzl3|(OlcSkUI-?( zmzY2Qi!&3ys&->3lJZOQqi(}k?^4dsHtfs6J_uD-1D^bA2>}WSAjG=`l9>(av|uHD zA^-vQ@>rLXIZy3>+T-K0duzIh1V8MXJMO&%&o29kc2f(Us-OpDj>5PXfF1@x%6N+D zmguDu)&B&sHT4Xh170_GFe2T#T-|ID>87S_mJ0b)+j<6J&IGTfN0s~&RzC$|Q`o!u zRx$J?SE{Mqbj`2Eb#zj*U@+o=R0byf(=AV<Cl`8a$sf2ARC7K)o-HP2DB-3ucf zyt*FDF4yKV0iN5_ngADofd$@1B2$fmgn$_Yva}o66R^kx_4@wpKw9D-~_wA5WY!Qb)zsLb!B+i z4vZqNaskMeF0c5xDD~Zhb%fN1C1(|!r`Z2z{2rJp=pp^^kz~>0N{>Ap_x2NHr#6i1 zO>_S$(Y0PmPY)}Z9v=1Gn>;m|*ET*9F|l1acZ~wx+jE`+08D_l^E{I)gWx4MV1_=d ztjs4utP<-qWBXNCP#e~`UFd#br;;am=n0s(lg zP)j#IS6;q1SFt$fff}c1(bz@$p3euPXx_cb3n#-K-_18EFU>q3$EiBID~px$ur5BR zv6N=C%XwtF?Z13)q>EwxaljBw!$x-{3Yslcjn#>0q@_C^cS~DtEaMr~Qz3mf0Dhc{ zode2%pxKIq-YaY5nx=z5KnU_MRt99@gavTo(KcH<^`xp#hJIdW^kf37=WZQA1+5~v zb^+wIWJ>dnC|s>v2RmhSMjm$|pPcyW5pKnyo-5ueOzDPhs51cU0WF?(%L((P=Z|<) z!4FM;ek_yfrV-l4qfiXNfLq2VA60vBJVly=qVr3u)BEsES}~uUHsMA!6Jm?&T~>5$ z^68opYln$4K;t9R?`a@CeyppN2X_#(5J2?moQ0G_wl+{e3eFKU09ML9AsIy4o_&y; zJmLX2H;(a%Z=rhf5KKk^jdXBgxW&Q}Z}q!}@yNkCn$q!uFU$IrfIr7Gl8$e(pILz) zPOoj~Q$m!N-YNs~FpFv4UZshB@6#Q|hL?wXc|39o(4y(SmQlYiBHlg0rti&nUz8y8syUgI zq1Y^r;yYt&EYVvO4v?SJbn!Ytev0J+He=TD^``4?oj78|JIO>!zNV*2dop&Gz0;8#)w%Nh1}=3b-*4#oFCorVe1`p_ zR!$tj>VtC_Zs5JF3qFJGC5pKle zlObAZOQJsCUT5oFMSVH=cs)*x8H_ENFCKr`99-zJq^g{|q_zHyI2_aTmD2wHe2x3XS@kxr6qhMUJ@-ZZ$DXczz4ivG6$*G5^T0qodbqot+@>}UPiU#V!O@@L z&Dm*Hwz4{7)dkCR8IvR$r6)USHdn&ru`s@k^TXg**yhND^VP@k^hGcv_COZxpMK>w zz0oAMNd+UJ63FJCRl81N;V?eHxJuf|rfCQD-9D;K?hQd}d5>;I|7malHmU;-&;U>f zWfJ1MAHUoEI{7yJ)4J|TCTsUa?y4nu80D2Q)sn%a=Vo*KzLE0R=V4|U4QqY-9-?)j zg}yWIi5|}~r2Q-fOzJ+69w(fY>ulVe* zn7xllAP96hvJzk*ht4vCi5`XN^x5=v8zN6dpRLAW|=MIw!Jr~>m z^U}ShOE>XfHOH3{TAPCmQzQi?r0|?m#;-5^GOGVvPTBL#Mdb8-ofkb$WXfI+D=W*o z#HR!w_#tr#BKVS8qbN%5Nu|?_C+vx7+!46Zd;kcYPG350c9k%aSnY8yQwrg`TYwt17 z?D3$OxT*7Y+BH@|*oPge18sW^@kAzaH{CA&Zsg=A1V4jp1=!aTuZDI&jV}?tO zDrQx2F%VM4r|0J@{SulAhshHf(W7(C(>`)`a}a()t}qu`y(NL3rppTJ$Eq&cj|$KD zOVBI;9;|Z`KnElM-gCB$MCM!%caF`W)iGn$$_i{deDDyxxi*@vI$E=9;`jYD_;~LB zAEx!GQ2m;@XT?J1Sm-hu>g-s;hbc{(U@4CUO23!2O&_#?(@9NeeP*GK&wFoYMZSHa znR66g^Ki``TzgFHH45F-!^JpP961tHWf(Gs@u?M;KeQ>qOoWf>t~g$thT-96I1GBt z(e>-;8KdTLer|yuoK97VVSLlCZ_PKF53OZ7jSr!4PLpu&i_THKXOuc!8B<5|ew?cY zLLd=93PP{Wr4vE4*C9!}iB&5ru$@W^7J#uMX+Oobv`J4Vd#q2yHw|M;40{;y6`Qoy zvbkkjJ>7=*>=;itG%s?OS{i{H@B)PZmx2ah~wjXQna1w z*`s%_o&9ol^*lFyJJByJ&IC*kx=W=!#r9a$Ozsx*BP$NC^?Urn=O3OAuN^L|sh;h; z8&O9jqW~*y#i#VEj1${V_Ec-;eY$~_)bRNeWdpum8 z(3i%@yAFACmN0(zbF$ayIk$2;b>gEN5xXMQ^VGAg8u*x}X|u0ZOof9vM40d4E&gjE zL(hkG8r&{pS{p9`685m4ggG5v#`9!4Xe_2Ivbaf7%S|wi&u|S<@6hDdb2WJwxoVT& zv@wOI&#jU--TMk#xZB=jK7qpojr_cN>yS_k&72`lSCEG3UCAnz$mzJ5;m!H!FAwQpN62ODICuf8l>-$1=*~30heeqhH!4Pd1PRMM9?h7GSm(O?4DNx6hTMnL3nx&WTHhN74Qihe{ zQ@Bnl>~sU3Osk!P@`P+i5+QnZu9i%;mNqzq8hT$TH)O%l01k(P{S5#8$~0|HD@>TP zN2#gu2jeWN`MXdjM#ZTQPan07=`=RQb?(NEpOjf%ut#^t!y*y>uJ7LVR%&Fg)U9n7RN(f_@ zoaAe`1NK(sJn9L>MOE72?Od&@s(@+nd$h z%^Gj!WZYE+e9!pZ*u(6FA#(d#+F6?LpQfc$xSjsg_7HL%9|QzV)JM%^olUW z08eLUQvd)!=>z}(0002EQ&k-g0000gg%(s9rLn~&EHA*P$gaJ?yQ#gw#<|1GytA*l z&R(2L1wacxNi6W@m?b<2xz-T!WhHTuK!WvD)LIEjn&CsnwaOFXHdf8&%hRO&b;LF1 zt`8Nw#nSGGrAjRjLzh?yQ_dcI&U@vd@_KkAZl)J{NU9EpHOquad}sj{Y5Y)i|CQe4 zw-amBaO74k9HVnSqV%D;twuk2TQjDt5D&tX|RjIfcf~M%%tAhtQ2H%!UOf zCKqk4a?nJ(uxlQxzW6n~^07G$u9FR|NornVih~}|5D-XNz^^lg@B4`#ob$D# z{Jg20O(`ZC?BX0o$rvMDNZb0;?*3H%Vq2>pQ_5Zqp6z>_Dt6xM7}M_iI4VPIm@2El zhM}U?;la^0>5waDwFg*}CoJT_0CEVbr_2~k;`F(3qLt>MA4KY>*~3N*BymU2UnM5~ z=()a|^wF&5X|Ly~wH6rCK{o?-VYkXuZr8*TFN^Lg93q)(jF;#z6tbEXInISs3Yrs~ z9DguTUd2($Tgrxg4W7+aI)@Vg6hTZ?R&jOI0&H^nj8^2O`NwOsMX#5uYnqtqE`8}v zWn&c02|U4eK=!3ChC~VT>rLsXL*Dove!6lqHs9as%Ido7+hTEa2zUPdiOm3cCLBMm zGR=ROvYiPclOmDFaKGW?P;S~2=IHV7XMe`y=V`bx)hzpn<`-qt|N4&r+RLL&B?tkA z7SJr__u%pV#mreel^ zD`@2wD~(e?Le(j3&QV!b$aDtKPO?#Erud^PI$+ta9CP$cw{*XUtvh5P zDvus=buVo`zu<5ag!#(Zlwc-Mpo*D~t6^^de%$j;fhGW>F@pE9h=`@N!KTH%jD3Zy zR}q2|>$Rg8E6xcTYcZyYO-<8Lo5!axTt_@jg;jyPDR#E0$@W{93!FzNi;IFPlNc%) z9-2FbNQ&{~xRQ+YE@TeWL*g9i*@E|Hh{CWvb?swu0A64-ih==0e}f2|ljS0%Oj@7oPW%dNlqo8GmrKg)fZxV~dv zK0o0p?&bsWNC`*GEXYc>0oOrUVj6 zWUU7)0ZsVFbvjjY4g1lS2-a!k1jn9fb1P$;9tl&7@|l1LZYu;4A?s{nQ~y#;7!I1H zxDLxY+uixijV&XZ6z7%ckfR9Ll(;V(gGUYDI8y?CylYW~FkoRq%)4{x!z?;icPTh1 z>BCAv_kk5F%Lwn?@40PKx!xE5#uH_p+6Wyol~mrAbdmZ>NOr5mRV@cG1scn2jRhh* ziz^^rmu43E2=QTPkzoWBNH3P;u(kE?hNkYzM}GS3dP9I^ve%x$H5pdPG~PZ@rcJ(b z>QVWa$gD}KhZ}Z+@CUl9y9Q%@1{nf-ALY*S?Iwj@Oa%;=asomVwD(P@_ev|h20pAy zkpuvY#9+ZSqD60?8o8$k0OZOv_EnT(xx*GWVwpETvDQ4Ac=vz&ih@^_|M$J#3jLSy z_ei*H(R|yhKL+KLMKW;DmpM~O)T8T1&MV$GHg4SmGE0?H#7gUH6N$&}Ps&jL^rvac z2m!%tt#fs!zkwLB$f=iwXF{*HDhL>urzAfaz2JXkFtYv!b6%%stXhYfYo9Hj7kMy3 z@mf?m-naXJ2|OGAzzDsa(VZKBP|^+|l^N8wf&spJi-v@-36#L#z}r}QWuojfheDlf z;IJ2CULI2hr=IJk2=fT;7-=c>!x}YIMUkUDUtQwt7dOpLXFY=$Oqj)oFkraPHt{AtBD*L+?v$S zfDyJ{GOQK#qq)A%Fc_3F$w#;e{TtpsusgfSZOS8|5mz^$ z2%rF-9Lk!qnRAJ4UQ&l}0#X3TAiQg(87xPc(@Zl*5K`Dv+e1AmrY}4{c8j)@UMs}r ztj&;k+3U^|bwa(3!M8>Aq-tn?1+8{>aExpX zNY|o4b9PGg`PDm6Dj=G;mdkLf@+Ch8YL7ze?%{yZ|F8?SiN>$*1$)}rC9xPG0EMSn zs_J8E7101beVb!mA>F(6%QBkNZE7qs?Uu|zR>e*f`{TxN6oql?XNo19 z`<<7$l(@v7){k17J0qXFVW4A83=iVAr!h!s_-MFxyn)hK_u zAi+Ki&uiD$!@>X$D$54QmC60jmodJ-BSa<+XImh1mC#=zI^K)K?s0?5pTrp z&{4~^X%wA3LN;QbO6Z^jw$QGc%x!Bs@16TsZrc2^>U;lb-XDqc`&#nf;;D9&o3nh# zPn~CK?=7j|36LOlzuvU`KdxTaZ4+6Vqrtf8`=m^ez-{=TyhIO4HieGlR$$|Lw7nkl#X zUL1Bv2id-8!qEX;wyJ0wZ;c~J^2D#?)1;PWMI$H`IyU-LQ8sf@Ps8r0nQo@LS9wjRyaL0xf&bR-l_G)vdqj!VgE5b#EnZ zb}KwZOSmq=<3;XTTvY~MOiLpGb&xb9!+XxAb0fq?6DidRy!QDRgVr+H8QX4Foli?y zt2|hCd%jEUwoUf(wPU9P@5+&SOSH|1Q{`xjxv{0zESMV@kQTKT&}E_dz20q4@VvnsyV#2aGJanaG%9?vw<8ZKinSBiOTy>id|_6c=G zT(dJac_<=OWk%;VO;o}GAm~py7~#EILm4Sts0#b&rA(yI%(6_AYqn7%K5KKDgbs)b zhTwHB(n8?QO(TFlk){O!R;FyLO6G)e-PJ>D>zX{%`=zyR zyhC+6u@S)%hfgIP{}a8x5LQM@MG7BnLY%eYgiX~4QCV2EGpkP~>hZUAszT86u~V^o zQ|}ryI9c$R=nRn|q(wnrVGFr#@OEjJ_B!v3lobglLK(;}6h>|cFRpmqJ%xzlHAl(> zWI08QR=y61>HW%$B*qYHSzx?qnpF|TI2Uy%Qtkl$>k}A583{^^B#yk=OytR4vo0Fy zS#$rR!+y?$IfleZgZ}T7T8Z3%d}Uq z+i~qQ9iJU&J%)2|YSvY<<}j<=-w#fLLWX?7s=HX}uLCskkbN6ryLUZ|%HpJ37^dzx zRkJJj3UMDqd!rQ8D(hi&RR!P79f^dGqyOea-)C^_Z%aiFT;sbSZE+dLb2A!#iEHDN zN0PyCR$&@X(UJu2CC)&gov@n0T;0Q(Qw_>5+B(NqXJQZ}z7o zHfg6~SLas6={-1G$ov>&FQ%SC&f%wQzud@H`$D}&5p$xd6?}xf%dp}AX}iC8^fs0b`u9F zGJ+5~(t(lSQPp{}Ro#wlSvRH+%>HDeB$z1o06rWv8bAPmWO&b6YuR2q1Tn`ZY3`Xk zMF0VoBJ0|8NSiF)-$2v#n0H2-kCW*AV|+)ofv#Iz`jeY+yu`C=nUO&U_{(fxR|!@p zO{Dqpj+LGL#*vs+o&{mhwi&c_rzts-=`ULtv^p2PZaL|r3wm=C1egh7bq{{`{Fcsl z-VZ~Z+1xl4m<}!pUQBtbk7fsrD@cX&jQWsT2Qpo7U(Jj|45}xsJ$(&}U7Nu%UJP^^ zLQ_BkDX-3%k*yJt7i>Z;*?pzFUXSq36MObmX{@GZuYUH?Og)Lz_02)sk@cfuY)x0F z=i0DlAK&zKG&vPmnzOyuPH7D$-VJ99K8NS<5d!{YEW5kl#v+d%YCT0>BTaU?^Su8m z-|HvQHB&7J?8{?M*yXyF@Z70b&U;KTGO79yBi*|i5wb=?&B3fQsYhZ*B$dl%RGTXj z=JUgyA{5LSx}NAfO(d&;D1l8D(pXhe13v8Y9)ge{nVEvDi1#3J>tsGecjPW~N_y4G zG?t=FCmfZh$F%0Yn{DsUT3^`K7qqRPC(K%>HLKI~{jQ3$Ndui*`^>bR9aZ0U$nY;U zp##8un{&C@npPVn;vn^0=qZOR=4>9#cu9I=n~;C0=jR`UPLu$^_nGL?Ox{=8+nxZI z8=Igrp5o}x=2=9DVQjaJ>;b`-QHC`8 z&>ZnG9XA|fx>Rd!jpnDsX~$OJ!Lk8QXJ=CY0Kh>7000000Ju|C9S{Hj0E;OyK_0TU ztf;lK#UvsmBP=Y!v#YM9w6?CTt*ExRBQ7q`p1LDQ4!V%w#u$8W4C*o?njqUoAQpR( zK!O$6mLk<5@maHNxt`DN>jk^7_DIrJ(w8FyN1-Wel#vqC=6AFV^Ed&;@t4P_J&HR& zKcz*DEEE?KSDKotyUsSu(MvT8j~4lYp4Vj|yHJ>W{A>lm^1`<$NxtXR#a|7nimQz4N9E!s#$U>6n2vC7~9rB$XQ zj3je%L#Qg1X49$Z`8AcPHDkXolpI9|zMN}SfeJuVSVeEuQm#V>v9f>_4A@r6wZsyK z#QSxt$;-A1hMr=NhpB*b1c^NHyRhVh3Hp~S0WE@Q8ehfzo} z)g=$@L>i!GU1LzMFWNRvkTAht>i#c0w-&pa*^;~oSamgFW>xU54lt8!-Qe*9y2L*eYWZd zjF(FqN&oEbu3*eo^Xo?~!Dy72d68Q?9x}cL=i4t5yv%LdW1TyEVGr%>;9wf48e?%~dNmy-lKdhW!42 zx=@@|h*CYI81grw_-iAbbAanUAX#&UNXwl^IgTY^Q$G#QQ$L9@^^qo8xU1Z;H_v;_ zltUn>wO@1BY5p2=l{pp%;-6iVq>qx#mB$}j94RI`3ik%qsYL=Ib=G1yj?-!@&!@Ay zWgrM?h$k0`wupR$LBD|x)}=Q(BBu$S_%UW@dIvx@o=ak|`lrqBm}5WlzR@lfZ|>a4^5ecNUl`eB>) zD>(_igNLq|0usMt8C#0Q=0308lG^{H;d_;;ns#S?q8}&prv2+!7+-g%lfFmAv($Di z1d_9vPjGxBX-IUyxLOsTw$FfASokYs+PpQ;DMqX2WIo&z*mrhdOCQlmQB7R|Hmj2p zCXxWdAYkzRbS8L>Pba?d(3KfQ#yvxJQXiJ`sH5ByQ@-c-+~dBVrTvR$t>!vsi(a1) zUR~!-pR&j2;v&UELQ5r}sZiAl5X`}$`|#~ombdB=;BSPhh`Ga}-MW+GvEDS#>Yi(y z)_d=vpQeIl50_^sxTSoA=jrL2pycg;e=MsvGAYJhO7?LH?DgHq&1xRbfv<39?GrN7bnMNio6#os1d8YDr4Zu{?@8-h(9IcNN}U);b_-kfJl2=+2QyVf`_ zi|_zMLl}Tne-{IyY5&sGKWDy`?d+S5e$IItOitmYN+Arz4JjEcM+DgYb5#)lY}i}Q zd%o>%1LcV^8>hF*Fs0G$PipX3AEE6UMG|yPw%9>>a1$6FYL5cq5fsqFc&Hohxz0Xv}=@K z%|_30-2&R%lMbP35+H;kTOOawry`!#{e9zn!@5(;2wmWUfCOXSdr4(78vl3x)Oh0( zo11?ry!-vK{MJ=OsUEE?Ep$j@^@-b%su`E&k8?fmd|WG({?d8vGm2&pJDaYl$=cU9 zHmQ%}Hnn_m;XKF_Ki_$$6dMiA3i#CH+r(JwT6(kf?sPrNQqg@O zo`tVMvkC)xF3+2XyY9Ao97_Er-2FA}R|b#rQ;baxDrUz{$B>n?g~ca(zWj5MVVY+k z#TfW>;JH&AhPE&Wa4+^z?sds%GNAA#@As+|k^H_lwEarB0n8S!X%NHdBS!+oWT>=h zreew|cl{`gDcdT<*^-;z%|zSrdDoo8OdLqhw-^TVu95oXeKoB!YjbG$#TOd|kl+^# zIYRON%D_mJ)_1d~gZx^xo$VfLwEF4vVIfp zX}(5xd2UYLJWp@+q6F)!CNa|1K`&7BlSiWiHB7|P7z^zAP#9K|4g9agyO_& z%LNl=be@VgB#9^$Vg+Q#Xa?mHbelB9gsDcqHvyi^D~?1owTMUomvH>dnvmE6&pO;t z;zWc5U@yi#){PYkXJ4M|8@cFk54oM(ms3vW-Kf;W;}nMaLRx z_uoQn<09B%Sb(y&M9cA->`w4k6Z~{@{mF8z&bi)=h8YRA-sY0Mxzr7jv)lWB1`kkU z`$}+GqnHhHs%A-XQyKB@2F;msJ5MI&vEdG?cS4pK$e2oqK{lxINsfzBL!cEK9?UZi zfoq0j5cp{<#50T#YDY5Bfs>qwPoY#a&w zE{xh+g>>9J(5;9FRf>~ohx7^uiff--W0U9*2)k%5o5*dP*-oROLZQ$qhlMW*08ueM z*n-Ez20pCw9za9})FRW0T|CDU7G2p!hSQ2 zFH_#66-dx8xRwAHB$Rbw=^r!oa$n>-CDw3!^S#Q@<-jJM8`{q~KIG8cL)#R}_li{;m<3bJ0|f=D0#W3r*ZoN74hV3TVrRzL;>CzyRlvBfs-XolV;|8 z&*M2MUS~wPX76AMl!XlAgZF?S_Soz^2=qAm0p9v!Si!?eL|IWJ^W?lKOllp-s8eGL zoQQM))~cl_vCL%B)W0K|I8aQk^v;LCiX- z6xpVOuk|5e9Gql&_1Ya0sDwSPL@*P7xYlv%E4UCnf_*+y!e32^gw{Rh?IDujO7u z#QNfuqNrHwu&zokd26{(WyBUYa>A>Dx71q}AJ#!;>{ABGJx_CR;Wkh1k=W~^&%vji zbNT)0UN#F+y?#HkzbAD{zQL=!I4A*OWShxZsosO*X(n^p?=|h;Ksa)SS#ir`i>pTm zMu*F9Z#-z1TV#EK##YK`E5YTr1fHDhl>;FXq9AF_2td!Q3A2t4O=1EaxJU~?x#*HE zX&%CTCmyb_sYX;@ccVXr+jv<3%H}C~=4w^7I zQYALt&6it{y=HXG)g_wFUQAEw{zUcZZJe~k7Y!&pjWeLez9*jCYYm_-014oEkRh}= znswV$1`YelbXajvGN6R_@nBt^!r0aP;L$Bbwb}Ha#VPmK{BEg^$iCw`N}BBOa|B$B zy3M^G#DvwWra-b0lh&=9dm0ksQ_;|=nW8qWomM{pl8;DDuc~%pBS#AI5x<}^LDURx zgLzz`(JpdKryrg?qw>UTE5o z*-QfhgknPs`v_KrZ3Y2e%xeWgNstAk74uxEbx4t23NOp(W#B~AK!WnhR8xglDeH*8 zy&aF1JM{1Gv8PRL^4|JElv{G-hBq&K`5hrdRFwadFASV)y7uj*t*#VrtA~)N6EIGM zT}ZA6OyVavMR|zlN0bzh;H;V{yW2JoxR;(+HN)?g9kDLA!K3`$s)OwVPAi?y&6Z1I zDq`?T%4p7TO*n)is2e+)O^JK=I3uKDV(rf6nr0TD;8D}qM=jAu0TzlQG$Mp21c^wt zOYnR;S-Q_w_pwcd=iEZpVTKcNBqZ8i0^2Nuagy{g#oGV9=V_x6j;wvvih{{INUqV(p~ArU+wI}|-MKFM+)dt$oFWryl$QJ0_6igvwa=cM zin)U4s|Qfv*A@7llfE~4h{xLR#X$-Nk`qHd)7zOT_c9BF<_X~uL>4}n3+sB>ZEr=; zZ%Cnc@RK4;pum^DJm2V>89YHUfcjUji_qBo@RwMve#TJl(k{o;$;J%%YE|NKMEWuT z96wAI`fqn#vr36K@Pz-IPluE!x#{`itT^X1M{(-Hh=&-y709L6q7r#|{RC{o7J>cx zX>MqFk$^ehnNBN@D<89)ew~(@RC8|q%D5pN;Xl6p8FYKyuFJ(4TH6DRATSn$C?dqi z*Ds&$f1ZoutBJB04-sPWr6UJUB7D~$P+uk*fKOMU*sjp>GV|9qWGu;5P zKuy09f9>f>omuYKCK`5~El8aI{)^Gn&RG!lN{jHQvd)! zp9KH_0002EQ&k-i0002OGOMK;!?(Jxx5c}&q`0}fB`+_vnWL$zp|q^7sG&X#)RaPr z41_MS+xv0zkGajn%j$qqrJ}~irs^rT!v;IU zE-;^$Q-`Pg$jQ_l(aS91@YG2g?WYc}CiU}2^!31eem1{jvy+@#wPe-h5qb_%z1dr` z+fvTG#EnjHet5U_CXH`;k0zt1C}~n-E68GVahd`T2|f%I9wVe16q|rc@DxsFWNpqI zqbE083~aC`A}l~#DOPr@?2veic#wa*Q|gv!lAfn`H&eFRI3KLAhu;CX*Gcl$@9Urj zwKBzGZL-{(lRMS-MBG!A!+&1{=;g+maBPw^r2H&bm>_YSkvq$N_apL7{iHxnm}Yl` z4Ih!#ta;;{&F_YB+l{{JITzE;GIN|^5`xgg9t&D5m)x?&xO*y&tJasJJrSKJ3$!!qf~D5rkgP+OifR?!;FJ;xJ!1oFD*zavh{4b;t5}kG02DC3|3O`{TjI<#BiG#Os(lO7^POPGcb@Bp46 z(F1Ec!FAHe!?LonlfiH$bf0GQaEV=MuY7xZLp^5G$B1k(+68*Ir zS6B5{=QIVlFBlMIQ5}#tPc$prDoq)vUWQf`l`wvfNKID7tSNbO>z34A3F`S2Tiotx z`(?B*`C|Ai91PL=)Y03f*o+(xBTdvo5WRIBwP_q#i&}`1?(1P4$0|upcuq;aURa*p zqZ9dHQGPiE%>5}m2T!RFlc>i4f(7^@qL@0d1$Ykt-l_vgfu;sDO-W8Ho}K61mLAZY z#0!#MwQ64}!D1n!IaPM^ZZDnB-qvQlkBt+@QQF(RzYK?!H$=*75%ln4=7%Glsd>*b zLtTeAVDI-^250r{(2<2{{8YUevlp6G_kMLs)$c2C=qw?b9Lhb%5?EVJE<6}5`*plO z4{JSTC^;BJW(j3B46-YvS1zI@?vH(@0}F66VF=`eFL76^>#fJ#YRC|UM=>ehrR-zB z&y9B3#TA}9FI^gRA5VyY%U4Y{pFUhOg##^%q(}jeI6Z~PnVMJ~YLOhl}3hc_%?O+NemeRv+|JJn_Rc7Th)P zxU)57@@Er8HOZY4*hT`4lEFNI8T|lWoQn~GYN9m_19&@RY)DINn*-0-Aop@nCqj`_ zWv%$kZEBKtRxr=G;f>Xs-JJKknf%v%Lsj%-UCaFndGgd8pJe+3pGpA!D>DgUgvil^Ca`IIdJ4ye5Gl+S z9w3NSb!Dugp_DQry?(oH(JUU)ap@vDY*1IW{ML_rITgaVV>d$_iI_k~=)}v!-|EjN zhieL7S94;o^A~z~#4mvLe5K12Y@5X0Kp-xw|JgW;$SuTK8Ps9}0biTOp9mZor=wb9 zqrz(X)sKg$a#HMpPktY*@JDV0^cswiJNU~FW)x&spjy3x{w ziZYLY#jgiGyz3D{$AVk|uV+n|-9%HDy+rFJPo&pMfC9E@r=yZ8ylN8c+x4j0_jxyY z&E2}Zr9H1djDBIHCO1Zlh$4-FsHlLuydJG~?W(^Do$N_1x&_gU2Mhc@{r7mcBL!cWw=`)FshIiO9#M#mi28UO z(r^%O>t>MaA^$ruKi&XXHbbiYaS@g}pFomOc(XphHjX=1(f9OEt<&#@7!&pWF1 zw(fPs4guN+59G{tWP-;%BtKOl`ok;EV42O!4EH^DX}}o)hLY0MPVGJ^G*#k^MAw5p zwH!T!vieJ1m)T^A^Z!RQu;11%m~Y@OR|i_E_in}bm z&%_TVMxy~4IpwTzHy7iosZ&$+_}8JX%wT+aC=Gw&mDxQv*Lk>ZUc3bwyW|AI@g^Y5 z63cTGF~9z&{NSFd<2jSh2;*pKxIP>K0Bxmwr2znhkV$r5E~b`_oOWZzsoz>-waE9o z8)>ydFZ<`-~Vu z6Rr3u$Qc<1Dn~qZn&pCG9P5s6Mve(@9amETy7P`RDu+}EBl?qKCS>59pjEnWbj{#X z4n5YcsUTh4HYW_;MrUsTzKdiSAQ}JwPmw!F4G&>;SbgB)u&(2@SpXn}3?+q&58sHE zV0=(}T9*9X6Bi#>RE$c40w9FzNq;B ztPQHIMPIl0sP8eoKX4$el(PfJl()k1b43v4!MjpP4H_s`48lz=1YMJ8k!_X887Il2 z!8UhN@59q<7@$NX2Y!4sjzR?}lBA628H8rxErjNfP#pI1nC}&PNbn_PL@c9R*#O;ja50NQ?9O~BP8t)95*DCv*# z?poXl2B^?|9DOf1iGZH4C7PksP8vU%va6_a_h6aT(V_sYr<%IT!f_nk;Ys0h;=Mmf z&RC*<^p4jtu<4i~!ld{xgo0!M{+rYqL#QK4l4?ZHSG}5suh6Lpm^^|8Ag40j4~>VG z;jFty_edy(?$M{@MV;cr1j=<31jn^f+U_xqB9265x5DVeifnEe2tU&Bar;qYVPVdq ziek>OV^BBrO4zLH_RF4Cb^|>b?f6W0Q0_KP}87C zF~>&sj0ZUBB=Bd375D^~7^SCl!{98(#LffrLPaGm5Z-KT5mK!bohT>^OP@NGnuRE} zP8k-S4~N70FbN-nh`S-E8ZT~i)9U5z;42V?J%!Dw=vv+mhj1sbf%l~;AtERCBNCxm zIHbljX2&&;?jbXJs+uQudSbwaE^Q5GiU^V8?f1CuNd%k{2tC7HtL#>ycaXf=3^DE5 zj^yftZJ>S_dR^1ED6#_tDp6MXzz=qq1Yex0OIgvl)5=MzBn!p>zFX81 z;>`sJO5h}<&!8qWd`u|4NccD%#yG5MGFVc3H}@who0s^uuX!^Rkxlf)8)GHjS^5so6s z?;tSgwymN1PZG{IH9C~u3{QtuTcoe!q4&{Buzu9q!H2M#+cac@c~j?vW~KsXDu@{) z172*5QovEr2}npFden8yY3IfrY>wz$@H!mI<&|=il9mvTDSsMf-#+u|e}v;m!)&6SNW*DX%C*2A5IXHwjUK@uJq5?(EwD}ydA2m**+9|CIFz@|Y!y{f#bJf^05 zE3cbW_8rGtZ;*df&U=ilaLO1`K~MDyTO#{5g5V6o9@icnJqG5P8di+8I7q)BboLWq zEQd6$6CEnnu++o`5q`e!)|-al`ELA{yj#@8{ClCL{~Xc*g1ogMvlfq{XyQ;G@?-tr zNSov4#z^qiqCV~sUambiMEh`e+tS#3fASNX z)UA;(k&$k4r-`m8LZoa9*oy$T4K+uxLRrbpTrKfWqMu#5u?re88;^<8qHj7CmA2-D zRyGx~t*rqbOw>_gS_sgTfR6>w%%mkWQA$hDgxGgFrqMWc6XrJbs@RHpd)?rl3?p&g z-`^}xZtJOVp-;1BOsvw}L~}NrbitWCbF5&_rf5g2{f6AO2PTGsN-sYui zvu#WFGG~<=AldG8mwd9h+NeUc^J6x7ZdKK#lYy==E1?(mF4~TnJJ62DAkB)vpG6jn zNgA)aAUmQrGFF<4Q=Dl&cO(KH{7W7~SxF`d0+3WZ)6_O4=}uuo5`?ku0}rbjKE@Eb zeP(Tsx3D$oh`R|&Q6XWxf$g~*w${Sud|WtX!f|O zdm8a92ayGnoTe^=3`h}>IRaQc)2A}Sz&EF|Q0&H&>h(#UC`7?9PiJRS002P!1poj5006jCRUHxl008J^7+V{v zv>`7pFUh^JvA46gtEZrK0=ToLhL3#*c13kme_xY#? zCPN3+giLa}){tdu4NeaM3&)A9R#(Oq8g2uyJ>JDVK{xa3v*R^@PsEQB+3FS z$_nG$kkI2cj~MpFO5dYUenxsu&p&1kIC{iRK42n9C}YcsNe{A9KNe$>Njy<{09xy* z>_-gSl$%W-rOx+}x_hr!92i*K4Kum{bIT9wp`J8O+V`?!N@P_BmMWwkQaBPyN)e2N zrO!8)diYr8lvw-6B6<~?hCYG@Aa~e`F)<$0IkLa9`j*tLCx&>o?YbF}YfEr|3yLcR zv}EdQ?jWKMVAgF8hDY2a6%P&}Z5+fcu_g=6F zmiGHIZLlf1FL2POsFO{;kIzy3;YBhjzA4(aBLyv1>Yg-7O!6Z|{8xT%~$K6sd z!YzX*91~7bjnsV=EJp$~7GF;Eil)ZZSgY@*T%i)LIpteK3pF>-u5lU)6Ffl}$iC|D zoh=jvf2NT>pRp4UQFC6M&x3g=zG2Q>9h-Z2TgdU$wU?PzKVn<~Y(sJI#}9HQWRdf} z>)84kr^B^%{U&S&H*E`K~s^2HUJWKk1-X7{NJp*h(9?*Gy(nPLyAd&e0ok!rk zrpI2cj92a7h`A1XiMm@+0OI!d>F1u(uO#E00UDbmAtjIkBq`D$^y$+px=PoTQzqZO z)@QuC^sKIpjY!y4CCkbN87P_)!6sM># zg&t02r!BBcyCa)bxwYrpo03dh2>{-k6j&fs2qd|Sk_3Y1tKQb^=Du<)lOhG{KCJih z304EZ#PCBq>gv6IrTW%=Y}6rojGjlcI}7Nc_g*D|DqA{~ZwmAF_}=I%w>b>+o7Hj2}LQh35H=~Xw3J`NlYr6FRnP-t&%PwhCYsu<;V8iEDRJ)= z9h^An_oS-0uhhffB(HJ`B4R19OQmTNiO_q%0$WI?GR)BM4C^8L0rY?oX9h<&rZ}bw zMIaGAjATL}x(kpbWg(tAbx${T?0t=u&V&s@ukN872WL+?Dq|Ofz3>9yx$;{$be<`44b6A^ z6S_n0ySMRqwC(5l&R9(*R@M!}TNDxkPt7~&9sl$nI;X`Z{&EVmRp1qFK%03K{K8Y3cv zD2PgVEhK)vAz+4|1)G9^@(2Ks>%*#jG?`ig`}9#$!saeIAKi<9=b_P~ABfWwcTd2o zN+UM6y9+%~v`A?uT|Wf_$*a{2_s+c$wnOCT$aE~8Vr;5Yf7Y39oK(FR8{RiX2)(4C zs4hNDyVB0)kV0tw&WO94{GV9R*wf6`*1cq2u&+Ho<8imoRDh6p2A6L-U8pVh7#I zh7QKd!;tMPhV6a#6|_|z!YeNz%Tc_sV-5A{&E8Tz(JH{WS98xkJ&^12LLD50(L_-&dFiH$!zCdoU^k+UKy;;l1vedp{t6ye~g z*VH^)`?Ab!elBZb0B8ksJhQfXhr%#6{xpr(>o&XSaUj%fKV zrH7#EHuHRn)2O-b%8lHzZ(#|?tKdS&VESZh-~WF6YJv)F!rmdZVi^ZjFX6M;B-^io zWtoI~bh_mZX+Cv|Ph*hSaR8odtdWN<8Yrcaz|v(52Hcfh6!(dB7c&zZ4z?+oJwCCDNs)C_R&@Il- z31G9WJC$%I!q0a0O~aQOY320@o8j((Khb701}u$SwDj?%8%&}1);r5Wa)>}HodFvd z!E{vo1eOr+oX|rsK01yXzw|ZfI0a$AO}AA3RVYDE4qi-52_bL=qqKlX@|;xLl%zX_ z4NX8F%H{Pm2u?~I8P9Zvh7=2v<1feF9@js*VH+ZJo#$;5`)oOp-mzE4ZaD7OaeE8F zuHJ}U|9Ibhnre8tH-AW8mG2NDgpT1cbx~5l@~{+|>#){bKA|VihsMU4reic#G*0T8 z7>w7WeP?qM+XoNWZY|`%^bLFaNltA;!#m-Odo0eBmBuNAyU_?^Mo}aHUJUdeLj$lS z;JFD>z(r((%(F}N=!)ioiF@ja-CZ>S-a9m2 zU>bl#C?t5^^sLh$WFd4o)DW`+Y}w%q->E`+Ue{aon2G`G%Y4;#9k08Qv^&Q4w>Jpj zw#4AQ(#@O^Iy0wF2Fz00@ZjzxO2vJwj~eq956u~SO3ITi*6t-gru|>B5mm$Uj25Z! z`mUMz1ioqv`jq<=z2v4OHQh151yli9*ASbK*bA&Svx#~H^Ii5eIu8SWp%lE%^kHla zp42Jh(lDhgqW~W4D;^>`5)cA{_$kuR=@7mK(V_K$aoG1UcE+hCkhO3Oxp-@zZu?Vm z!?n2K+LCL%V-(y_P54e&R)^)X4sz@HGjGWKk97UE&Xp70+fhnQ^37yQdj2*WjA^aN zBkq!ZH0x;}P;rIE?v}mBDJ*Oa<3_=BwQ6faDvxoQPUV4X@H@2k_zbJFA&biO|0h^)OJk-tTrUc(Od(~6KJaa8==UtUsMCAZr zkdtmkOMK!^ut6q|#D~~ozeM-1xEL<)%*rRqo?&e^ay$S+yCL(GF-W-)v`5>1vUjQV-k>A%F2!`TVT=uVdfc3`^e5+m_H zPxI7u3yme64((w7Y~Dq4K5grIxH)(FHy+b$%YnbK7|pMRt8BJF@To9rwY_LstdF6d zo!Io&H(-3;oBEd4R3Gp;^qCHo+G1jRz%ri&VtoMKymJYGdWM83iH4_MSnh;}faZ`~ z41mVGT;5@tjJEn~j=fa(qrQ?Lm^dHwY|ZQWCXMfl@?~I=*6IPRC$FUCqgU(F2whq)9*iXJnGJwhHW+UDiTc-6?BCGh;x3TNl7^tIw!41!S#gJ(bss+v^ZY9%Sc!&5m5{v0d?0G>d^ z0A7zEEjO40mO*e4081QJ${QVHD5iqS=c7M&!w<~ zCW;*DaY7AFB?4E$B}a`(V#4Q;GkPG^P0bl?Hn$5RNA+6qpFg{^AH~@)q)F3H!Q##* zG(uMHP=CVVRn@7b+AwZ%^=c$q*NlZRAUa7{!psU&DGqLquJLeTCK!b;KrwI}VRA=K z43GK%9vpOr0QZCnjtHJE+-b?M;TZ(W%_$N9_Gz374FE%~N@zsC)T11*+pE9tQoFSc zenX)O4pZ7=*Co1Oycrw?#D~1m5Ux$;sZ2Gugy#C+*YKqLgpZqIay=G3&ALnEHFq5I zd#GObv%y{GRGk(10OpI~>!6x!>Zwh4`aqNdeWWc02l`ED^l3_%qTk0j;}i<@0Es$o zh0&U$dnkoz=15TX`FCa`B`>Y={`&lVCQHbp7&$pm0^ZX?fTPH@BS0NA0{C z<+fj2_99pWFtGIS9~q{zX4`ytf*z}@zk=PY%us5sHu5Z z#p-|a{_?pkb^}ZrgB7pas$%iE!<2MYMIhZc@s)aM-~3tvlVOQ|^XxvBYQ^O4{AO$~ z7@#xEC{1tvidkuHNfQZHZPJpLenWqa$le(*mMMdg;o2qf+d3} zZu&iC#XS)d&8%6&cFKx_3Ko$-GfF)$K!<)^dF5TTz5DjM>BJnUqV$UEI<|(grjRK_pOkoF} z(xLp4@Vr24Q-qgJQIZWg06|D3}sL^Ov}@*{rzHl!?L}eRcqtgK#z9!9&!p+j5xlULPBOLbfdcr}+BvJMwxTsss zNDuxeRjE;OMqaH0`)JAK*~@|k18b;g1Ge@Y>O)%@$B7mCJFYOg*4dm}!mfK?f`KWa zgTNrS3*HGTPS#$-%+CfnI@NJ`czNJ)CO7a6@HowY`!PXIO$xuong+{K_8VubfUzUR z`Yk>fUh(Xw9%a|uIG$~IM?j*%N%+DCTR(OF1!A}Wp1WfXVufdDk`QK%;8D1PlmR0w zO|u-z^ZB9Dt_r`r14Sy-SEWtDsWC>Qo`aa-=fe$!j6?Nbf+QHRavIZBs&5UoxiCA# z@76v72?joa&xBFcZ)?)rDl2-x31z-!*xN_z{4RIn;%vOdM$}AoPG!T?Hkyx9EZrui zB`i^C*jIfV>hPA_s*0$&S$r~%>z*bOCap{6i=Ufa9|Yqx3I#NT9YDvHAVVMl{@XJV zgHVepVS;BXbU}K74MFk(hxtk|r<0Yf5Uq((Hlw>yY4uw@dmV@Fjfwo_WJf5q^J%6g zZM(jP;O0n0=Sya2Ch7Lq{vb%W+jhwmx2#(LFW#!9DdhFy#; zk=|s|3VYaBXgy);n<#W-s!XYeiZIg%=${s++mI#Q!$vqUw9=TpyM~Sk!8I^ff%>)~ z1nP;L1maOt)~G)f&oA`3#EGrAT_78;2b>CU+wcY~F%mINm0CfQtW~@5@&z&K^pJNn zk@mRUo4A8Pr$;Qd4(@-#3FFFSi0U-f95r1sqa9p;v|l z;qxk(6QU{tb})U>A~{D+%^lAVp7}lLqp9!Rm|M96D_F&4b*^|@f`r^SS<%q~v zCt{mbD=T#`NoWgSk9noG$VB+B1WnUWdl>QGepbErD(BPgwUBf7RuP?@OGW1uaMPHo zf08qPEYp+F4($OYlubSi+|N`{Z`W@ob(iVk_t#H{sVEI2f$Lmhc;8Iwd1_W+?>;yn zVa||navjoY+4E69X1Vdk2IFU?xIG8|PH;yLaTs2NHk?N)+=uWEi!H3W-5L7=#$Qmu zw7AiPsQaQVMgW8E1iqW25F?uqGDx=$6(W+yRl((!eLf!?WMxhf!^&ldk~sz=%kM`8>H_^p)5%nv z<#ik*C+0J{z#Ru=6|Xz!Pz9&apMkW^w-Gj6v^0J1=fUbc7z;$|6%0ym0a;?ct7RzAO%1Tzj(K3;iYpZQffppFOZ(9p3Wzn0q za}C*w!)*oi)Q__`W!DO_p_P(951Fkc@d6^YpS6Lc@lL;jm6?9AfqE>+jD*Szf@WLw zTp{7y__85H3F%p@n`E{e1{y}iN2ltjISrd-S!* zXaLv#z}ma}tA02~>>It%Cl6*9MZRG#>n-tvt0VO30cq4LMm7KVx$m6B%|_^SpRjF# zuk~R?Y4bC0K0e=J$L)YYESr~Lbj8|d5jm&{=bBJV+tLuZ)10kEo z$xc1&VDJh4tJ4xfkB; zm8fF6OX!7GK~?olbrecF53wKCk=)x?(Xb0i+w<&VnaXpHk-S@m6oD*DF#%*f#4-WSY}*;=<&()^DYU z)3w6Z2d`_^3gc-Eg0%${E)iTy1YS+2M1aZ3NS2%@ucHG?Kj4*WkGR-fOA>14GUDz>^9b^+jU; z#r`~E2yj^XCTX3UBsE&2QAtoaT}@01Oak|BjXkWBF5^hPRhaGYeP zm@}*>KAWSIgQgRleWp{krE~3tya1E_qffHDaTCsn z7-quh4K|8>=&>DUgL~^8P8f~JR`yU;dv9+b)`4xxh6x3}OFIb%9vABEA6q{ooZW!I ztGNd>M+QG>b&~SIXQ{qhJp~M!;PW6A#yOr3{v5YiCKHUh{7i_!IE_Lf&E%1tUndk} zNQD(`0bY!Yk$}(yiiU6iJYDFtEX|@V$zh*Q^9d&&I=RO#n^v_~39(AIu--nI;>{MP z*k^8SDNU4^7T;SMBiq7YIt+z!=U?Tyws#wGkE$W|0EJ>Y&D5CSz4p85eQc(7{?^RV zpoEsxeIdB6-$yA{HW8^G0T%J1=a7^|yYhPIcMP|MVQ30enK8~W{f}`jB;VB9mnvB+ z^K7(Pvr#B;5)___gQG(kI_hMY06u(6i2%m}!0GA2oi<4wHZ{Qkhf!WuLOESiyaYG( z82tu6?Q3pTG**vn@Q(g3r_4K%%}nZv)Q6h+WyI%7!=ZLm-0)A#o6i0@hQ(YLtPo1W z3DA2rbNE^aGUJr!wcjgx>*6YM;^1??ONWqmL7>p<;; zkkqvDSidLq&DHLk=-FXiP2VEHcEPzli~{UZXG?TPd6a;bnC1QIG*5MXc2xA*xdGle z*D$2cDuG^#`AbZ4N2}NAa*eEVY7RWpKJn0|JE7?d`05dvTPHx-HYehw0flK6Chk6; z6h$=_TOBzx*Bo>DdKw<=i=IML0{}c*=+LRbkXV+%i3k9g7h?qnQzeia=SvI3pu^cV zGi-I0D9>Z*giCGsm9c06*tXzz5i#S|Upl*``mKp?wi z`*X8ZCdFWI@Z-VG={gf8r1d-1!{HukeR z&|+o^a=ai0^z)64O95c!UxFCtmPJ|(s=kWSw1SCmx z6^r*;G|Bw;AKgBTB@%n=a{97hizZb4nx`ySmb*8+p^mcempk4m`#10iKDc9gQOKIlry8VCJ#@x5a5= z*JF4w#7nRG7)n7O@%C8>Z&sD^!stdocsqVTYnQe25 z1!%04`w9yHa2Qu&$us29r;+2D`!)8}clJYj>i1cRwu{qT;SqTPcWvX6vO&ioYrDrw5xY%Hs$zF>lo{1z0XT(O;LK8nMEuj7_4oxzq#?VQrrTd!Cq6A z_c5a-czCLlfGfZdk7ltG+xdQX$w00xk*qh=ngK3)UWT$4bWTYt)q#ABgNqbH99L0p z`McIu_6X#nBLV(vi$%pC49KAvER#sjU66!9oyLh1mYTuo(f}|oj{_mnA*!Rk%`kfy z61#SDxV5BHUh5A83c4V>S<+h;=2!JEkS25xxfUlplAcz!?sK$r_RqO3M708b&dy+8 z5_jWz)QMD3@2zqD9%IAQ0nkG+ejnM-vDyTHWixKw<50sW6uvyO*NKYiR>CG}OonMk zHIG?uziw)F27P#Af*sX0nF5T|K~ovhv_xD)cQgjjvQ1Jt0;A0NNx^W5e@L}Vjxecfw@bYa?f2dMZgu()f8biVD$E~?FtpiVI zXHx(GK(ht_00000xKmXf6aWAKh&wPH8nmyiy|Syaq^_|fE-%coxvj9QxU{OBpsS-^ zZ1WX?yF^nk)A)3uSq?Yb28V;NUMbTPv;_c?F{;F4CoD@Vto1)hco8Fcc%S{uH1!-q z1(c?Wew|*Hbsa|d;*>}C=R2TL69@7QmIIudYMmPcFxwv|g~p+}LZS3)>de-x?x8%+ zuWId?i%!>P!Pqqj!%BSXOKxB~_C;F!S1$Pn=rpi)0(PuIRzO~x89&bo(1^QQ}) z{s_Vgqb{A-4gqiPT+URflC}LdnTB8}@GE5xae)k>^fG`~ z@Oa*?#w@tLOq&V^K?VRd1W!E?bBC54i=MOIq$bj;m2!CxrL{%2m}hv;YM+W{w(xD? zW-+?IaUaCIFT0*AuQ}{>Y>RguvXBSU%N74ozuH8!j|67JhUe6yV981COlFc&&e z0)%d`!FFmXFF;}A-e$=U6g}R3-ykEh=MLXrlV^=SW2@3_6m|PO&ezuE&M%Z3n#x*L z;d4el%>(1{RKYLjJh;|iXwtK+7l)}fTWY)?xQKhfzg%aWfH$(Ix2ddn^g|EqPFw?x&F{Y}! zUEm-x^jr?D$Hq*a_9DRbz2deUn+RQfD@znJ#zX6w7xlb^aaEYl&+`ulZE}q&6;w&s zKx2}W(UgKCgIY^1oh-ZY}kc2s$S+WEfE*hXQpXi|uT2a?Mqh{}De35X+ z9@CaM&(J#0>qFVCXX+9E9l}!UnY+2cj!rEmdhMta!oK=YiVgpSp@VUCfotsUd|<1z zN5|}C-e9ox`E-rXp%wJOV3VaE1Ly!htz7bOo~*=P&(nX@;z=utA1M~(KDJXL69CPj zEXcPnHLh(g&gQ7&lYj@VfRn6CAB%9(#FWy;anz8QL?;2w zg{x7E>((L$ya6RQGl`!XuUC zWnJa5ww0YQA?iv1b_~Fjn2d3^s*n5yO)5te2)7)9ax>gSK$k@`p|1m3f~PmrM+-kl z^T3oy1Kb2KkUQVMCdgv&f&T7Bu1cv39EH(-x3I!Yh6Fh&q;+!htWPhUPRFsQIVUtq?AYqJR zBbAWG5wD%+Ib-zt`}UhUR=p{hcfR6Q08cIvM7IEj?6#X|U7j+j`B-afJY~aHY4ye` z70Z@vY{K-Jj_xS%_%?^--`cqf5U1!z48w+H%*^%-P9@IM5NACOdXu!|+Y@jMm-L`+ zPZbb9N3fN4`Z%3IaX@UINA1SXsI-HWDCE%%pntDK3qK)ql7Es?ZUz)ZYRTGr}ci zqRA&brajB!ev}BI{kX$)ZmXjPTtyw)FF=VSZwwgLnro20Myk7VR{#Crn^x>|H&fQ;Jw%Q$h8YPQp>bCIE>F>yJL54v$*ka(OeJmQ zNq1~L$YAf&TMf^Kd!b&Li%9vhm7%9xZvN63hi6v0-AyBcW~7RbIWfNXK=ues_L`mO zeZM66#0HL-%D^)hYBqunitg9GsWOgFt{KDSS*%#?vJ7xCEH zzBHv$(7oW>2Ih1szAWU+~a`1i*NX8vRaLUD2#{2a-M%&l!@~wuIU+cIjt&K{N?#11hhCV2k+K_AoVnv>9q;*r$!LVrN&l!*h(o`YO zQFgCdz9|vd?rt^8h3ni~&#`MtipFmL0wWOSVy*E~+;2C` z4pJrNZxH-AO^_RjmX_3TS1}cKml?y-fu(6|(U@#fQHQPc<~cWQ zrI*KSA_7|z^=Z;^S>>*^@3W>6l_Gji2cofdp3&vaxK>EI?N3`&6x1CBrf~jZ{HLZr%8IRs2M{w=(4>1RLz`e!q(Ga9l7&qJqH+%X>^W4&gKX|5nQVXBxIIx z+*+rIXdlXo{6s?FfVB{%$Ist$NsX*WxnxNmG=OjyW6-`{tV0K0JS>d_DgebIDR{4x zogK$o*V%JprA8c9U~9<2h6NCg(-2NfE5BYDt8Ci$JLKQg>ofn;!(wZgM~sD_26i&# zS>GWB^-YOfa~G%RZI5eOtm!Pw{C zI)vD1-rBiiFw>NM^LP&GqUS@$b4m#fcMLWZJ?~ML-I6Ljuwh~rQdq^qq1d%qJ`x@8 z@Hu9(;!W~-`Z#6>HhiK1z6J%ZF(LU10A3s{9RLaljV-)acD9X9**S-vmqsUXD37r+ zh}}@xPIotPnq+*m-8|cKbm_tAvUulpb@FzmwU^D@*NwWjw~feIslM3`FianK+P`IP zigIeJ>8fIDYhntU&D6fms2MZij_jp1V(*7qsv)4y`Bf@Q0Syks`&Nv>@I-QJmJ~fu z>P?!e!%uej0Gu8Z^4|+rnodNG4#;DA+6I0c900UHOTQZe62QTdx7pgFv%Mf$EXJxa z(S1k`N@&?_A3=;hiPpxmf9>lkaQej1$8O^%n2rx1R#JS`RW0l$^{#b(+?PD8%)di< z)o`~;9FGY-Ro@hK(ED1D&b1f>m48muHgs9yIOX*DH@$uZ=VPOpk`7IkQHoX?UklE9 z8q7DzFGn{80(b<#!oHDEJL!4Ck-HH}pF>E%0BVL5Jlz0Z46F@<3P37I81Xin*c_TT zX|A&cHhAraX+Cy#G?ZX=pXUrl^klcYx;f3O@obnp^w=~3!C!* z8yitFbPhULo4=%Z$34jwA#%{Vd}m5%D7PyKN)??LK5G7$>Gt7(hs$* z(xmZZsOwdhkr{Aud_{6XKKvDQXXzn4dh=l_lKSzYl2WB`h@1S;?H7_;C5Ok4n zqnqrQ08eLUQvd+K4hH}L0002EQ&k-m0002SGmu{$y``(IrLDBNs-?r_3|N9>9=KQ9^jnLFZ5hHn;@cZVQiaW2R)Ny~|vwXSZs&}YH-23qSRpZYp zjEj9{Z><4kH^h)69|G%`xx;?Gjjo5a9t=|43;-TZ?DS=TuL8b2j2=QDfRSlP z5T5!79Cadh8;zFsu#d4H2EH?M?It4|Frgv|xtb^6_L3Rp2f0A_Gvt)d3u#fq64|{P32b$otW^j{!Yk4lW zlB|suJx4qrh;2MOSS{>kww!E@22>j#hMc%U6)UVXnsvxPD_>56*3geyr}v`j-=@~F z@tA{DZ+7?tHyj7`>Ht2R^cq6*097)J#P7B#U<7o}SjaT5I;@oJgG2Ww;+b%|>!w3n z%t^u{Ua3WFkdtt8mx3TleaW0lcaSHO+M(#7oq*5@YGv zVbL6xDViC)V;IH|?2tYs_LRMWD9r=_1xrEUR*L{$Y>W;?mpKB|T$)gG-$WA5!3@3a|TcPO>|Dl{mC z4{gsx)!iAn{5DQ-6L8G$j>Eo^{uuA$SoV?|y_f?$puo^KB7p-79&9X{fGHf*6ihYm!iizo7+i`=$|62 zCSFh33Q(|@t_PkL0g}6k8-zPPVr$iD>nn=g!$#q!0SP=XSix+6SUDC`x*-`PK*udL z%?USHeaeASZ(&)q>QOmjr&LeF1Bkx5{8 z|NpyV_*vc?))u8Q3^4ucO0yq=`KHknM$+;)_-4=nLHz#zD-@PbCEX;-55=`t&=z<% zip4tyT7vL(XKe__h!!X0*r`dVM~mxXWlw>M23{Pj$s$x6l9&<+HBor#IRvZGZgSS> zXyY(HERYy%03e)<0f|U;*>-CySy{TFp?UufJigW6Mgzf~j2tNx)J^3S(=ZSEbZD|m zj`dJzeW;3hQqpzLdPQ8Rxu4-FQmj0^9Fp9(SR!Y$Ns9L6Kx&PIy-H-YUZ_W2%B^Uy zZ9uPMHX7rzH*g*c{YX`X8k%ioz^4S+I!<2Pz)vFg2LB!B^&s`%t)8D=Hr~oPo)81A z&~#_Ist!I}Y()XI0ilqIrEiOkjl~^JjINUz%=R!Z8aClhyYPD{+^*K?wv4rHTPfJ1 z4Z?p&%GAbtMNYC`NG+J1R&htWP)kzkcZ;lzwmE5Xt{hx4Zmx}E7&7qV;3L?~2YhN5 z!00@6@5>X1R$EY2-lrQs2RZ|arlnxiu?>^~ehlm#0002Ijg7@gAzoetp>@(10B~TN=wV0RcBt~2 zo84U0{HD)c;ym7s|G!~iR1vzQTKw(=U&3T&Smo{a(9fwr_I*;Wd-FEbwT|w0GW&Q{ zo_q`eS@=*@C;DvXd+dR`oi+9=47p*;L*RQcw$J2ijE}8rjsrcj!IlDF4(U9pmyoXA zvOeV*St41kj+}JE$yEh(0*?V4*!yT`m_9B<}f&0zT|(5kU}u2pNa> zikAg1i?eHJX!fvDl&n-j#(<7;vx!q`XMCK`+Wy>6Ja*=vp-bG#wgphx2t*e^Nq#Nxt z5@^zVO`s9W?)N6pfjRfW&a4wjCN%h{F-V|cb|ea41@;BbSC>;6r5|=$S_r`tpI?z$S zZzs#|1C#kPG)dCH_t`f?BASl)wXo!_x%7NXocv&rCBIwa=Y^pKHp41JKj6aiv7R7T zOq@CB{nNUG^49Z4!T#=_D!hA7(AkeB9l9zP(|lNO6aZ{P38a2qjyjP=e&+Fxc6gbN zOfy2t0rQCZ>o8~6dP>)PFjKhhw)v)S0c=6-fal+z>uD}tL`-q!jBTAuFqm9 z48v9%uGt2U8{(ZZH95Z2+zOiO!&(p+2QB6>8d40eK7W`rePKL)Tz!s*x3Z9Lr<3eu z)v~heCGr^Tw$HCsYljcvY5Q>;;~9R324`XG>VN0=c;0#1o~IonV0SElvcBw!m711C zhKJcs*po<*B`PeMrf?ze-W z-Laib=5TzVc(C*eMQ6wey@OL>myE?$_bu8z+xBCQ-r}?Y5gYU)8a#1}MFID4Q37kNLfX!ftW%;JDbhD1v6kyVk2(`^ z+u_QK2BWQmy0t{>&wk{vQ`9+coxM+ntibDFQ=zm>_+S{13oG-gp4`;qj^VBCmnwqd zLX7Z<$hCYV=<71hR7agyJ_Vv`2`Hm$E18fQ04KCok&=W84_@p`3_zCwQUuF)lNLlE zSB#8afnK#<=BL^MKsj`*Wtw@20guzj{ty1+&#m?{$+j_c>CK-&jzQ4N$)Bn|5OJFH z-j+pu)U@dQwfHP%k#jJlk4Yqn)ri}@i3n}Le)BJ5L^MPz z1{0Yi*GpM^owg;`(Qdw&a< z_#<{}WS@nO0M8#VZt8j6c=T%{VN)At7LnUcM@)j#1Luv|w=KtM89`twaYw2e;A1h;5uSbo>{!T4c=P<;Wp%3ac!$^02<`zdLkE+i^cbm?o z`Ffqkz)Kg7;1S~xxs0Nm5aYqKArE)_a;jgo1b3L6Rau zka2kQ*t+eWrX3M$vE?idE9;a9M>2FMYnYJxe$GDzJG!~2f2VETrsKX_h@t*-6SZ7( zeR=2Jm^Q1sbMBMCqk4n~T>AVV14o@24K3mZ6Y&9J&^$2zN4Po#NxNoSfnO3 zZX@O3o})B#hzG!)+8DrC0AB2?m51;K2nNE5;+tb*5Au~XTb~vhfnv0Y|=N?n@`OH`al>k1>OO=CJ1(XDd!+XVA zEu37lwo5Ks46&~&Y6-%zOj%rRyZ*A<^N%e(U$CcH;b?7J62GZ~KR0@+uU0wsoz&j1 zSCOLLPKILtoK;i?qQ<{jdi$8k9yw+{ZnFzFT+P9nJyk5LkjJLD7K#Gj2t7Mpx^L7# zqiJOH&lM9)dRlxMMr@6GEra#7KJ;8?G_g;8=nV1k&C0~67-Al~p-CBQY}Kc@4j~N^ z{D4pN`ouj?XJ=CY0KjDj000000Ju|C9Torp0FSJ9ejBf=y~!;rFTbg-uBxNAslB+d z&crJ$FwVQPzCJuG1;X+ILSh!bSG+8^a|ONDu}-WwtQ5 zNx209f*HK$$!?sEWPxE~wXHVU?ErnArkKY*Mja-WQ==06t7j;lv8vzeR;3ZRDYrG; zx7J`JuYK{vN*dcF_Mx$E1q=W#lx>u7Uo^rvJ`7BSLU(|~>cD%&TC_$o>WqX=;;>St ztEx(s(raZ#QCS}Hmcur`mR$E>1~;nn#@8i`nT{vinsb=eta!*;HyyTpB15FM&SPs7 zn)fEL(Yc_-#>`ZLBt4bE!`=c`dq2~zVaUg!h~hMJ6@k5+>RUWkXwIE|96GFR-5U9( zpja(qTJL6X4SuK`z)ibKNz9Wd3UyvqG9SU#=!}f)1O>dj_Z7N6u$?o0>}x9ngoBir zM~C-{%}5b4TAiH_8c1T*s)?>Dr&pEP@;!yuHRrZ8{@sYVOeM@ZAJLxaS!JNB^x0%z zi;`f}PHk>)bPzdDcjqY5Ad=dBf_S%JjrEG%p>(%X;zZbTrbg$Y&;9zKT1RwwV++GI zS)rOl@=nOkSN0t19N<1$H<(VJiX%VaaO!IEgRrXT@rFwGQDAosL+WaP?S7x}n034> z>?O2ppvoHt=}j@HspwMyFku;HD}r$V_NgO1L}{uZhZ=5OL=eg8Jx9l9<-6jt-!V3y z%K+qXuPRT)vO-BwFlHIw`t`Fls`9p-=aVD_v+n!p=kLz0$~t<+{Dc;GFP8FM+i5fN zmjhFbR0n+eaI)~*_!9G6=;p!^aZY5yg`x zp6Abxnx&wgHSWCF#*!dOr;Y}_WRjQlJ~lzrNN-k^?LIjGL04zfUi0ApygcVZ8*^ap_nA4RmE;wC za=Cj>WcRVB;TSB#_I2z_-^>Z6$ajg`cK6nHa77cj*_F;J6!%`|?Qy0_=fQ=-UwFEh z5iCV%((=DN%1#6e2hxZfXW!Nng4Fa@+n_Z{h+l3yYS_Pa$Vc{`jYn--x97qM+WSkB zO&}-GTEGqoh4;zx)2T9+vhs?PE!yNYQk~4JR#vtH9PTu9HJ-OO*|mKk$ZdVp^T$;= z&17YFohrBv$3stp`Ls*mSQ_Z$e2JZnh%-0q0b}&L+UlA#;Be}Lpj}{0{heyHfD>Uy z;xsfQjFywzzpuvKB16*>y8l!THd^TNI}X-eWN+x2ey? zqa}Mvwsq1|RQ0qtrTQ}=7f8O$=v&PHugc@5gr={X59h$_=GUH}2y}5}?DS(8A6xU% zP7L0meCGWC632rBBlG=nkxN43YelB`tRsWw4P?v-3*H@_kP)R=n?#aMb5_ANjeQ~w-!+!Yd}Y}!&tdJ> zkMV5HWRu?bFNr(lh^Ha7$?&*=>is?4Ap==TEP(T-y{jYAWMxn9)N=R1{q@wD8B^(W zJQ#$q#*aj@p-cNbOkx8igss(%qVYL>T?3aUif!L%a-kwt`}b2nuZ^xS*h zREQFVKe-VY3U`b3T!X5aDl}`xR+XEm+EbldFKwbFcnsIzA&4HPsooYM>9mag|O1(5WG?&JLbCj0FPKfE=uo zl)R2RY3oMoo>4=v7c_t_ejed|k~IWcnlBGsYrQL#;lv1e9-q0#1lv^&Z>c zPiN^aX{3oK(Toy|kBK*!d!3^hX*qv5`qXtzs0&=zYk-1%WU7)^O44d%(%`BLQ?gzv z@VZ-Dk7YF!Y^c{=xr$wsps`kEkiY{pmv0ZFwxHr|5Bn+7UM9+~r1OAcsgQCYIX+D6 z20^0%Er95$_uU9)J2$rHj)Xp~SFI|Fog$rvaC~mZxBU{%!yhx*Z_wVnc{|RT+Hu&b z+wmTsu1UWqc9b#uEYkJ67#-&Ep{~cq&lS}(N=WdA<{_?LcIAJoKNXtX#hmSO;YYRb zf`OQ}e~h-)I+$^r)O0oky-+e|b;H-c|igt@SXG*OP#1kavs8hjF9 z0Q${>u-{yGR2ViTxuFa^=rGwz9e`2*ejMx(LX!myAboOPsBIpc8P<~IVTYmf!a4?& z;Lm%W!B%yZ)4tE#TKq9>51n>N}e*VBr*cEo-|y==mZoFRghkFkK=_b&|_gr1*)`(wwOueD+s9vnh* zTB7P|N0*Oba%?n#T>`Z;?0YPpDgPR4M#>j)TV(*~GpUQz1pdq$7m5s55F6_x#h=nV)|>`KIn_s~US3?0mF`=qSqDN_~wV9&UN9 zdQL?_^_yq%(B1G`Qsni04L$35^K2L}96X=u&S5iLICHi>EW5Wke|mA6Z3*4(pp6~y zcLiFbeJEAQCo`{nJepGY>L?qUTPPK?sL`Bc^Be(9$K81`6}RqF?E&pZ6(iU?nv1ul z(Z9ImxoRt@0x8{K8>qzv_5hx|D~-Vn0fcJ1NDgmf9kkZcC|aANK^*pptYJegQ%=1X zJME=~%|5fK$=yXg|3q8+9ZqT$gcb@PfiT%I!aFq4dKuzrI_2is40pK~!?zxJZsQ4q zQ}H9)fPPxx68J65WvASo>VvMQNOQ+2;UBOnk)}Rvx*-rOvXxZOmhQ0QUD+%P~L zjt`G((MO9Zs-PYBY2QYC_jccrboCCPB4EfuAA{-u;eG2vCVTJIz#r%5gd`YbgNcSD z<9Gd~j-G(SM0eXPbYliy-0KqtL;^?DGzK?5V!j-b{Q5q4)kXXe*Knmmp=&O`4n6zRGfLZA&z-$h+t2M-x5_eaXWoxh zDnBpBHD-~ep4{%%L(7{h&j5(B>hv?`u~IrPdi~LxS6(UY3veA^FJOjfGgv*eq)I85 zBbK?GRFsr|PUnJ}N6#sVV1>F_KCR$6em&kQq06DCeJ-Z?_I4uwC4PMDGUq{bBUkTN zZbt#^72_eoL zY2Iw?ZLHQbeZIb9&!OG>+GC7=jJ=ZQ*XPcja*mt%SeV2OuC?fc!5CU@{$%Jg1KJv# zSd{xwd5A5-S&37wHN+B#1+~WWCtCt|Z=rj9&dhmu91p_ma(9cCp`x$#4(j@S{Y=ps z$y=L8B`|~?JU*>)C9^8b?@h!+AjDgmRM8XuRw(-~ZuK4@+f)3M0waY3;K7N;1E{ol z0YpO}!t?0zoEjH=_*|2wP@d@VTw~_H#3;iQxSWcB1}F>bV1JGU0Lo2YP{gPzBSLLFbL(uD3b*uyW;+8d-2(vTLE6atqh@$O=J%6=TB z{XHM$w-0ruaV8Rtiv)mWS=Lhx8BKok_h&cD?(YA;;h^xAh43UvD>r+C3@PWtIM>f} zd9xig%`AegBPK9v+xKwigJ#c$bjQoe6Lr9Tp==f=v|I?&YrU+P;cor9+PV; zFQSRMY2o0<>#u1i-BK%_LxcfLz?9jVK|LM5y%yg4V*mr10ToERJLJ+VI_GAMVBxT` z0^0^}LMS96&HG?&$0_c;FCR#kFh0A>wyiJ}stStnI5B7VM%QcB^YtA9aDl$cB`@pD zg3wFX6+_vJlW&kFmH=@{G;2iD%(M?EbKRN3Jl#y@c_&N#G<^K8@K;ORXrIsLmNH-1461YY#gfGPRy92Lh)^0OY)!Ih-<9;a;+8jU&7)8R0Bh zZLRb7n@oRNlp&?YuQmeI(F}OZ9H7@dKDAH~S7J#|Ibfz~D@O-3-zN0*gR-KivT%Jj z6P*-xVdNSAT|(NQxAkHILpG2HAp4;i()EB&wsI&Bu~|iiYX6kF+M6<7UrI%Br{GkJ zn>ZoyY&nj_?ZViN5XG^SG+`Kw$TB{p5_IZ`RGACCp@|1B}I zn7PkG25aVA(tPj46DvdmT6z!H&F{t!4;U$ z#e_9-Y?}MYJ*M+Elx8(0sJVW88Y>NFse8z#cD*QyYqMD<$I}IaS|e?HF(D_+nGp4+ z(1B#rK3fwgCvgLPQv^?EXHx(GfWZd<00000xKmXf7XSbNdP&l&8n&yqx3Z|Pv9uv9 zEzGm7vZk-Ts;#`Q#k?dQ>|+LlH;Z9bS{q(RCu=n}Ml{CK=!+Bpw!Bi__kr+k5~4{0 z`^v30o3)Bhih1F1KfJxK_GowkS>0Y~|7(f#_m>7m9 zI_08+S8MnRN$x9S)jmd(A!zCZ@Bc5sn+rtu-u`yr5c_T6=PM1RBQz6-m z{sR6-p(~QrlRB(Yv`YxNt>!doYKLf0HCt^%KQY-2^-^a_I?3lg+Z$uB%cD{+o*_Hm zt|p#(&Aw}#QF7FbX}O^f=`vmFa#KFXR_>JY8lJak_^&Pw@UYiZ*M}KgLIDC_v~)V8 z`QW_@e(PhJ19bzYVHm-)nVBo(q0Y`gooo_^m32{KQ5{X>&Cfe{&o|^;VR|>M^=Aci z#rRmN!`^%A}YJf22P!OMV`<8;XU@~Pbxnwm7zDcct4L|gIf;0%DEf{|8Ybxqt=6F-U` zWIwp?oUp~HdxkuIppj&3J&IKbRsqjAo#M_=;Ufcn%=3kS=mJ4gv_Qfx-zzqk92U3U z&X57*VPz$gMaYoqZl5GunMYC&u^**RuyyLuKYd~=^j>mL4p&6)=|+&tq$|oDPT_Nr zher#h$aRRPuV1%%_ti>er&RwepiCJA4mfwG%8t%w8JbYN@HviLS?oOQx!y^RnL+d!YGf!Zu>Cd^&BQ}+6 zG5B7#n!-;%+78PwcHv|0dHTI(P4e|p7{zU2;8Pc3(Wu_j<&a}e)WsuJqVy%)evpUk zp_2DzG2X3}YUjugRcYyRJfBVQz*~<6dS4lJ zas&f~Ketw&Ui&^*wD!s+j3~J`P5v_Np3775o2@hKR6+BQ_y4AW;u?oClt}B=<-q-r zKKki|GTU5IzE3&z!mYjIT29dIeJPhT_sntcHpy9C-WgLOoploz=XyvxiY8ZS93>27 z3qrgEJyt`VhnNRp3th2o5^q2ug8%reJ7OA{^htG{sN6wZfnJRLWl+@Ngnf`#m zyERK=e`D0zZ$g^r$l+F6wH{eTLLsPhqa(B0JuEc3x`@f$Ww^@b^GCc@>4R4mStQU5 znMJfb3Z8q-UVIxH04Z}6K|q+dWT!Y3tJxYaNP0b=4*Q@;8jAJoTfAnsIlXoZuC6_c z93PQtXw+$2k9)oZTb!Z)&i7AGy+U3ayJYKwML5M3 zICukuR$EQ_=6{*pEG30d&Mb=6Q`Z5#X4K|-6FHiq4lyW~1J7fTZD);g2mJsW1d@0r zQ}ou>v(9cp=#P~g`D~eR-<2LJZbqCUPOEc+#k}#HN=`Fdx&VI)9TAJ90JamOmssIz z3QR{Jfd00q18iD{wf56){K%!8N%F2=oH*leX7Oz_?H)8g0tucUJi7Wo!u*!rZHClO zuQc=V3o`G27ll4Q`$?+ry{=?~WT`)=C?I@65!LzCblae0{%a@b*>@~1l*gvNo#f9! zZ@;`5V(aO7t}pz}m7*q@Wl{$EI~0W>z=9BB7|Ch$<|p#?g;P&^H2F?mm(K2_JqhB( zgc$*90{*6YF1KG%%&rOxXwS!ogtnAg@+ zju@f_=a`4RxZ;@Z3$GHMTy(}jctWA(*kVVXNWn2{ZrZY&0pmbd)%7$dl4_Z!Behlw z<8gl_yyZC@D-@1;Ek!90@>GDLYo!lDht79`w_om0)sy13EJy93IZFXw=3^K(Y57eF zTf(QUNI1qP{J)2rC~iF_&?W;rB2#NKsq#_r7=!)XeWb#1Pa}0_KwM-lm8KHxu@Yke z4AmkGzARx?a`ic>a1PUQZ}~!s(`#&Ul0U(kIdwj?7$Xz{<^iTX__%}Pxl&RYmb4FNG}382e~If+7UK^jx_ zJT}pR4^=+o2tpq@wH$yg9ktP^$Fs{D;PH^{T<46{JR+#M+X;fqDd>@gEoUzyrMV2# zXljZM{tI-1LTLg)45d&)Vf-Y+lA2eL8}ewlFIY%WUX}H`OV2bVh#`gTri^y!|>F=G@MGH4DBp!u>kE}Wp5KX zKK205eW!Z^P-n8MoLjWlZ#`iir`T%xaL1n4q$oBmY?Q@-A!y+ZJN&jww=jI~u}Lng zVCClUTLq{mFfi~4jydMk`mSx3rdMjDj(eUAObG)mfv`eo{G1e)FsG9cb_L9bRpq)a z4=ULNZ+Ce`dOl43^r24>!6y-p=+%!-TPATf#XB*CHeIzv?9**k^Z+O-e+Lqu-G++s zg99nJH{*xsav>4lSl~N~eA@3*>=3bu?u8EazJ}`S4k)bNn}(VbXY40+>ywnIpdJKl z!=D^I3d+cM-w9~j3i4AH&^+HLWGsq@}f;fX>4g=r7tCAO!c@l)aJP?+~tm9?$5t133? z_m!XBQ|Oav_j(F02_9R~9RRpaB5sGmH@!VKaa{y3oY;vY!3+XXUw4POa=;8o;;im~ z_18zu%ss=q@*Q@ATgt#D{)YIQj8O(=mnNyON(I_pV3;IlCO$0m34j6;1O%QpW}Suz zT?lp9jUb$z)-q&aq(%e6QBw6B#tM9BlbijwLCxD~p`R{Ra(I;xJtDjSv)pUWKSQBj z$~Uokzv{LvXeK_F&q2H~Rws@f-GSdNEhgTh$@>yw3g_N4OmdG2FV&&7baBheejvTX zce2$W)H8(Xq37cs>YsM&AYmJYpJO+{+rh&v`4fh6K z4D=4dazGFekDf?FrzN}s9U@~1eJIMK1P5Upx~EYWF>IQ(X^-7l6(1o*lrC!5gZ*m= zQl+KNZCBQ*bacK+muvDV?q(A~NgvnGYy(Xe-{?Knf|NbXmT!M$`UcM-JdVD?OUH4p zW9DH$Mj5XLII@+%%R}aYt#0~O=;t{6%(Z1h38j!`_DDA*^x9TS5N;`RbGvhm)IOMI znxh1nF~U6xVG+v+py%sRg1tXgNR)%EG^N3+pZuv0 z8-eDN+-LbM+;UxL< ztw=q$pDbxo(b(H`D7nlM%R!LKTvXf#xi>{xZ~wf>gO|(Bg>Z8LNNutws1i^vsbcdm15GsAO7Cl;8d#Ho>`WS4C&9M^ZxjbLr`_@Gb z3h_?qN{Vvhl_&p3K0NQPSuR37O?8@bv&;5@UQXV3#eRy*kv79W5?>g&G5fT2pOu$3 zXNLRDCN~%JtA<#;Kd^Ev1@&kN9F-P~K@q1UEl#I8$y5$86FeHZ$%&WS#6FTjE}PzA zj7eZlIKzr@($#zzhsOi{`)dZE0hka_4LsMWB(zO8ot<@O6SfPlc2hvBrUkHRcUo9>9={#&z#O2 z9Ve9tk&1GC?%&dE+AR0jOqC#Qeh2~Wd?o{0P=8W8`$WZ~?oLd>c3bbh^D&sotukPe zKooZX_SqwxAhO7C%Zo@`5<+}>=r(_y?tk4W@}fqUv;ljlE4>|N1w~Ea7qw43+FENj z;S1(`zMjn(R)w{HkiG3=`~DwvacMKF9O zhoDE58YeRxwp4uDYVNh@ln(PiJRS002N12mk;8006jCRUH@r004Dw?I#*9EZD-f zy}ZS?ud}AAv$r8DEXB2_w7tE$$t*ui2iF0L#Lq^6o$l7G(K_<&VMoV2p2JgJoby-~ z!y5OVMrYD*Xv5RlZP0r*d_&Ic=lY7mtQCpx|M?!Vkq&5hi~xddq`SQb`&lsSBZ`RDq-<-l?2;`{EN5(?$JrT*Tz`SE3yvd$}|Lz{*Np zLl!`4uwYVoJI9w~T*lJ0t@`({mjCoZ7r_DyZ-(UEo!v7_rs!P@frN zxYMzgp7=-nvaU!O8K9c{$d^6|mzPuj^Q6=KzH&JVvi%#AY8?baSD{ zjutZ)1GQd?*h?U9w+oJ=64-WU2?wuF#CR?vt97y5PPi#5tSS$i#T)RH9Tl|F=`?~- zP5>Uv(^$YHfz$;~JU6EoB5>+WJrkBatQ2%T%~7nDuej{DZchn+zq)>m#~fy!pEp@I zh2!0iy*y>12d1G5uIg!egpE@eqedP6i0X-WgS>k`%_Q%ZuIaWa-3qov6W1YAk}acE zm^3F9t+gNd91-SO%iiTpfFT; z9d*G(53L-9Zh4LcE;HW1K^cOm286rNxj;Gq{@asMKpBaq0g}&K$c(jvTw^}VriETL zFT=K;co0H#P8QwspVFd+_+v#?<` z+3i++_0>haM|1jkoalOtx3P8SLDWolUgrGu@)yVAa+o@2&qNE<#pqFhm71a!@Lo_Y zcm%34`gcYZ_w-!#u0{o<#AF|<-WL@9t3v`h%xa|{LRp_ex&I1)XodUL%@$%wi4v0= zjX|>c$5e>lS5qXiF@1Wy8|{Mb)3TM6H7YmRcun6KX>fJovDOl9?G`*wdG2>?bAfxM56|2K`25syVwsbcq8wKe;kzYF$TFlIi z`YCZde9yYa8q&;iPnq6QHyrLE4F_sY(NjU?mgP-Z5f+6w0sA^Hc#tv9XF1<(^ptv4 z9>g$ABHD#TN98`JtmfL5?h(A5O@b#Gn$uJOXaHWD)6xc)fS@5x6rQ@SYl!CnZDvIc zxvE%~OiqN6DBJr^F+*C9@83Nct8%=btt-m&+zd_CPO=;wS;S={`6rD0fygXg{yVkT zJ?2zp3O+=}ima!@Q_Qi_o9xuY3ZdSWrQhwAF?VTuw@s<5bhX?q(JwCSQ}s+eWgH@) z^esPb4L%eDhiE=c_d4b@#G-}ckdAv0RK7pLDk!(iF6f70a^k>2svQrLcC1rdCl+@N zL!^lXUd$__5Z+g2)IdWr@P>Us#$<{j{Ike69!|yPIUE&YV6CIdyL2WH$*wKFEvMzIH=z zkWYJqSTp{-7wPjc>pAcJ*S`WE8qT!2=FaRg7DN=;anLl`1SeOOhluvv^? z)jgStwBWPM(W&TSY%<%swP2Vnot|**Tz%P=e8M$NnW@t$cSJp2wL9z!gunYV&8nDn ztdAUrdTQY8Zck^22>bu5azJ2FPHph*U(~`s`i1}K&rHUNpVvvGL9%lFqg3P);kmpRdrV*W*WnNPlv-x zL6Uk0Ui?d)feSzac)l_X2kI0$Xfvtz!wOm8V`@^RjvF@7q?GxvVFeRJAUn4<1|L`OcSD%B#{0e2(^pE?_ z$vY|mhT&V3A8=n)aMjl(MVI9^ao`p!;YM*)MUY=7(CPrTM4t+6w~79^Ue~T;3}Kp> zcnvey!QQsi-KL|CrX)0s*?B*m!il2Oz zRk6ItG(Q*ZZ!|P>e1I>hk%a5HKATrL#Uu=NC=dCLp#RjX>ngMJP9qGT-@O;wg%p)M zX3nUR*O7Ym78~t28qP{KS$bITOC$>kj8aph3HJ@bDbL~cM)y1c4VIZxBt)eq3W>;A zv-p(ZUor~)r+sCP(C)Q82gItqJk3T^LWX79mh;H{dzbk~*i&qA^HAF~?vf6rxUcx= z$DGK!xqm;A0Ka!)(t~=~eQqmy#2zOs%+$2I(<9>8&4zfmMjQq{H9QBhZFQ}16PoC$ z*iJ2uv_$z;aGX~svIRdC8D`YI%? zW-^=ve~+qg>>)OrAO*JoG)EG-R`Em+u<6gcX3eW`9bE4>=14*<^E=7Lu4lt)1|&gs zV*5+a^H@41zCHQfQ(*oo@A;V*L|dKsk^gAQ_}z1@*4kOXlW)v?9K}ijo>S2yP|DF@ ziVa)rs(haEW{1u=mwk8IDCXtrnxP!gT-EQ*M$nf4n(K32!Lb3t9pOx+m3&4L6&gSUs(RPS%lb5)s60Q^AjIJrRfu!#`u~^W>W~VPK|Z11l$CV^!7FNuv2}{-{mZU1#o35xviQTUb891J~rwG z&co#oA_7+ph88E%q-_%V0V^;@_Ix+=YC(Ht5#dfn5(L=z;Bpu$l=_ypej^93PM z1ybO^^O0$r+M!OVkrMJyE@vkM$zb91&9V$z@Piy*-)mMAO}<2rkSgcBQXZN+rzkik zDfnXtq5Ak7k0*5C9P=Is`S@^%BdsEx3!6U9YJw>+ zNNp~V`n}xa+03WXjBnAWpQ#@~${GDqtIow?ab=_tCV;zjU`jN8z>H1i)Mbd->fmln zTVJvf4PM+U6@mcJ5|%t~T#%N~L@jj)&92%fx|c)7h#(^#S|)aR@de+zMZK2j7tGGJ zpIf7-?XfcGNg%Q!XpWt3a!B=OY!k~H_gmGw%{Eg$yWPBr!|7%VDG+w^!~uEJ8m<&g z)j6Mwxt5q$H_lzfy0kWZNIe6eLg0T_C8n0^A$^5g`2(1=w>Gcd=M>Ve!70OO_BL{h zd#)2SzBj|#<2bP~;J|CBSP69wT{-^>r8=~a1s?3H2><{9JVovxwGJ7Clx>EFZPluh z6Qo@N0EWh(<#Z1o42km+Vt6gi*R~_>K!mhKvVh>Kn26^FJHpyG{aWcGPg7>4JdGE6 zqv!5)dse@%RV)C&KtI2r)j(=ty_}tjeTMGIQ&=D%GAKPaGi1L|4}!}!+(|YsMhZJUNpxh zpXu^$Ic%xMU(1#LP1qy5`wkODJ$aGvzQ73pFBD+jkW825EaK|T9q$W6Y%*8MC1|2d3|&+NSwoc6sj?QAvbYu;c%5XF$VL6ELzZN+G= zI7a_;Q5$Sc)v7m*m}J=*jwL5@BCLak1O=Y#Ya=5GV1R(Y>(wuuImg;GXQ#26*gdSk zwz4iSX zGBMV0bU6cyOYNaxOjrSNW}R2sJu(B%1PX&m6k1)yUSgSY-I9ZYav0%9=;I{ZHlr0~ zz@2^ccv8BT&|dtoawTaUHd;vZ>A_ zE-uEhtF@}Jv9PwPsIj~wEG{p>xvH!_4O|xrSdavO=OJBeV8km8LrbE-E7rPvrrjT;lI~}%J&3~g-&9>j7F(H!tuKGxHq)_ zGE|wmxM{aIf#_Bwd=gA61~5t44>0%2km2*3J)G>6xziYfVtG|16ZUZ@m3!mR{fd+| zKsB;~>ZM}ig`d`tZ){5&qN|D_rXk!8W~O!MgNlKTpbS3O&j*K<0~dtTC zV!#4~g2g3c9|+8~dsVqyUZ$P&7^2(%e0ir-oW3$2s+9%`>nrtPKHEq=z ze)zS(i0d~R^*jbNPX}HMEKPu0Kq|}&p668%LWB+jT7Wpr%XBYb8%*S)?QMx7zdK=S zdvXl^H@2P8O1)luE81Dub=Bn!hTXJvErKVAzCG{rk7)F64u#sUg?I^ubt*)2sb&|tzJQKH|Ft{L<&_F1~wND{`;#1U=#qtfalI$8-_eK zoW=?;4%56mgK2DsAmepeOWzb$X)JknVYO|5@I{y7dntFa>fC24_G}BJ*t6+|>Uyqc z+;y#;$~lmh);lqS5z5BL=!;daN%i--n{R}ga5${w7w~YhYzttNH?+OY(tL&9hDRT1 z(8l?$9V#8)9Z;}25!fDeX<#*@L6?o(TzHH~zERH-q*0+#tx zv_PdJ4Q3P>qZ9?7cH-k?zN*zfRLs5>y^*QT0=y0bwvm$g7$wn-^2qpXu-n*YuWx!Y z@{hB6N3`wi(n3`msoPF=FrTY+4Sy~k$sRBB)8;v>Y7G;=5xxO}6J|BcavJudd>U5I zyOBKCw$mh!&y`MX#x_N1m;{SN?eT6e$>FvtV9=*~bxhc4Cw&bgDX8vdk7Hp>3?==# zTyE;i=NtzF_2igiTcK%u%$j?=9+LuD(PPjH{b(2ag@n{lools0Qz0mdqHzlZM{Jh# zdu5zOG}$xmXB*r}-E#8DO~rYzSp6Q$!-f5LCCxB2L6n01?duNnDiZ%$4R!2$t9Sor zV+_;vqiX7w{yJ$Q^!~Nl7?{7rQVx-80&GF;p#AlyV-BqpInVH=>E+jHsmY6LZ+*$) zl z>dtz0Y>R0hUb$Cncj}J1`=iFrMd6+pRIy7E`a4LV!Dj#xc&aVU($+7}o?(NxO=tr^ zXz007-+K!x1D=b-2qH@xkR$L+Qy(WSZf9nIg~Q6KVqMnS?1S5SbN43Udsk0w+m_FC z%^pJ3mgUR!&G(GcAlC61H=P^3Vwn3e&mh6X*GC{Vz$-a%%#+J7THR;4^^fEoUR=;_7$&LHXy(FTIiaEF0%nW7BvKIG4dd4PU3`u%n*aZe7{%O-Sp zmc$_c;2iiXBR{p0zI4-`R_G`;OB`TlCvwokgICg4lNEC- zCKVX}Hfs zy@jlEOuLqQ?u|URkJsb=X^HjJ!|S(bPOoUDZuT>qg^L06{<3%mVeNY6-%=NAV!GLk z_Y{ydbSeoql)g7V)wC^rc)u&0ujfl{td24 zpK4sWn{MRi%|U(L?t28Y@oUmZ+{~ts>~Ut^kX?8Fq`+VcfYJoc2{~-W7tZ zmpvv%BRxDY_)+C_t^a7eH_f$Ar>g8JMJH_x)4I(^`i9fK_VRF{DvX(1k4LQbKMQM< zV?uMUH1T+Xx8gLorL9d(k5*?+&_ncm6FLB%pjnqc71<-|Z}G!QKPI-u^-=(C7@TQO zoCAJpR7i+6Z^%f#rAY9EwzbS2T<5;#MI)&XEY3$?9y{TdvETU`N-^?Y%F^N|TocNETV&CStxU(8HA zfk}}z2cB}AjMxc^qkvfgkUZ2~2t-5gdiRh1oKpIH&$k{QCE4>zBiUdLGATXGcA_~HoH{=DwAmb2pl@jALaqcpJdB<|hL?%b zQsHAj{9NmqK&K2hwCt@@>?^R138`a9e8pId8(R+Bw;jX$;U*dDYuhh%Rz-NfCT_HP za2>r^zr!UCl?U=mejxhWyf1*DoPv`6A90qs)d;39r`=?(tZenzrpJ2Ihnrgikpoys zt4(P%e{a-Nod4LDHKqa-3_buqE|ffQsKieCXQq9pTF@$do+F#S!QuFqU;zGS?8Ftm z_1Wg2Vv6t;p`ZZ`s?NEfG9FAU4nijYfTs+@!lqXk(IJgcr?G0)s#-E=PYt#BxZSJ~ zKaHWQCF%XcA>aEvrps~6uKyj*B9izR6$#SOBgh1*!xTN|%AMZ%8Uf_aRa}IXBeu75 z)zP1`2kA18$y0W-^=9QEHx|gVm+}KILV_K3dYSWVhZrZ9BTPj6z)LZg1dJa@tP6HT zRm@wJ3Y!T0G>{46sYwl~zt3A)jFWkB3h;Lv=kNs{?28EiAONvA;CZID0=&{2kO^l$ zl&5)R-iH)fw!X;-yM&$KF@0iMwGo0?|5v&`@0HEvs91a-9W0Z}HcrvX;pW{wLyC|L zeT#K~q$af6WT<=<%~7r#Y7JHACf_uH9mKmPw%veNL6sqFo*RBz4J))O|ZZ_p;=5;hx8|P=DIo)k(Es^JRrv)50Ra)DkTB_J1mhx;2<|7 z5Ii3_mbIZyQrJ~X#;SeQw3IXLn}o`7b(Q!Y6vcjn^-fs-yEkR^wbJy~D|WIkG3Koc znDoI-xSTMV=Fb$*Xg$|DTQ@c`Cb!!qe<9GW588sJ{FL`-ZMs7zEL+F+-)u`~+ZF*| zt>T>-o;#9KlN2WfnDZHIJ!N)qP*p}4sg!)Uxw;$g0I4NqAR3s;a4)|*7Ay>WY+~48 zZZV4;UBHx3k2!iG7Am9$PzY5Jh$IO%Efj7?c>wGs8g4QG980PI_I$-k zL{s?tK8{vTd%J<7$G`qD=*{y<@6RBU_mKrGMHs|$#a0|bN@Q%`E6=Jr%N_1~#PadK zf98z;SE6M#vnap4s{m?+&DjB5MIP|;_Q6d-$&|O}JM$r*$JhDJTo30W=L=cq?tg}y zhtuoa>gCMO&Dl)~w{%Cg^Yg=p+9Dq=fV!>38=KQkP;I`Q8XdpSgECZ3)m1$tn%RnS zm5_#cm(-g9TD){!Qe+Ck97e@BrL4bs{qt`;KQ7asx3_PvVmE0Ed5Q*r!=dgIL0U50 z+cV{S;~{7I>-*ca)|{{RBad5pwDxSCCqaZz_=`8c#x<{Z)4mhQ$r>p%w04Cs$1>DGK0!}aX~Cew^$Mf+i&pN7JZeEZ0|O z3JAoSjNE_&E(!oROj%C{Ch&dZ_Y%a0erRMS`%NX%W9S`cN^jS(riir{4gU4gks(ve zQj~>tqXGM~EuFp0#LddLf)0ADUJe9s_0T=eayaH%@EOy%jVXYWAY1bh+gX$dzD^8< zILm;ItnZ72y~5Uf6tGBhC5Z-93oZDX?%oYeyk$)cpIh)X0Fgl7O<)^55-b!`bsj>v^*PRi zN5vcf(i>57GpZ4*Gg*<}vs>rwJV4^l1f3!m++Jksq?aD%!BCRw;jf8-a*#nalj)pC zZRki z5}<245<{SGCtqyYk60=N|{HuR%^SnBLEE=u`3{zU8Dc1XjQK! z{!4bFsyvz<_5dI?H#y?iXn0BY;ZW5g2X0l^w2-9$OlUqJg>eD?YmA16N`)w$Az9|_ zJkJmV#0UZ=z=@a_kXW^9T{6nT;RoBE#zAFrI?}5BTxC|X;!@1n3vu?|5qMLF{_#z8 zctky$QRuS+*Cgio0*rPJqhsm0R_}1(vxzjG!ho_8_v!igu)a0xm|Kxg(Q_*g1|!fz0|N4)+<7=W^mB}aM~8uc#jNy7j&&Bf_p zt5D;E6wM=$_>q7aFdPL4q`3rH!4D+i1OQ%4YzaXlfU%`Gl6ZB_uv*I%!w9Is;{^cF z3BaLTCsO7T_|Us%F{RQs-esEAC(FHajVe2BAp3^h)E2QVRTRA_y>OfkwZB2OmWGDB z*fPj@wQkiYs149>wIV{PZ`+)YYtTFd^B~x&|A`9K2_zJ5$_i0v7mbd+c_uuL+7q8Z^Ol4_-X1 z6+@sPlr4Escz4tiISVQ8j!vj0PB8!yhgCU{y{Bp4z_$In`iW_}$pL=|4V^~6qin`p zhh3z=M5;Sj>AUWZ+Uz6Ax$9UnL3{w>rd#9qE)7!gwtRZIwp?>3e$!?xNHBwlv)IY$ zNVLr<33+=&;kw%vgG(U1(}rlFI)LTpCM66PHJlJSjcq__4xD#ouZb%gW!F!lHV%@8 zO%Q!w02A;LSOx3@(BM=6K1?i)g5!Vzyo{7|)>huKPAS(L>?sZl0Jb%mPgBE82f68Q z$JHk9e!BGic)vB)IOe($yO(9dorsiQLSx(W(b@#W2nAJcH2`ZD_IuJ+4ogSMa_YqZ z;uqo4NpCu}tn~M`KO}QUICV!(z8+q?7=zeEgpna^7^FOI`KU>6_6r*+qJ+lSM!iB* z0K&Ml9K0uisRFL98Bz?t0``)N994iJjfh5ePC0qy#-b)Bt z!tj!k-WyybK;STy)ak$kzHgW&-CkFJ>a08lx?G~%{R3Cijb=zXykVPHV@C;yL9IKg zJj!{BQwrS@J}1~{-0(c+CY?Iz(%sylU`AIqD|D@yq2~@C8_i0Xd`PV2MJ003j!Ogk zWkbCk1_sy>NZo~?;UJ`-3OXU_P52KZrnSmVYgHAtDE=j&f!+tuy=Wg5DA3-Jz*_*; zDFhl*Bmu%`Va7)5cm251`(OJmJ`>SfTwSIzwO61o3edoQD3Vnsit3_d0cV?o)v5OQk@6+oucCnFi>C%EjG zZ$br2NpB1f?ezJ+5{JroZpqCxZsL0VG2&iARKE^ovcjh*C+Fu3&P=4@>9X7K7qhM&vg zNwrnhYQ}C(WBBF_VhUZ)4cj&RT+nDW$j8MU%b|x^4?Xu-EY`^xM%K7E+9smKS*R~7 zA0Zw|c^0}&V_3Wj{Cz$Bl+?~pHV(rHr8;Izj=M)_F}ADrp?L&gNqikUuv|Y$x)Yo# z0q17{UK}*iK%t0Q*JP%n@|$C^&9YlQY#M^>T~@)iva+5y1itV3bt|=?ylzj&9b%H& zY1`A+DW{RkN91IDntI%Bb?KA%p{5#phGGwTV-K7Sk?*J^qQ&7FV487onhUk%vEF0K zzOP3;gcT@1EkT*B^pAfYb585H*T8HynTLFg+t8th+k&b?FQyps;aIL9=3H#_rfQiZ zh}+$!OoEy$dT3^>xS33aspw9G|F?ihU`Hpg3x1Lz0*?T!=WILxer!zMK$t)?;^dUF zzE{?AlU*Hcx(*_>fjz7g%lbf@jJ@mlY$e$=X?(i$zE-zSOYfxx8#m(@i!tBqU%Aox zMxDdqFzxY*IWzw}ZB2$0UN`}t4mM>PYOu%tbJ%sHB&|-A^VIg|MsKKym2o4&$ zuLH4mjRovPR0Fg?Pe2-zYLUhT0X|$zQoy7D4U*(n=S-dS9%SX7mCI;20YL*;by(S8 zx(R&ReD~1IkCig}R&9h`-6akkkJY&8mJ<8Jis}OQ_0RwG%5z(9DLf1FFr3XykY>JK z3z^$`zS(+G($_&4=BCtnGo5m7;qI5mjXXt!*Zo1*A+K5P(^gZlTFF(8jKpw>4ue8URCMljAkSN4Xhfhmw*x)8Nj|Z z08Rj2JgiB8qX?Lw`0l8YX(o+*nig$_K1Bi~=Br>kL1+LN%<$=;Puo4!keu>LE!~wm zTQ}x8dX?PmIj`*44VqbN1sgpuj#dT-3rY>s=$ zc$jQdd7d8!`4;}}YO6VVdtcb{Pd$x6$M!3ueQvrqVW6zJB9F8T1;G~@iGu{?d zR4qNIFsyUMX$pNyKhr%DIr)7786Z>ybV#5Y2?-~aXqpLrJ9L^s8G$U>ju9#PZD1e{ zG9E@|9MKJXST&VwFoOy0*52|KZzgtqZ5prX>pL_5Ghk?NK7PrlGiUSpdZ;&e2Nb4x zTlSi_JeH*K(vWoNLA*7H?>XG`w^GAcOK~KIZMFB_V4Q5W7O8vgg8FB4R-6tm+ao7- zEuE`4$vKJ(1bRE*dH`v|D;pg8%9;xOV=ZE=RZlDRbBTX?bF7q)6+hQW7E%PJ;(OM1 z%-5<7YvX@La1mO}nuZqNj0n0@0iH{YOo5gPlzkf}VSPK#IcL&6SSd4LM)zU9Y6Uh+ z654&UwC^zC*#Gr;tvS!{IZqyaROESvV)6gBJEl3e)DdQRH;7p-12TQ9RS9V|_TL8c zWuse1N5=d#&GlN=c(}={lIc=_qU9-ienD|fsZ5W(z9q1IPmUP~8)NN!n$AYeb=R}` zLV#Ngb!V9a%-PJj{+#D;ih*2G%xAV_*qT<~TR72dcn{6W4N+#ZGc#7f1I@GVKZ36R z7wXA?-F6Vy3;=!%OqGCW0fOUfuL+~~%34Z}X!htvZ$qBQNdQ_ve^0OhcSUY+v> z+?}-Ch^`|o>RKFYP3$T4c|+>yBNXd%pG>Xdyv&|Y$KyLooCMxvtTE4r#>Uqa zsErxud6Nwe@)>r6?s9yoYYo~9Pu{KYfH>b6ueHvi9`KyK<1>CLcH2HWC#(_u3Hiif z7%JN&pK5UnRNcK}swoqme5@mdS%pEl8NGL_d*QV$MeOL)BImK9_W>K-Q*M|s*lqH6 z>nc6^I84`|&%3|jG3GhMerI>~cy_^@F&&@GOpIdviJIzlYDemuVt@Z#(invVeWT@~ zBG1~M@?h?12w1x4>|v%^{_OSY-5sm0P_rHB;eGv)bae6JjBo%Mo%w=H@I%+|S1NmplfK#?sT_O4FLIaLEikWC7*#2;I=Q&;u# z_=Z}x&EsF|Y9XE!uyNmK&(_`THp5&F18m6J+21UN7u@hK^!N|>P_M@viHhwI4q8?@ z&MJJzm>V8r*)EFQrehrhpA@WX0DdeSM+{nlCK_tsy@ZBYO5R#ykR!JmwgD<)u>;W00L?T3Hu|FpP#DmQ zO<=7UeBY1XKUci!>G>CS+)+^)xw|^y6az@GAF@95M#w(q^YTYi@BYPY^gY<)AK&h2 zon|&>qdIL5U}`zy?*Bo&lW6TH&7N|&tV+)sXCee~QJXSBmu~fKWnvg%AAHSN7k6Qv zOgW;?K#dpawyZ$<6PiE-mS-W+J2UhCt4{Hl4^5-t86;2uqdJjhDyH{oYavHCSYQ`C z5;22GfkihS5l?4lQvd)!CJ6ul0002EQ&k-s0000?%x8ieE-Nj&qOha3ueq?TtgfiF zBr7b%ysWXXvca;h{6cjU_V>3b!YW}tkL^y`s_a0r-)YzNsY0u+FulF#)5%gZwOSi} z=E{aRZ~-*K_JQob^mwIK!emp=<9_w+e5#&1>d5(N%Hz6k4x^A`LqrW_gn46J0u)28 zvdaJU<4^+w4zLn!W)HWP^yp@rCA-FZMjcE?hWe6BVa2^dA_Sa5j2{}D)P05Xum551q~X_RI9SMQBUwiGH!>D^kP z%JiTVM5qo2QIEvNl_EnFVDHVk1B!a5I-ZImGqVE_e61ghj^-HET3nVOPI_0RCmC3S zA7ec(H36y0Wz3K!s;a(a-(Se6MAZIqv!g{TS zoX&@_uq5FA1W^Mv%76$MAJKxI0G`Z@5kSyD+Rfm&zL!Ol$VhBR63#`S0roNWGLaDY zUi+B-ucxqLeMF5l_duw$3a4oWNZSop;nLw|^wO}+Wc!&@hT{y4>gS+WDLGX11PNxm zOn)+wGgoWNJxoNR`KkcCQ{60qN9O9;5VAuIuXA`%O5 znAdTbumpY}jF!}QA3R;Kq6b#mbH6&lIoB96G<*MVZFJf-b1w#lCYY}sG z!Vti_1fCHIN(o{6>Zd1R&GgL{H-SLGSp7cEgVhGzM4mrr=m&|=0?7xqpg2sM#Vg5E zvSp(UB|yDCyT)L;(EgEQP3S)WX283~_vz#(0fri=Uk@5R0d@osi8%wXzYPFhOl+Ej zp1>k(S{QiOISax-jtI3-4-0!ZjPgEO0^c|OV4ibT)*c$hgf#zUnr-VqTs5YBvGGyW zCQfRYM?ow->3XoYC2P%72uTlRf{mU!NV&I&N*AN(<2&Ii9E{e)1^N0 zv|$5jqTw}Yw`gZ!7#j}fZ&ROcG#L;ZpgEFiYEp`a`G9?aV15E5U^oiU;3lFD>>EJf zE&yJPoFsy23yVD16@&%vA!-2!=6HzcFczqfKmc$GEP!&cPgDXww13vd?(Zh5TK3Z_ ztvBgWxy!0q4uU91fh#x`0kpK5r2Mz}7|W*9%^FtU)RM;ZQF)%F4yNEo$A6a~l8a;6 zhXIHqo=5%V!d-RD8x|ej(3_j&vVSi5Wx#(j%*EWU#4+#;6#^@PMWw4KMupg1-#(3_ zDVR#I!R-Z+u=51mJO!`|_$UHmVWJ_q4NeLG-aD*@0JVTR7!%@ckd_B{d4*;H^P`af z&;U5Vwz8gfDSY3GK7*_X1Fjv``__bC?ol3fHSVJK06?qV8!!+M0ENk)qE2qE0f^^@ z3`lUeC4)VZkWHCsBbzIF?7GDBXZG$fuh_oP{q!@>=4&2+IlSz0?drkln*}>kmzEd| z9db=hMyH^2H}jDI!i6-km3-2}UP%EF2NZ@;CSWH4`febI20sx)15X1W0`~!4ENmLU z0s!C4u`$uu&RZ_4;I_MFLk@O?|#j@JRYayeYu~{uJcg&+RC{w zma>jhL_OQ{<*wX~8PvBu^w@|Qduyg^*Zs3>T&LKO?sfMeYi(9cag`?$?Ui&a)TI{f zcy-rVKYDVrH>KJf1}ck`%_cGTOxk+?#5RD8aQVUCMP*x^Bx{wW-2hd`s!~<{lplNt z1mKa_Yq+(PTF;Od0|{^_ zPwT^wzz-YgHMY)v9$HlBD=T{|d!L1rO(nc%-iJ4eE`QEzD@M}2wR%XVI4+xP*0;N) zo`$DkQDY|Qxynh#5x2yC)S=-{YkY*7^~`*|O)pC)PYQ>Fjv50S4UrIt{ip(nz+^vA zuUxC;Zc}xQA}SvNaG;1U4xV_zLu9pslPdcVKmffDKnLL6fDFKg05Wg`9xd#v1Qj4C z1yY`eS;K5MMl8x*8sZdz05}n70As4tE+GefdsqL~ZPqvEj43~BH|q`U5mlXWhVs_4 zIMjnXPIq-6#L!haFyA%Y0F|}o%L?e5?NY6ka!odk-{br$5LOwO!m5e}!?~_hqC(*ceMS?YS;cGx@?y6=AH6fmG55s7eH}jU78aBZZfDGWH z0EmSE0HdYqAIc6zIAznynkg$gXY;@l@ zDg4lU`jzoxm(ViS*wDjk{64BJ2EBQ`d69eWDZ0q1=bqS7%@Pf_ijf67?>@Jf7i(mK zDaWLow1lHT6Uz5`uP{Gbbrsp&ApBIDYYY*6dM-;Xc>_%d4gE;i)Gxgy|Juq=)*oA^Hn<%_+gXTtv;m3);(li>qg1)kMpCsY{m9(BDxt(v=%q{u6buP0`lWDmiQ{pOr)_N!l#+L1s^R zn%;HJlZ|{-Y&XgTWI+l1T|X-3Y!ZX_V{Yad^XWsrS>4M2_Mfa|#>szuyvK(r3jIV0 z1NS9t#Sp;r%e)<0$-Iv7s#a5)DvR?x-_G{ONyqSfJGf>tC-V;1uQ#)zH8lacE2IKZ zAORs@@H>i+?q3&q;(Gpc@60gIJFmH&h~%L^XYb)yD&THi{r;np2!w(*^O@iC3$C0cq9;;o@4qNh6>1uVGymDe9o<1A0O%f zR72`aK$AvZ6r&2xCV1X}qq8VEM)?u43vR~9X$oSdleq4+gK?4vlF5$lfw%IR!;K1PQBw^nnc zbD`cL4fhY%tgU)AN0*p73_}JqD;_9?^Vrn(oy?MX33Oe?%=|9AqO;^4S1d$s&q{@2 zZMv;(r#`QZ*6X9s(ar|G#`SnQUGEiW$DQ`MrxlMk!(B$WhQgQgUbB3HqvF_^)PQ>o z$ortGoDa|m{Tv{2(GOq~9$YjGKm*V)1n*?a8jW&$tgzTloU%=8305ULsS`?&-WQ*x z8RI$1dipl)DLxOiYE`1zhj9Hy@$ran4(xuJh1*PyyRXk_dvD(JWNmP!?N@d)Wj~0R zo2?LG)pBdv#p8St*efwV5?jCC9iAiXlwr^@7~oa+ zq62gp@Nq$Ao--#HO1CQzzzI6No_zg@oQC_L8WsT`o-C{yK}iWxU?a?HOUK$ClDboH zv=XN%z>>H?0LXNLwg50_SI5Mbd-H6!KwVWk6v}#?x&>?UW5kLv0+!5S0AoUm2k&XE{5$vxFp+~wF^^&hw(xjg)^-A$lchtY3(_3@6sj+F$ zy7Y-IM(7~($Tsq^F_d+N-GPhF1(7xT$$cFjArWK5(>@EBXu~(r@Hvs;<@c#Y3ifM& z30^Er4uB96Q<4nB8_}YU70B%u0_IKZhw{pf8N{)B&_M2)cHSCRZh!j4&C!iVSoNsB z95I&px59N8%;#&?Q@AUTranZ}S?9uNEvG1iAy<{>a5765sG_g==lCUg)tAH$M=n1= z6lR>!|LSQ&U5s5DG1_-y-1Lv_!*2#$vzkYA2qI04hgSVVM`vH|!38(~i|TE0Y?*A3 z)&eOtOiobv3+iK18g+svV3a?RI^G*}8bTNeN?_b4cpFQT*`%C6fQ3^!fDnLfrPwoi z?HJYWZlb1}Oblhk*#G8ww;s}6Ro~KP@l`m@mXK0AS2@@M*u!(n*%v+Rg<#5IJfeNk z?enyO2x)Ty6|D>e-8I(KQ6PP7F8H(Gk(;yOxvK+~fC1pa!ljckw zT7|l1l2PT}_E5>i=?#<@~803ftb(vbEFo23)GB*ClY5Tyxk}IVeEiuVnCmppeDBt@ zVV8R)Bg>96bleW5JEq=2hEgTQt1(|SCPxecvE+9F-_qZ%pnlX^W3YH`q6@z%wueehf^BfL02E0MfVFve7IyBG4NOteRF* z3L`auC3HvzhSQtZHk#_h z+%4y}Z}#H4UUyiU>{!?9n@v)o18AC2$mqeu_Qi&S$#eC>=1*KhuP3uMz$d}r_ZERT z2K)z~g}z&%IL*6k5zujnI;KPr#~0#l4h(2?-@t@f7^T;{b@JN>7;4=EAo4c@PY-?! ztc8T`f>@=L9L5m8J72oBd1I{@-JtJjcE3OZC{HEAyCnx7Y2AB&-0ZV0Dd(SOt{MOL zl(qp4b*LVLN^O0;-E4M5J&VR<<|?hbg^nfuPiJRS002ON2><{9006jCRUI4v007mWmKPkSsiwLpE-S^f zudSk`xUI3XthKx&FE7l+zr?j({3{Ir3Wy0v-$UG@jcD^L80t>gUhIeRSgB;_H2Ab! zuN`r$5Z9N7zE_vFzOx~l>H1h1@$Md*G8>c6S6*9OW*ybYdF#+2@3Gk?)_yLPS0Vj| z4aB2i!TsW{Ev(~UwcFD!E&i5)PznNpOFnm6b>a5*1W+DH$z-GwC;qMEGml1nhPsc* zBJw(LZe{_MtvQt?H54ZB=NP-dqapJz$gMPq3;L7*yxqR3|i04IxNxl0o-L`bMk$t zuuq)HTua&K@)L93ENV>}N3IAn({~QZL7^X||4gQx+1gNX%0_B01SlPy-@xb6=%{$b zoS#I8{E_H}G#T>k*|b4Pkk7m5p~0ybiH+c^ zr{q{cQbr!qI&Wof+MSavdQlJ516!ik>R^Xw>XeTCrRfTb*&XTIKm%m}*G-RWzl>n>A;F%}xQ{6CCz6+sNIe@I z1J>Fzi6B5KNJT7UTtWQ(aq>ChuKKm|9ja8TGZ)nJBrc7l2~4mR6Yg7;j6VDR+UE`b zp=y`9Xul_o^i-4J?$@Y~5V+WatwTig0d$0XyrFa9$-6UUEZ5ELbt3mnQK)$9V%q8> zk(Fu69SaK<%m5~zm@VdJuQECz!RZw6fdZYjk3KQ6(TDQ@NrG~mcaBr>Z0w-7v0I`E zj*(ZG#Q9E_;Iwqhh!p7D0%L~(-i;)7$;6i6U!V>oM|1*pfEY;$Olw?YC^R~*V|9)U zLp}4&lJYQltMoMY14dUjqn;=%KXej()o(^dlSNKlf5=aG?>ff^L7hP=W&Z8(sLVNu zuk)w;_q?0^d4BJZv3x@VvGYl_Hd4!n1`Q%LYvmaCOg$v5M=Lg=w(Q-h^v*R^0FNQbi=s(xy-5R|rkc z^QwEvPgPijYjoDhw8dLxmDRd0hx_ZC+plyIZ*2ifQ>e&npgT41f^?!!0N(6N4q>Lq z3=t)u1UP!`6dO@PfzZHfU?1g`JrlJ=QtdHOP56KJyemdM_o*pfS)Yw`>@WGd>`q*d ztu<5A-ea#_FqVow7|uv=a(jYmxi`m=`LJB)vnCKm%W?@qpAX|kfUD?x&-3|+!Z2+N zu?;s7pKq+uTmzobIi3B{-}WnM{Rt66bSlnlIqcW8Y&a*PKe*=%{rqNHLIHa;Vl(j$ z#HQAoaT=g>8?MY|>Td8g0iNu0o`P4V2p}^pd8X|uEIQCSCAPpmRz~#d{)bJpMaB*GSV|E7bN7lh3=Cmn+TZ+op??x-<}9RdRGOR{{>R3A&hj z{rP${h-u`&BuaVxJ%4V?j;47YN_OB;ya2xs$YEu~HFNGUBg0Oj0qVKBqqgC2+~NTF zIK&OxX+pDbo6pFV>&Tg2wQRrKD>*DL%0rzk`nkuRN$FK z3PZaC8Pb6Q`@X_E9ZcH6I@yg^yqul_hrn5gWw#zwi5 z9^xt$mHiE)K(yVZc&|%Ef%N$NFe~EJ1FChupCloC&(EAUGO%H@{#yKqO)B1C=QYj1 z9e5*Ef*eoVWW-FG)WS$60g`-{q6Xv z1wP#K4j=>;12OO-c-jLYaKn-Y1TKyS*rc?s$7pyl7COtgH^*I!7 zp&Wo(DLJ-!;s9Dmmn>TXQ_ztWa}a6AZiB-sisy6M5#AM9;G_qpzsA`17R0jI#Ww_I z>Rj!WyF_Kq9&ZITS*V)E-eFY^9~>W5596&$-3ip2pasD$lx@yQkKcwcZjys+*dPFt zsA%ijRru*1%!>{oA~Hm|CnFH>92=OePRM1^i;H0aR(Xt(LCKJC%~*4NG4zu46yYOA z+K6$j2ylN~TyVfQB$V~o;#r&`?ljF-uVQq*swx=9w{hgqw>XLVFWXNgdf%CVOvaUd&5PMM?!x z3^TBzq!v8kML1-qG$hxS#3|AO$Z0H-a6$%@cpP=;ElnIW3GIJ+E!$&4Vz`iunjm8Ow2l>f|1+=S+M$XJwP z^=$mhe%4$qW`QsVGbfecrVQe#IKNunBqHp_DIo+V1sr?uvozs8>>4v?ZS^GW(p{wy zO0$Tz&UJ&20KS}Cstp(egu!+6%xWD{>l9vrH*Dexg$7`yJYW+}NSJuc^nAfR*vERT zHBCw6i!138x1xMel+vbt3Uh2bsw;LX3~33A^vcvC0(jhf6=2CX8>v`4ph|y6;CyZ0 zjI-8H2oI^BI2YVGaZ9W{K^;}%QeS2oUg4$c1wkaf9O-`Kp37pcPUcv$5)#5u)3`K> z(+%w1o1RTcAH@8JM{_(Ifs<+w>JA7Xo3Ek-y8u4iD+Pfo00!{(Hljmqgi<<$iIfZb zO0mF32PFea%hf}!&LvqlI_gzjVvR9!(3wl8JMLZXyd9i8rh8A0@`00v0S0l6ik~lq zM5-Ip)xf08Y__N!bUZfB4JI;f+Ldtl57;(L?60<%?B$C)&S{a80HhU93ZeP() z0N$*t4b!??GzBdXJkwq}thJ?D>riR}rwaiAih0RGD#NP;*NUU&p2=nl{r5)CDy=Ay zgWZI}Gg?uKWjhI#s!wVf;?vRAu1SVRQEA7q)L%7q8{M zi$fhfjg6P5L7I5Xtz|50P6YTI+Hv;L+J}Q?oy2~UULmaJ9ksRMS&5Hv71-_&Ij+&6 z0iodGcGSdP&8BsMI8&5315+QL&%M=G#iZ^e4Ee%9ngkyreVn?{BN4WF<0ZhU0YT6r ztE~i|dh&_KAIts#m;YE}F0NDLtX}M+WWM)kTB50MY1^{=lGhemTFUc3p1;sF{-Sk! zxn$p0H8>@@Lc>kIU-wWkE}QS&6^}GuH@+oACn5H~Twjv%J=E9j#=TC4Rp*L2=_`26 z_V&I}^C(1iwhm4bWP?Y6z16N*ok6b2Y!Rm$;qF=flfyPnkCXP@iCc~7lHeVLy6^c( z({!`VCR4o7aN4?zu zI*S98QqmeEa)^kAl;ex<{d0e>{$9~#wVvkJn3!jC3^Ep%1pq6{vMvdA(v^AR%1yS? zT4n@h<@&BP^QW^sCC<@}gjoJQCC@D*hv=PB6=Q%8B=gcKMdpB;k)8o(^1hVf zVpp!tmXtv({$Lk#S#BCkWM9x8e7~v3Z^j~4Q`q7Cl5s86*ixS1Ow+sLcF(*x%*a%WD|V(U!(JrF}!<3&(U6AQNl8`L-iK3sDY zLYEfk66ciTGoiFZ= z&&W`h6er_@;Gnrm!R0vO$hqm;^cl``FD%=-&9<}1Xm zT<$$L^y>X%zR3{LqBHCnc5wJ~u}6t6(Q%V*N`0reozAhN{lUzac9+MF5#c2Cp==H< zs%T8jq!Dd~u*sHn2?uz#72}F8g{w-z4N$$Mh|i#2vVtc13O-EJo?-$EQmvda&QIa2 zkRs}MP>nSY($taAwSIQw6kjuga#7lXtXC}ToBl%8)-baoXn8=*-V z(d&DJ5eeGqR6FG!r{c8L$W0H!yv{aFhv=&K3HDlMqq!!E8VfecN{+Hu&Fmq!tMQ<^ zlTmGu1)2izDbfm78-F6cDq}dn3xj|L1>^kWEJZFuuI@S_7EL;=tXhGM;guz-6w&ku2!~_wj3CGj`pl;( zkGJcu3R=%dQTm!l^>#)EA|+Lv=qVdbnj_O~Kab>=8Zfr4-XViJoZv*F8U`L34!V%}U;kF6JFZcWsjnyRlRL241A%eqb zt@IN%^aMVvt4Toy0x$vsPvH=nT-LCKMkh{0Apyj^yp+O*1K`vdOk9O&t?R3zY->r4 zUH!Kw)!NQuJNI_IG9Utf;1}Go1+o5o}sZccA$SKdovukzhl3N|o zW#S+c+I)t6TwQ+K!!QM-gQGjo0=Rkr6|4OURQOx~UW}`ehjIggfsoL1p$Xx(p{)*~ zNgRqXCSimRiLarDHeBWDdsglGzF*9(%U*JO6d(7Ldf_upi}SHz#A$0$IpKmF=`#qj zjV*2qiD?;^q{LU~1N9$Ccz%S*24}{#@O0R0+Dzcytg`6mQO9%2ltM9Ygjw1ko{9!# z!VH1=R)=#W3>!OYnln`9b_+ub8n&EQg&LDLK8c2nPhW#eLLorXWLm|IJ^(&ED}{p) z21y1^Zy$^h(g@+$Y%U0?tr}&zLP(RHv6jTghTx45A0x53kb9D>HT>Z;GMvxX?A0(T zBIkj=?-C&7$vIYt?F{#IAGPK2=OM&ub0^fz8H!|+tJ4fRtm44PhVZH4`|a#LQ=BUZIsivVb>ZlsE5jbmi?}t^G4BDo+$Usr^#~b zgJH)wpKCyAwvt-uG)Gtj9*pw^U_@91fq>c-JcSpbEn$d6VPgX)A}xR@k9`%Pk}4rQ ztnzA2yNUPt*!Ocv_Wjq)%hH!R1dojxqKWYoiiq9=^?EL!`pgSKUmdT!+D-~W3 zMbvwnx&>B$o~JcT#GnzUTx@B>=eQiKn0P>;*fw|34NemPK76Yk0U`rYLd5F}ksa32 z;pmnoErC@f#e7cda0usyw!Ht#l$|wM+%IJRj;&Raz?tg>XE|k;d-pmfD?kK;2xR^8 z+{VLwB%gaD=6d1`EmdFTY-`1{lnEHwyJnzce#d%8E1)3gIKH42vlY#Z{qp?cqw?ee4x(@)QFSZ?#W^4N_YcH3gttf(aTnBmhs#$?a?>5^!v_j zo19yA=?dQxkn_9nrLZ{{@vhSN>#F9r&yN*T9oU=mh&i~W*p&4m04~RMlCzQHI&)zU zsv-!SJ?Y&SA$BuSaWxV676$riggkvC*HpZ)jilqiok5$=&e&K^nLtw)lo|j}u|Z82XvebyEJrd(`U?{iGei(pTX~gDQ^?Ea zyfSPQv)z|Wv80Y84J13PAgB0Y${=Ar?=gLLPw{j!KIc>y4WB+gr7mEnMQJN8ka-1| zF}3k0WgJ0n0o4IYkso(pwHiG$)&jn7Dy!#?Vct4kT;J!5Id(YrVWOMrDGH;1++BKI zI;*2qA&hZMW6Us9;*?@|_3!@a(sSv;_jAuN(dRTZwsBaQ_kqAI8Bje=*iL!#xaYD~ z=f5_qd7I`wb$GGT>w9Ll<-FBJ-AneSQWv{0jfpLGyuO?RSB_POccK9w-_I`!DQk*T z+bxo}R>}@2Cr95ujZf3U21NMp5O<52gG2}D=YH}zQ3Argi+NBrz>;5vAhPRv4fumo zQ&f_=xY)zTZRGvt?U=&wu$A>`5=a|d`t%8&Tq|7#6bvObifA}Klin<~lU>Sg(u}Qf z7_jvL4%39R)X>y+TR-P`pZ>gE<#rEm*gyVuE%kZtSzM`grO>3Zo)ojZFdg)5)plA> z%{^v zfEF;4h|h$`Wi_SRNJDK29Lg*7q7wiFnXVYATaATp6>JOrk8b9zhgEJwwErR!v;!KN z^R{Etk715U43P)*>=cT7PLZ-Y1S4*f%UK|S^cBbpP-Y0tvj8V`2+mtfP!B}j(Zx~Q z2lQKYy=f0sEgPUIq{yp$a(Mc*Lp&J!iNc=rttn~grnm*)(hA@9sX={;wW1mDLBwdv zqd}7iN+mt2>5v3e^dXR1!sNV^*8m>8Yl%ZP6m$~s`ppe#kXGjEwff+6ZCYT39k!fq zNC1E-p%}vrWWT0weB(~HcYmC5hNV@*N+{c~&y}0m!<0od2PGXGgq=Hcwnn)UQH~9q z%S{iRn3vFiQd(OmH>1i}y2lUd)m`I{&i1&0Bg;}bM_9iq{8^D9Hfw<2kB;!~h9qhH z6)3aouF67yku)qdLx#kg?TIYe(Qqf^6&HzJwRFrAIfnHAeP6h)E0T_tysiZB%&EJR zW~Kr@Tx*F#mL@=AaeO<>f$*}lwb6l299F?b_d%EpmfdQwHwm?RKYKm>W#?DV3?JP1 zzf)YYZ>dzDZ7p1FD;H)^{w zCu;7YX$VRcUYwgc0Lp;SN5d&g-X4dL+QLa8p~PX; z%GgV4lNh$`(I!(~HXWkRqvje;vb)i!`g?__xEu8}Q7#E}vZp>wDGeeX6UbMCMO@Qf zi~_OEwRp>}h?SIX{sf$WN!Iw2cKh{@QiETA%-tXXCM1Zx>dwdpqLyBc7vP?pg)XDY^FCT5mf4XLOHM zi7DD>3fU;~nAAF$6LMaHdQTuc1JBV5>OPIMf-cj|(%IOZKWBCa3-v=#j zhSXCU@q0;~BE`y0Z{lyh&&S+~%g)v=$xIyz#BhkkcTqh)!HuYb>8(A){G`_vt_QK= zRXj*AV1d9RJ0-n%+_(=32XlCR)t~H5TZ`EJp17RGc*N-?zL zid{I)s~zD*72qiDBfEUlU*b%dYkD`PrtK3ZFgv|xjc$s2<(3NVXgqIDq)jUJsumW4jx}-cKl#zc} zd0xxX?Ip@9=rceoN|3A9g6meD4RM-WZ5cD6M6sEFB7vxiYWEs90(4N_vtW1t^Y9dk z9h#Vn_l72>{N``P`nJ~;fFMJH060E{7hy#Tvs-f`3+(HXl8ufHeqfO%sh3@usc-Uwp||u=$;slp?TdHs zMpx81w?FHsl;LGzk`elC_*akQb2xiZPF$f`x>N_8Jg=Er?1~2VJw%TOVPpF+Ep#&4 ze`knGXfR>;-L(TPdOah_LGqH6vsn%~<6v5C4mGQtBY_h@4ZWSHM%R-dOM*IVdsJd! zU=xLGz`{|I7U`rCK730RLTHAhC=$WzS)D?<35`zkHV3PUm3=*?%^~4VwZg28QroKQ z`B&F3Rdo+nE@yvy-qh)Oo~;Y=p5<6nz#W4VtxmeSu!Kxq`CZ?X%rr*3huR->DH6%@ zlKQhZYaN!&^c?J${+XXqtcMYaTDM{u$#F|{XL9e*PaR@Ws<(t?9ePElWn3v~o1wQl z5K{}g6`BGj8z!^-#jW9O?i+Zitu@|AnF~y45TK-6R&*SFMD~dzDnyJTfkY%*0(d?h zKONp)MfrJwvt=3rcVgA53AQ=To1mDZExl%wmh!q^pL(S9y1u>S)2cOpzeMkPS4LeJ zZuie&3avXwY^Y6%RXr|!28O~5Ri&*%Z<=_c#Beh{qO)!OQmd^H0s{@hf|rl&v0)bD z-B++2s0rE??w$2YWApsQDr|+&EH?odi;-(evpbE?Klz3}zTpIJ^7kH+`E=dY9F)GJ zgXFK~gdnI<)CvjDe?Ms^4#X`r6hTbY`q4MlK{Vxk<$J_>g_a8tjPky=ShrE`_75<&wzRb*NI(<%gGe&);SPzziaJy$R&+KFG{eB6(S=56%I~~&BqFe{*Z`J!v zQmVdk?)J{EF;8>X`DDM=p7W;{d97{=`%r{tNvL-Q%(ePJxn#vGGoT&#GRS(84{dx2 z<|{fap7XSXEB_OE%~6_m1AK5yrimZOoM>Z}Ils=cOH(@86l%T(er$7$!juN+1IHB8 zQ)@QT;!G5yc3QBlty)=`A4(Fk0*Q=IdD{8bQ}~e$y!Jf3DT5I?r|onYqr$CJl~qbOKrXfBNRtxYPD^oizw(jP`G$U^vl(17 zdDT;Y`kpyWQ$6HnI?d&I6PxwcJ1CCIQpR9E^l-T6Lgrl*r#+0R?eQ`U)>bXH?&uTf zrS2L%b$)mR2imFs66^e+X;tDUaNi?E!83#_N8BFRw0r{It1}EjNsx&_>FD(O*{~21 zd*o(}j6OE8YGq~FOXhP2M=Hv5l-)VK8?RG(FGl4k(=w|2d33x z`p16Z*MgWJKmB>D;!Tj~s`&nDQSt0PM+#$o0`g|{`}-tAPOvgmueu4Kg~GL;_j9Zn zhQ_)HT4SGw%-qmKVx`rYlTyn^-khm^yRaVctMdNna?Kxi4wp8to^u#%y_vv!GfO|Y1`cFOR=MIH%b6qrS z;J)A(%#=^_zmhz(C5Se^Jyvx4*GQZ7vwcQovxIz$$=5s--A9xZB$*(v) zBWr|51(r?OGg;cRgh{AfP7F;jy;q!s|Df(%TtoaEmYYSmhx5S4xwlT1*Ona?wBG}b zUU&eXu4smIc`p$@oGTGPAwj8tXF_exZXNB6=Y=)Gu~t@A>IrHM0EEs+6;9=?^6~sv z$qYa9_WHN^O*ZW9)cUQM(1KDChnDx;o)wD05^EP4^6_cEqAw*OkhR4Bb^*spS8UgQ zb0Vf>%N1t8VZd#Yf41Wx6w|X3cQ9 zwM#)vAeW@*<~U)Cf0MFD>~D{cxnO^jWJngaUkqLdfc)l{79!VD!=nLSi_;XKOC$th z3Z;}u@@%95FRj;WW%gK&K7pPkz*?6q>ym|H36yC3`cmmqHs5zhKJaUtwjbAKbNzb* zPPG<+eVZ>}%q>@`b3b%W_ITWTJIeok*_%C8v%o0^IUBr>Md@Oq7qi0sg6&jGolivWQ6d^RDR3562xA1#0G#?Ecc?~T#y zz0m6&)4DZkv^`0DgRHg#;G{Nm2qzE5uWJk!Uw-L`H|m zXsjw1fy6*s0EAOI7`<lFeNzZS$9V6Jyq~73tynt_r~rk-|9!Z8?W{Mi_1$^A zAHCl^TBuftbxrG6hz!oQbT69P+!qoR5BA0Vr&+fuj;AR?SqabE9Mu)Oi@BxCoQU>DD;Bu1U|@TU-5$zqIjask=hNarl}7 zSP|9PpR}IsCVsr@3BUv!2nakAA|<%fu;|n}q{B+NIFyvMHAOvtIXBlPF+EKmwWe(B zt!!=Ga4P=aMZ@Vd41@Y?DsGCra!|8O@5glrX#Ij8>?S(*d;J#ocvCjK57H~eTBoh- zkoEU>ZE7BdE7Txc)b;Eh+B2?B6*{KvDSHj(Q3~@4?41T?;37rzqnByB5jW9Vjcl4W z9P4gIL$0zRxu2_SY*fCBLP<+i|GIyN_@)(5Ap0Rcvt?i86!O#JR-v$B3wUPE_|O0RaZ z{AUNycvGn1iK4iFWf1vF7dXs0;#KAU{DoI{-dLo1Oe2eF~ zvEBw-n1AZLqBLc7nh(*XUKf@eLqD!f>6GURwN&(wa z>p;p#S^wbRp!pkoBjrlraNEvHi&iiHK0(?ez4Y_G=;Xt@x&r{j^_9*35~0pH}7^N~qE8eWf=mcSv6KH|7I& z4Oz#jP#d}6qiE0I^j?>Qy>5y*Tqz*;gSo+!vBN!0=by>ie$JFqJWnHMQ*mXi@AZJ& z{AN&~^zYQ=2=?rQZfXcS;Bw<1OG23*$VqeOIq7S&$$(CRGIdq0L1C0|ILUD>NYL7F zR3_h#n;^*~ZeFj}lzvO21_3D9J1TjgOpucT6hllUE&w*Q4+I=v%+GDqTAR43`{c}< zGOKQd&b-XLqn8Mg-^&VAJzl_myF4PaQsP$%A-8AUWc8V!XPyn0xE-RWZ%#wo4|QRe zIe#EHuK&n7Y&+7zH36`Y4+#V$z9@mH^oF;mx0>2p;|uOfc21YeEj2WavoE5sz3Me0 zoxG(x1UkIa4WUQ_S`hFkL*%_ZRN05?4b={J5zhWD<)TBxp(yEX%2J1^@tST<+xGaq z(>{l9dEC(#@|Kxt=z3j@>N!p6rfOg@loIOv>C~%K@2Wn|6dAHh@iHrhsiRpxJ8?E| zy?Vlzg!+b)N=)nfUVfaf$BKiYu`6$?&n-fgP&{Xuebxn)m*bABe{gYsYv#Z})R^s+`;-eQ>@#l>oHWQAXCBsuSk` z9^E^OjUflg63BruPb4X478FV9)T>=^0H9OdVVY8E%Dks~UPsVq@Eh18V$%QVTJi?y zQmPZW*a~xsmb3b3=fL2QI7v?N`!z|KUAl+Ci9`CT!BDYJ0|Wkq7D6Pn>{%PG|Kn$_ z$O1y#u)5DNH`m^*N%$BU_NFD?TlFP(Hc*!+?|j-tglZdL^ZI)ShS@Ww2!~5+4s&w*enPUr;10KvPh6iCl2f(uv z6dV?ez)F&OUzzA^SfyMc{z1bQ3?7=8>{}r8cnt4Ob&}tQ+m5VL*GzCN83Y5EG_00Yx)YQvjnzwh3Lbo6TOt5xWoHyVGDH4 z&!Mv_iq|Pin^<$Z9B~W0ahoNw(Kz`&v~rj>Nq*)w-35)*_Ed$0m7WU?Y^b`Fw$l8K5RBqj1bWMwKP@ut|%z+q+)v7uiCOEw`Z`gpfK!QPiL2yV=HGF zKQU$IJ1(T;1h(6B(dOjD96ke(;c%qNT~zAztu0T<5-hVk?KzDr@pd=#Q5_tGsnbW@Lh9rk<|_*%1}7{4!mC3j_fJEA?v9+u zE5!J`^8d~IhuhMG3O&IKF!$&BUtz4L7=nX<<#i_3`xUvR>2>jcU3oSnw@~9L6897} zAnd3zi6RZP`+#2#q0_KC$4#sw&qr^SP0R{7TQi_?;&E=Hf6@EEG_qs$)=MI-oqXfc zCrZ(Q_*kOzlsr`kaaA(Ee!5w;lC4Pyz;LcviMk+^0#9dWQvd)!g$e)w0002EQ&k-w z0001%?160?sIIlL$So`@(!0XJxW>Y}ueGM}RDV=uxR>4rsE_ z(U6S2yi!Xn(AqlJ%vWv< zB1&04v>bGO0;OKCIvllB;#6RB`YBiXUTbo!g@;?S8Lqg>_L!IVjLl3y!*O=4p5`G5 zFxSijiGKqWP-LluYOKCRnmFsWMyS_`w{r8iC+aL>1geDMh8?0w>(=vVr5pubY^#NX zG7z)Gs0Dy$2a}Y{%;9TjGhp|9)KxeL8C>CZeJ^u^LpUtx{(d}}Xu)tx#lqONZNasI z(vP&-p(3 z69HY*E0DQX5+_urW$zo_TlJ(pMvG|9O>(t)4^L z1Ux0R@*}S*^szl+3Z858kt34{a)41w1kbCs6HHJOJR@SR!Pr+Qof8@!T!p>EF8pS^ z%HLaj)3WdJ3k^3NFT#gwOGkErRmFOWzX z+{T_qV_ast>251Z8jKS`)xM$UxjIHaNA!56R63k>c zi3;gF!iYE+J!-V3^C z1C2$i7O%stvW{1LWqXwYL4iRW#_UlieFw_Y5`cp0bqHgw9LErlrNx?GC5(~3;Hw`5 z95+O9@~ia8)1w#Jikp5=$`12lO3{*c=;Lm3j zWlhK;|JU1E&iXWU*L7EVTl8N-;Z{o1W^pjtruwIC9#ewz4x5zqqU2g@5+CT8je=Q^)|N#_%+xEWfw@uv!1Aa!UF7 z=AzE&`%%uubEL>#dstbSuPU*igZ(y*M}28>i2nV2PkSssnz7mR&ChFn`LHk^b+qd- z@m_m;9lH0XFdG&N9hgMmmO*_)ySA2=t#*F1DN**yo@wt|s1%aG`fa8Mz0*u(SAXLWg}A#LBoull!V1E+Qqkp*3k&$BLHm&x|2-6~g4 z@tg8eG@LnRgM`!`&HU*x*6z2;cd6w+>FVcOUhMN` zmLaFe(W)~u6BnTHXnrjM4n&^V@vCEf7c90@(Cwn|qMrxN2j^`WZPLtkc2NZ*5l#InxPmwu@p=Y9_sloOY zs>W~EWf9ge+iv!PGMDm&dovKh zlmQx$Fn(TbuL;rA_8=k=ShccJoJtD-Ff|FuHtmF6fbC0IS27Z2YBQ{YhJUYmyF;7Rq6Y zO}z^>*XvYo+Zykq=EmxJCDgax(I91(^ihQ9alV=;{##3oM0Se^R3P+hp$oZS#L&dp z)Jd<5X=Nu&S!SAx+`m1_yyYIU-0*6}W!mM%L)7yDL&Rpl`fi1u@tQgNCH%9JP7nVi zj(5)PEtDc)I^}&ritAGA0~m{OqtUJLV>gC-3ik|`~ooz`V%s#|VNgq|hIo(NU|tSYIqX7*al#31GW_C2`s!u@hW2x)Ct;GVSIE_0nR-vH)5A@y3kv4G z<}}LplPiQOCk8hoR1-zjS=s_da3jC*hECoG=^!Y9Bcb$kZ7vgIkwYCe<|J(%R@TL25i)1K`cz6w!nRd5e8x!Yf9Tub9=)$? z$awKMoV!3rJ!KTdq(mCzv44fzEq{8&UGh?6U0HcH_YPMtu7~x;r1J79jd!GT`p+-F zzwE3RCee9}{`@wCke#&LulM(V%7v7XjvD~=7cWe+jj@}S&km;a7`D((f@|rjQ|gK< zq@6{AMUqLX&jJu9eNB9~uBk9Sd~mLIrUh6@C6C&Xjeg)79(?P?A|Vx2351?bCP;!N z<X9jw)%=*jo0fOoPZQCmM`u7Q)Lu= zkF?s{W~@yN11^Vqszy`S{uWJbo5)kIoN9I-Ju5^fZu&sIWbYi;FI5@i?7%d4XLW7p zQA!lYs8DOrE75#5vmr;4nwm;a_N>q&Ynvjf#%aBrM7Ef0vQ8Jtaz%G{a}>Y7?jt|0 zkn>=&H0T`XbTMV?ayApbo2y>Mk_E_u6$Fn;eMEp|*UP*JeNJQvnudKDkQfLJAUslC zx=H=+U9XZPL)1iGO=bXQaXi5eZ^eF4O*eGfJ@wQP*|z1VzC zxRx4CP`ZjhI0Xv!fb-vgxC^Qw>=~Q|H#H zqaUruS6-*&b9W9?^roH)QDO`qaWuLTcWbVv!q&*CuzF~osK;=EJZHDv8<9;r{F3nuSq38ktY>u%@?&NLCtVAk-PPM93E%&&Lz{kK^wik+=WaM@jFRoS%K zIi2al@!>;@W3C)KY~)>nw&E68ZT7X$ppne?5^FdWR{)+{^M=4Q1BjU6c@&VDlL=dR@Nr>cR7u8jd9R+j&Pp6Zf9*VollqcDQ0`%sg(mCBi`U^IGBy{ z*<;L-aD8+$_r`Ts6vnzA#GwFa_=ZX8u$@cSzt9svjd`-=Jxkd?0ctFuu36@uk zXtqfsBMAnOge2T4hBE*_Ngy&rvip3Zvq7;(H)~d3i!>tM*cZkhecwN-pMlnplys!)!QCkgUUa!;}CvDFx?v7luaB5kOvZ^WuG858s> zfiUG5FEee=pH$L9)Z01fDQnS{Dy!_jyLNRk!hJoM-{S_uCXcvCs#ra;olJzJFF+5X zMXxs$V(hbR{@MMjZRut5t9HwgmE5fIMJK3OX>6<`si*=JJ*?FFODju_XsE}r&4_Hs zOXg&+uPwfLy_G+1cv1$3Zx19r^Cjx)KfNEQBA zgO!Pi5W3pqy^73LK}#jvjL`GRUxX^3hvz4vzoyQfyo8L@L47EXdZMrZKwyxE*xMg= z$sg%P==NFuB0`qOOO?03bEvCfG$JMYm9=uD&2AL#=*pfPg9+mot zf(CD4t<+0g&;YAU*9RO!YK8cq_KX>btQf~E=g?N1tPCFrh6)(GK&lT(){1Y>@p047 zfEh91?}-HX1OIqKL}&AyZs4Ll&P14*KGrCj{Q2j6ROa^7HC<~vm}`%LD{K}N%?7n_ z!My67%x@L-wR#Xkm9UkYQn&t=6>i>R$y-z!kjB^{eBX`gniED89YE7qq_m%)#+MlW zyEBdigal2r7UJ0{lw7djxkO8nc|Vltsnq~LggXq>r)q67IK>ij(~1%WpX%u`-g`q` z0Hw*H{p<2Z%(2&+<_%T-5e(Gjo>OzZ%}$2M8g_beewp7F;U_91+b$xc0wz)#HCm1? zZ9ewOHVMC`c9U>7%xpAXpHwX^$KR~yucA4V>JMisu@NMlVtsW9tXW)J*4g&PPu+c4 zlgL@$CjlhqK90eo#*J|?15amXQvd+K-UAhAy zs!P&Vb46-8b*XYAHi)O53eQc-)x+Pz@M&y^C;&03I_FayTsL33&9z<6ElJ0iw)qZ^ zBK*2kAcLSeHdb|%NGrALln=Klj9Si1=o5NB230fObF@O!bO?sNA}*ByYFohoo}2TM z2NwXCC3tq4nL*QO4%Bl}&=-UNIIK*2!DTWrQX!5#y}9RZynW^~4cD2{ttoyLXsMsk z6+zA<*}5V)Cc_ys%k$Y_Zml=raY~TmmI}cNfn5Pyz9NZ~z&4-7>p6l=yP5gBbnJP! z_Q6WlEZOav2pmg2lpItk{Yw&HdTRYc~&YOX^RM;FhNgu9|T2Mudr zH^Sz7@>#IG8c4WtAx6XiH0Hf+>iWY8Nfn-)8zTn}Q~*46z1h^;R`X(J=^#%8X#rNI z^&IIWY#clqzhd$e=ZLnM>OCEKEoWTaOrX=_I6=WT-8-ZoDSKEk>ywtE_V*#P90-pn zjxe!Renx16R~$FnT^zP#*cE)u)s}jPpz@uGOX|lt5fe-E6BV0H`KqbZbfp&hp8I`# z?N!Zj=1^{;d>`@VTi@J2MuN67TnYIv@27mBxtI6I5uB~|Wg)preTZ5@+Ev04U@Rr! zF(@(DK^0dA{+w$SKmh>odZetmAXrY4yL1veEg4HOhJ$5&BR(V9<;GgchS8cELZ&{X zXOD5Dc-l5O0)na3#bEm(6g)O`89bqb9^{vNRu# zjGGBfa^u&})a_%l?1jRmfbQ1D|>vD-a&xXhK3xDXJW{hw)*LYiH7@ zU8nAZ($?AL6|d<(xjPb85?rCuO@<1fSz<2)z zUVJNsAcX-zK_YmfWM&DPPebQeAWjLE2JJxUGRB01TlG(M_?|`}J7QyPsHvkho$wSt z7|vU`<#w@Y!jA&V6jxD1_+X9&wkkI;S5mXoOLJJV@tH;97u*TsTxF$BEY9o?0SM>n zZpopcgx4X(+b`!up}J6!^_wXAyL{ZYyZl%sdLXkY2~u=F{xBBuCkZ`K1}loK}aaHWHiCE!%i|tlCscNfZV51*Q8hI=)m90O&${j z55o9^_OC}&@I#)L4^uImI4kl}(|qvZ?LGGAI?o~W5SnQ`TAlZ8m(;|=Z|o&uZzK4l z+*DaZbpEU;-NO_;9EI3M^?_^!E^eK~v)|uTGk)M-Gt?>We2$%Sp?dn#h#tVuNyw+@8$wBYay3`WZ)ffGz9sa?RM4(dcvSoyqQLJ&xy znPtN;xz*T409JZ4MMZ2Ah-wn}_{8(ghlFol_1m0Ng}F9Sf*|Nq&;nqe>N-5gu=i)5 z=Vt6-Q$1bt8={o<9M%Rx$6Z|XZBe3V99}#f-3W>e&nel0%X2)3Fd>F_34FSJ>{)S^ z1eFIO)S}ISG;$Dae=?Mx9M(f&I2AMVq!7H0n4lQ$DSPk1NJCDYwO7+FWXq@d)2dkQ zCH-X45aQg-X}KW;R**8@V7OJ?`So5Ccy%YqXa#Yi-7{>6c784XbY(iG;x#|I% z^pMA^RYA6bt&3q|UzjLo6Vjj;vuIIxH zH@O5`-?|WMF1D~Y=HxGQ!C=61J@pB+QjWL(eBRh)>V9YLRN0+B&eytsorkIUGViJ9 zvH$z&@eDFAAR3!PnBmq0P=zZsrYzrnGUxc|#5wV?o})Zpb=(fJ*;OkmV-;ipqy|hz z^RdMzwcdK$*3Hp-Jm2`ey`rT5(+!|rrJIshm23WTJxw|HPLB`Sgzb+m*l&K7#1wtS zop;xHtq}tCWXG-pG~MWDsbNf?p{qt$+;G5q0aab6Zn~hUDWfZ2&Ir?zgR5P61wVw)Evh)Qt4#C!-i9*JuVtM*Hbx%jJmKh zk#P zlmKji78yt4Q3+N=;M5_S1iNZwDNgrj0GN^v`|xfWb3LKO5Z^Vfoz_0qB|W3DZCT?G z$Q@UL+{BBG>uwD3=s1qj@w}D$A@%HDzLH^+Pmi$HRsMFD>h$X{7z-6}_Ip$m^;4ac)NFha%ilflEA^y4Bc)extqv(_#-y z^uLwXqi)ujs3s%~bSSlrX}vBTh1>T^@7+ym0N#s}j6%b)jV>cB@w|qej7?*i4WVr! zuxe#hv7SN+<#??(y3HQO6PZuWn^BIlZMk$fk~v?iiwp z-`7|%bF+3QX(3j}(B;%&9#U%1!7Q^g%#94OT&-c>Q09_V`Khs=`5C(rx7)F9rjrL= zIp^t-+Ul&89Bx7G)H&BR42A0m9M3^*!&fK0SyOZFj1nb2Ke#JM8xI<+bHPVrtXf9Z zDZ>X2s0n5=Iswa!gGjId{(Ey8Av-FfiPaE2ui8#@gh0%NF!bCcW7S?>Ikf-)3=f+t zwMJO)Gd*bVdg9-eQGJwb5B+f)Mz0DUDMX3!Q?WQc-7I@h=%J4SiF1C#IJE(B5O~#{L~eHssOHZK-FI6{$^~;WT~*%04{ke)9Vh zWn$fKGfgXbx=I!#%4>c_bu->`pF$OL6)O6H_Z*6uQ*(R_CIGEBgr3e-_5jR4GrxP( z$2opWGZH~p2||w6NkpEV=D9Ji=*`hY9xXYmR!YWRjNoXb#u{N0FZI*s8KdbM?EHqPEN8Wx_D^2&B7gTYp~d!1sdn9LoW z9!_eUvp9F^7jc|VSsOb}q-};+O@3xVg)F7O@l43?oX3$)SGi$h%6*%?DIpXPAv1#)^l87YME7jOCus+P!lW^i- zmb`DxdtZY$c+bz<&g|w``}7b?hP<=!dRGmR*E9@+@QXLWX!)pZp|fag3Ip${?zC*? zfmVV%?PDs_GOc;Ksx(?hwGZG97Uvp(W-xMWE^wb|siF-0@^KQS7x&{e;0uvT2L)R!!&ZZ#&{)=lx zC1FcN6pO~;=?e-5K_em~1wpLZ=K~T0X#s>+5_aK-Hh(DYjJTY8jGsB0Ga7DOB4M?y z0}8ZJ=~t!qR^FD`Fiu;9_4zD{O?uA$JI7ndJsI zRYRbW_+EFX>n6*5OyiNE=(a32Dm|ptJSM%89P$Ma_psE4Zi-qUnx##Q4o`hMf4SAU z%l(|oU>jMrHP_0RU-iIzpQq)@uynGw?yaL!!`wF-Km0i^5%GPu{TH@JTiJvMT)ajkPwOM&*PmBDj!uitUr^FDe{{;8& ztxagsRaz6v_jSWLe_XqSvnp`h;ypZQaqdVCqzuPl*L?OYZv7&I&n4;Xys4+f@?N?w zT2V|@50AWxde_!TSbD6DlV}&5n$pUvl1(22y+R@0v3tX)guouDz?FzIMgZPha|J*F zD45}S)%FI1k1QJ*0d`we9)t3*ituFa$G<6$@b085!m?V`SzmPWZ|5PL(lt5%_-n3V z7?a7QN3tc%n&E7Q`Z{{sAtfpSgoQQ052&%zr6<4H&HrK(d&}vh2lrO#C+S^ia4~|W ztxc21^1SDKZw^}agA+$ZbL)C??7Wv+hg!7;)3pQHLgmDKieq#6#cEpanovc6t1`_3 zpp<%^XHx(Gz%>g100000xKmXfApigX1}w-d8^I+kE6BICysEXZrK+o^ z#K$BpF2BIWy|&7^7D_{ffCLyy5%}SP=bI0S^W*j9y7u0zEV@}QL1N!mmG?4^l)3Zh zF1^*rf9gJ;{l-`K7S?}1_to#^=Kjc&TBe&3{Huxq6LUyT(a&}6#H}!4G@nT@ERWIC zbt<;d$7iSU%pisl_WAaqp3Zn2*J(y?-*EXC8d`LH<4_1BQKc`*@2)d+%s4762zA?C z#Gl%^bQ$Lgci_ujj}Rq5VqT|(CVeNnZXpgiu|KSu;UH#`>K&6LZML;isyi%A6C6E6 z4bM*&^+<3uP2y;#OBM@L_EulCC9tPeaz*sQ+h3HzNh>5Z#MqwVzm4b;%FGZde;Kyk zjiY6^UCQ2Q%55{}fep8d1KRu31*1p{5ZH8*eEY?3e)zP|-*b9m`aFj_)iEMLy$ZH+ zJ?)hMaA+J-+J8T`DJ@CAUkcXvdFr*&TFKGw&Q{-$3J3_w#U1bLzB_Nlv1I13(?&Kr z*P-C&)H#X1dVHFj!TF3hZUsNec~5S!<4%QTGDdnfG2Nwb%poh17b zRWWB6!<~f^0UqreCxDe$G%-hmqu}YJSVPXN9U)>RaDWZA>=VO>g9*pe%b6e%vbsfMS6Pc(zjPilJdQ z`b2Dk=RMpwpO6r=2#edX}Up)iX9Bx!`}{LH$k$N_u6`NszSA&kc$^M3ufP@ zGmi(^!?^R0Y4S=O({s@s*9E9RlrY-Ligwa7%NycHqVJJ%6IR)6U=3Ji+ir`ttU><+ z>hquA#8yFk13ujI5re8BW^x=VNJ#L^C6zLdndYUB-UJW(fNj|)Cc`vpR_v*pYc)0h zY>iOa=wTpJtp%yvQ-u@CRO^+yjXrCgH~*8a)4I{xa%*T%(t>Z{1*!89qPWu_;+QuC z%}J&(p|O7}A>ULF(7zq4iA_uXfkWl%=^YH3r6^Dr&FRa+_Nd zVJQ7p#oSXqS4e61peaGNE)4WrH?GjcOtVvJX&*He<7S7b>`dnET_GT+v(WFMK@uCY zt&}_TyKPQ2Gg#&P$xPcvkA#6s4 zs?G%7%S#S`laS;b4Hi5boMz2zxHYk$>qE&j7TCCC*jzQf*1}^;Dz)l%wVj{WdS}S( z#SPBK{Wp)XrEj1*x1PY+1TLAxKZVT33E5VOHTK?ZnuH+3jw+z%GkIJ$*9Fm9Ryd%C z{F^5_yVeMcY`2IxKY&gf=HS<$vlyzfW2`QTAxG(uJw{vx;JEw@LSAO~o~)7lG? z?5-p#sZzGjwOch)_H#x<|_@C79Fyl#$QZnt{FDa1HBx$hWq&; z(Ey}l!;~*79oGO9O|wOUN}A{C)m~6s3BFQrE4fY=G0(Y+WO^^Ddns&K03>86A@57>v*)!}GyV@mkImSS3N2^jSaw$NvqIr!iBh(EgSY zEipG)1s<$Ro`*sJl7tXF_iQJ~77V_oInM^jeN3YSOGpzqFD`vqMZc&TTYJ~Yhws@+ zcaOXLy8GEr!<2Kut~m9cdJbE^Ld)Z1(w6x{eXovoqYY(^;3Eo~h&Vvay_2@lRsLCF zMX^4rM8escuIGL}>Ud6_3$_NL8%ij-r>dK1V=m;#WB_m`%*C?Y4ZU~1>Qx3LlH!O3 z!UO{dHfM(3C^vsyNq1o0e%0GiLQOf_{-wifhD!@tKM9E9H1vkcND%;fK^sL$F?Y+_ zu{HJC&H(0~&w-apht%D%Sh^OaUxdN{l8;Q*ey(;kokm;gLm>I;@2xXzWc z(1C4QS=K}{91I@y#;Y~~Se0LQ#$ljd{26=`hWa$krH`~Vcb8}7hKZW#*4~aM=wcW` z2M6l@ocJhl|1r_gv1)OARu5q}<-@u?FHC$(bSvnxDL1AKV&No98o_C0+Z=u+9q#z> z_2nrJzXsa2C(Rwj9-iBAy299T@!zBfCQr@|GxSD1#cI9#!&g||BYWDPE+~wlUDGN76 zJBfR>SGp#-VJ?Xv)`7{1fT#5uUNVR%&W$#WlXcc#?#QI*f)IruinpS-?vlSu3aUnAadn%;pOQuNjremzhWW| zf~RgA{;zpXvhNrKJ03BX3}q&Zt{}$V9DJ`hsLu8V0dsqm-D{t<#xyW&aDMJ_#r{#0 z=2unoy7s};UGK)(B>E=#vNLkn58MOcI?+y{+VXO}{VAnh0uLO;vF|2^KnK!R4>;g0 zeU{qO4Lfn)L7!&y9oQ#6C)!4ntaMsM979Y2{pd->Ny{et#qH~hKU9Yy-^QQE)vqbX z6>hxa2KQbFEISMe{WY({Op!S6`TKiJeM>O6>TQhu8L#T_`wj*B8eF2(raU7*J~N6s z0Bpk+kp1r$&15Gw_2E{bBX~od4}Wiky1`EX3L%< za`7Ytb#091PLx_30ogj3z zQ129VOpTz=LCv%XeT0ti+o6Kfyh)!OTyvShCp(AGH9eJ*fv_eZxiiPUO3`R^+%rn0 z69!1Q<0PK8*nsNgHw_+aa{@qE3ASQ`#M31aklAZ;v&E1Wv1+Bfa*7537^Xu?8-3a^ zy9fPywLP1o-><)I3)}OVt8WSf8tA7-}78BKf3 zkjrL&hs}z6Qp#^f=a114ailc5)b2Cwg>CE0 z`lCWzcW<^_57Q1N=G+*h8{nqGHGXcpDm;0%U-0NCsj!+>Pt15yPj%=V>y78ZeAfEZ zDuabN+c#@_K6+(-N1crnW_4U=wXF-4CgJKn`c`5wu0u!oH@fXw+Voq+6&IHvRnjl_ zqlsGuHMbKx2v-gwNpUHM^TuPL>u$?lH|W0sf0K8p8~UW~>oKp4YHL z-C(X~yLHA~gBi9J*orj_QzfL{_*Kt$_A}1nJlw;H%%it7rTh{D^2`H&=0WB}IXP9O zhn&)>*rR+PU15QXfDcm{KsCk1KSqk#v5{?+$Fq}Z4&CKQ^X{X5cWF<#M71N8AM;Li zr_h<@d>;@~L4K4$?3=jEuFjIrc#O;yk)XYyj5?i@v}slMsmH8vai$Nbupm6+?ZYx6 ziM~kHNh&0FgCqc6OVbRJn&v=f1Dee~i=6xYObmr}w6BSM+YgbS(j|-AbkF#bXSu zX>&i$PR^-=;o6wQ}o>{^sf zP-}Xo{siC7m5$|wLO&7a#>0RF0DK!r&lXp$BM_fyv#=!A{A^+tesRk3QN1gXqbSvAty(`$2f@r`x&KmGlm z;K;8^h-oCFg4iq7voDt>XGTfTn++>Z&ziF2oLDdtC+3cT~BtWGk=L!^(I(Z*$9sT}S6GS0}62T5F zc0JK!xERw{$d32CfRi@q$E}ae>iK-49N}KLqX{)?It@=}XHx(GK#dCk00000xKmXf zA^-pY)`ABZ9Iv#mtgFZ*EiJsNvbC?jyT7xnv$we;EGaF`!Jb^}%?8IXAVEN%4lbw( z21nfpg5LL)Q3{6!5Dr(QWc`Hv^mc1jdh)pm_8C2C#aL3oh#!2j5dqHoyH0s|Y&bY8 zOxJKcRnnWrMZ^a^o4cnez7Dq6%#;;#(nU871eYR&C1IimFJ>m>uX5w}Zn)+5_m|VW z^a|Dl<>fIm+J+|VXH0i{I{?tck_e2@>a+Rg%!qex&Eb69DsJi5n~wOCAe2yYeB#hV zD=Z#1(_*s{yiEL z)0F=Z6{$+wTfxXxNZMeD_kbzkPvn;84fAY(huF9_bv6+vOlFbeKyk7gu zP^|h%we6R7QP`y=KKjaAPhMX3^H^*wtN@AISqF{{kyo)dHtkd5ifyI_emtv*gC-NulF=eSpDrnA(6mmoAbG9Y zS5esD1U{sr&{Fy2)u)Cd>aVvT!`;h!w7gdgh2glo1zQQ^Ss>yon%VHSJFKviWlY~R zYG~tRbc+XP9pr;>kl6|AM98%;uQ^(NPfpKzyw{lAsHD{%Os_FF1nhQ8O!fp)nekGd z!f~tz2=RrfH4$>l>@xJ(VX_SEIe~Yr`=T?)o7tVrqonLAOys70u%}f?6J!lui_@AQ zH3_N;@Tk-;4oQX)CL=Oz^01GwvW7Dm!dZB&`ne(-Nhp;u)6hy*wu2jLCw9l*D&7WT zOqEpEZyftcC%2{-IWlmQ_ucm2i`8#QdRN6!jxo{W%k0bp>`%aLLCqJXEZ^D|hSQ*!wToPibvkD*iX%G3t#F#Xo+GORzhLf-I5p_@gj!>bqUH^m$ZBL>1f z?tGxfU<9_*BswC%bR-lF8}W5$@|eP+45<~s*`j0`csR~=y(bo!Kh;F zDqUO}fl}nd^Y^TKJ?p>CH@wi9m9wLT{>=ILBNRXG1gu=kZlktS+WI3rkIAJYD5YqL z781|TRoJV%%B%9m&F=$^4)QQB=_>&MLMoTF-(OA{oAFbBORGBOT;I6awNon;r%>)f z1wqDQ+8w!kip`9@83CzFsl(OL4I(19+%QSWwRh{r$jb2Jjd9zFxvbeDn*|AIE3I-o!k1Y#sW;ofzX=>*(K%BImt`ENs!7wdtGh zm7xjj2u)+4*g6u>^^t9g+Lkjlh}0~-IE+zO5i+ET zx2BcM)|KfVrIPZPKV)5tYwQ`XyJ>wbs2nG4s}H$SyK&N7EFX2R8)IDT&SR2%43ZkEu5BVzX#t7^Pk9i669>R1PZ9oW6b3>W-OD3Dc264z=u zP~F(9k)23GSYpvSg zz-@+VBet`0X`1|X_Fg<&Ay)BRl}{u{e=`R_DW=aX zPNk(8{>zJ+2LT{qhDW8IIn*qpKtoA+0$2esFQGe;W-w}XRS)|T_#?3e$rVkzP^qgx zR5A5;m8-bpz8{y_hTXU6Gd{0&Xkk9gddFDL`&A;3H$`-J-ut9&0?orEVTT4u`+Nq+9wW{~0sgCVhJ+9xN@$Vcc}+6eN^sI!z_N%_ z&;Vi@bQ^TQhm~5N_TDuKKja~GqZ+$O$wo_1;AE6i+_LvBSreLze&oX|v0!>if!NjQv-n0MkX zDE>HAQ9aKVZc3tet=gDx+Y(SG63R-FxL)(F1haen-u&7|#{`PaQ^6S2+V?31ew&Me z0FfDNHE^**3!V)^1i{U7VmAn!2CM+uULNZeT9UzBeBPemb0h4?jb#T&+bws9@76QC zZlfzuMe8<+)THUU(dpv`sg2-{0~4YM^8j?{Nd*ncJ4)&Xwkt)_t@uJS-gTk3$2RHK z?L7r5B7i!;{q6|?4gP`O(Qi7gDseq<`y5wVcks9^8k)}?cS_%H~AFRea%IIFk2U+?W(-@WeY5$xJUpXxJf`SfR`upCv|?+yPkXb!D?fDqji&Zu_ex3Js+6B{ z!m!!TX<`d@(@2{-(NV)WR=|EJZEv|LhcsZz6|ZZk!#$;qOA4#<+UEY&sA3XfFzW-8 zWQEfWRaqySQUG3!n>&C8$N;>Kg5(CvM^*}*^r5`6vQnnyhOH|-+QYo>^9aq#ZTfm# z!EX-rA%lVcMCCO#DlE1r4;VuFZa4~IG!EWYOy>a&2yR%y=7Szsr2xwKT+hA?5D4po zWa+H>m?Ddvk8tx*`uyheA2dqwp$lTckTzwxO45PkDA+m`22w%vx{NO7yv3nR%yxMX zTp7x58Zu$OtAgIJPjd`C7HTW64f#l1LvE$E2}VyJ4HC$Cl3;KaOt#YpVMJT3W-wTJc7~*I)+w<*b?4dHAT@o6BOJIEX+13&qLgi_kLSujAR0? z5kowOFN847nlxhKustg(etatpVby@3pta%YWP*~Q`5IO&Nct3H0K^z$MpHtD6%CK) z|Itll!X6);1erPuLm4<7?B<;X6}in2`^90VT=5Nk=znVJnmbE-yIZKTLJKxpsd2v- z`-(;Er)Aaico?h6&k0l*@;v*4%&FlqjW^8D63=2LQ4DqqhFgeq#Sb>+3`u6%%#^pd zcgqc8Jb>r0ne4VUU!x86lFzd@I$$Z~7SY(Owo?rNo?DY1kSUOg766Y*eV8CwzDX-P z!0xM7#s=p~=G?fCNHhHnY;YP@BdC8JjJkKeE{+VhL%~_tF(%OwX%A&)*N%_95s)rO zf2h_W?Gq4f;K>}v4>DEqseG90dGy|*;<>7MmMOZ7Rj;e|+zsHEajyeO1Z4q|#9bBQWx9+iga6C9~|_9)(G*5I%cp zAzyRzxGfaA!fufR;&Al`j#E^Wspou3fgt?%ur>_BU`*4{oXXt_-7V3pLEX{49s)x@ zM&9u|Ua?)@>p~*%NMx;esa@F_%n}4L-6T&qJ8{$@uc*a zss6{g}QViNzYdf%8fT-(w&k~uj_Pjw3Wh7lw;gfLFEiYeJB zBGvXBf9yA=lA9rht%s}}iQ##E+wzQTJzpp_T9_M4joM^C3AE!c#!Sc}$=BAZlj(4& z>nl;`-5zM0>z}8$v7gU+i@vOMLC&DFQ$??KAQAbtt#{{rMvvNE$zl4aH|vcZ zb(|efmqF1N#%uN?#}Lr-afDcFme%h+$wt!cDSo*Wo_L=4})f^y+`1qr58B zi4JbH&QD^~t=Oev0p1H_06+l(0MAyE+w5BT5NTv2jb1f}ZRG^30RX1qyQ7hqH0D)* z3iR#i-)oYNO5MKS?}aXTt12Y#cu9_Y6moLA9~rX^d$I=~kyCAefh!XR4*X4dxFne# zu|_NC{)xhd?uu)OoI}~EGdpH*Qc>&oMYO@g>OUQA=pSfXX_6v7=?1Jfoi>#xYs}pk zg0h88_Lxf3G?>BP%V%t*xiCth%FDM5N8YI^I8<2Cf>nCK@h+32bo!*j@~ z@_MZoMHYE>Z5BQ78-{_os2BG3s<;X>6)b(NP#5Z_0jIwqn2K0zr50^&CzyIgV7kNnU4EPa`C*M3I6onYGAzh#S+Aj@vrZ2PY z=~KOvlj~>Cfs;fNkyFapx@5?M$qRr(?Pl^?y1|x`BaI&F1my&1<~S^!-v6oae@l|D z@o_4SG#ud*+VvOTe2^M?f-2q%_FAlZY6^b)V;DdOQG*J2whFy6Q*&?3u`N2G=Y13_ zJKW4ua}>Ft4d(Wd%N) z(~y85Ln23zJ`R&aSTa&BEx{gOE2eds)6&*f63)F3t6zSX^ma|$M*NOWnRtiu(XOY@ zJ&hT97&gTBRujQkRbuMOB@kYk6c9;R8e zA;Qm=6{C3B9&|3N=*Z+Vn)_pFbFc z)|5)Pdj~`0G#~F_y;j)RHHEY^k?3@EI#Kj)DSc$mgy+=oQ0RHCr?cw0&ce8?Pz18u zzz+!xR8+*jACKiE1VU32drps|iQ2QGPazWxGz;K#y2Ej|%z;g)hAJM>@XBLx9{_%= zONoaF37TfX>FJV!W{GrD3rVu8R#xVvaKZv0!J!iSr=i`F@tzK&dGHcs{4>lBC09kOL>`yk4(O7=PP3P`g@txpi~)aZmi>$qx}Q zL06GXKjkjmn?G*S9`WIp&FDkLZBN6m_nXGkO*iUoLTo**-M!I6d$%z^&8U+yS_(#& zK7?sQv<{RoeZ#&G*Jm=(b)uu|I+^dd>|u3I;lJ_d-r*F`v_lry!JT5qwHm>YTs;D#lg+obAbYHlzujNoAE1s z{!L$n_1wUrnCH{dtM_E;0V*Q=X2CyUGT|2X{dbD@1ac5y_c`?yavYYs{` zvb{OmZR+TaP`zo&-lkWt2Vzcgy#_lc!Ea?fhq24K{O{4#$cdI?#l~Zl->?U#JVlB_ zAR5{akDkjgw6qJpo6`wk6^Ijn=T+Of6|E+C*5YjFAh-D_*6=37(a3^23j2=N`IY~9 zHoGtD&tLlO^lj6%i!olfhSS7ddX**#mH{HOi1zJA;ax^04L!JCY^szp{FCb26=RN{ z+<3p53~V(v-Yt{^Q$6(aEV?5*ogd4w5arFUSLl;{4r^_3a8&o^D>rkTs|9@TO*)z> zF9pf&%r#}am3=Wwk=%2-jR&84a}zAaN}_G_YWYh?xGVjur2pOqP~eQoQYM>$yhD*xP6z93#2;^#4}Akj!((QBOgrt zNM~hW3bLnnZ`O^MoJ(NfA`1jAzrDsdj&X&La+8is8EkH`JwpGN<*D~R-<*SrD7VL7aq zsf#(&9@plc&Asnqk|!TFR6e`$7*u(`%38QRm7S(53fQlqII#3s#PiTmelK&cyhx>i zy?v^Os;K*PU7RMT!kuZzp3991_0y0mr@UM}`dr~S)>O;d@FXFkVe%A=*qoUx&*MW+ zVeZarfvn0owcc$M!$vS+us9rrJqo56v_^TC3=~HJzS0tIqI@O;nhfrPllFN0JT5mO z{9?04+yxVvtMru$1VL<+oobbD2Wd} zX}qnZl=!i&^Bo)WRant4hkdHP9k+XK)T(RrAbQcIzKI@WScrT*Wvugsi|0k=ML?n@ z>%2J+FR4guS@e8&QlAa3mk#just$MQJx=!HFO2~D+hdJD(F91LAPM>2{$WZ_M=|}V zE-T#gcwCpuXM;_d7byV5R367$TL7Ta92(!_Z?E^>$3L#Q&+}f7omRXdn3j@oo83z- zC}peMs5WG$hV5vyK-3EW+OO{RlFNq;4Xoa<3k4 z5zj(L1aTa&dUi+!a*!=3*<*m2GDWx$OZ?L$~z+AK4{}z#mdPt)T@j zU0=a=Z4}L4ra9>1dT?_mo^lccp-#V6-jju(dfc>mrWqAuyGmtMq*+?DAzmcJRyzBL zAsZ4^I9fq@&lO*V!h>5m&MVP`xGlxtn+d>78#0=+&=Z{`0Dk;4o`Q!(1{EZq!6t0d zPQnXf^}bS`?*)--Q$lJ!$G$E340qV`4GTsEB_`hBUL5!^v-TpJl20VI0!UxL%@(ng zG`e$%606tq4j_xfYi*Ns=WI)HO<)q}cZkg)f5OL&gQ{1@hMmtMiUu}oWPb2TI1cj5<8_?i$9I4zO8c)=X)07XpmHLwj-MGE<%yHovg|#Yy;zw2cg5h_9FYJ7c!1T9G{||HBe$8=IMfjM#_K;+RVPM%=ghD+ypZxi_&TJ9J(r+JAU`nPguuQ$~c}3!W_ah565) z1YW@UAmRS_TJdA6pON~@P^;R)#?H`lYw;0869F46&KQNu%B?5NY24}saaa*2r3p#VW8oJ(9R0d^GuWW26t0Az+>$IZ^%vzR}~5n_@X6==7aYl zew(940YZWy!Re`M^SmAAAW{adA(z96zyesWln|IWHJ8uZj&pySn4)JfyKmxuc4=~! zp#U%ry=K?s)8#Ix!|LilbVTiRRLVW`PcEb5Yk$}F4(3;G@AUT0=^_fo`y7AKxHrgh zBRUEba2}}6sTt3(Cw;x=Kjp|N>luqyXiszXBLi?ao*kS45LB>%T_Mawxn}(}}%&KiDV+J~k!y3Z_`xyzi%iKucymK)hde|5r_QG#e9#UswtDb&v zY^EKlCvghIIx7q#8;L^DA5BxRz?q)+7>O@ie)+T9VwXR7t@8*b*YuTF6;J@3S(DLk zq7tbDKCFw81R)ZQB!J+#3tbX6N#o?QV2H!2@+jfaJS6UF&(eO z0xP_0J%>DyP`Pd~7ka+WGlqnrB(sfiEz6Kv1=V{fha7V(ovTVzE|+^N$io*waY2^u z*2dh<^}|q_K{gOA6#wmc-qKR&(Vv9*`0sBt=i}MaZXbv*K(-rQt zG#ySFWLVnn%VEBXWDp|a(Sli(eL4NNdGkT_w+KVn#`%cP3bkUIIFMgz5+*XU0OLbM z+0@VG&y&u0rgRqPZzs|DV*6h1Sae++ubO%yOcN86sQOSJw}k($Z|-53rhc3qb`D_! zPc5}&?_sbPRLpGb?_BJy{YKMSJE0%1BRm*QXe;-ulISQ0dFyP^BTVwETKI@J(-}*F z)PW(2XysAxcr5@=XJ=CY0Kh>E000000Ju|C9V7q%0HMz(vK*(cB`Ph<$HKzJyRo;g zw7RdrCoCznrLn83p{@RlD+vI}A|Zg|Ckqb*BM3di0jm%5z&3UoQlSK7aP<8pe9pTx zj5LMWd)VhGw{q~RUq-GwM$sk(s+g$|2I~Mp711Lmn`+-J`ATX=)s#lkiklG_wXOOr z41z7l;0a0CobsR`!Di|nX;uc9`=jr`nn~wvS45FQ{NubKdYqpW4pg1vAryDF;0wDn z^z7j7iGj(P2ywKdZm1~}K70=U%!I{9BG!o`1VZr)RV2J4g6H)iCL`-|zZVg=d=7ks z#9?JR3|>c;>}}{XNbTPxN@Bk3Iq^knY%m3Zu!*E%fviXwz5L`!Lh+1It?eI$NhQ8ZlG^g zav~?*O!U+$McT^IB6h9kFVI@tU%{y?8aus;Q14cch~*OqoFGO|QQ;#4JV$td{h^{G zmuNb&Yx3ULBB}E}(bCz}ZQR6_^OwKgjcP7(J7ht4{H{MMW=M%QVQyVNvcppy z?fQ~oYhhF!ImE}b{*Bg8YmyB|yY+ta%J}i_ZRA`zs1k%r73>GWnWlHADko{w-kWhRA$pNWF7sY}oI%A=2AT{yP(ZLKiu(1db`C=MI}aXef$qJvM1rwX(9n250JE8W)O(=RMZz#P_^& z*fx%%Z2zBnpY9xt&pBj_4{8Ikf4@cfXMIqBf^j9;Fw4jJFfqm@A#7T{;ka8tH!3M9 zpDR!v&(3dtH8!094B7QwhQpsN66ddmdga>fO{wnaWauPmv0Ex6{WJIG*i=q=O)zp1 zuD6@AuP)TmN6po9RWqBrqnHi*?|z-6Vx@1D8pAy)t})D8l^{^Tg9gKAP~V6IdnN!r zEOU+kHyIFdI6Ze*Daf@wxgytEhI-Y?N_JQPLc$U<+VU>4Mbk;I$um7!eh8wDI>jCA~y+xo6E!V)a1}@2-`Ge`G8~0dNT_`ib+&%b;y~$i-Um}PUI)n zP_{)`)1FG$?@WEb+r-r>z3t1FNm5tDv!`(L!qR9j`zIAq0=`Sr2w`q%LC6rwC_Nnv zizFjDgAFZLu9}x&+h8aqgNd^n62peJmZrmonXGCY>XJ@tsv~YJ<$2`&!)tvX&v!=!EmvQk;!EM&ZB@AT*1eU6IopkcFp#|!17k10*W>r} z6DbA}Hq`SbU3>4;97JDNmiV{nfo3LJhde>(&6E2x3fpRShObMjTMKB)c|6>GWK#phD6!-V7+4J z9>;q;`|;b&?b`h@=VY^oqx;1)s4z3oq^MwnDVBE_W_vcIEhc{6pP{O)cDm)((w6Z< zg6giH!LBcAD%c}oejWMl;3GQjM>5rb+VqL?fo1}&TBiYb5YnaV_T`Z+q}WK!KhgfJ zIkf8k$Uskpr&Zg=`%C^rJ$P7WNR8k0OVFaXFjT1nXexYF6cX?0u+0d5i!;K4V_*Ma?O;Y<0&c(fm`2C6^j=@Va|ECJ zhLByz|HaSjO*a~&TtkDr(3B$AXFJS?0{{K_hC+mkG75<|SYl3b;&Fb9X>MGc6HWUy zM2!ycB(!i-E}s(tKCDZP#}E=E4TPUN7&c;qp-()grf#eERfhr+gOM5l;mA}uf06WS zx)b^_vgW%)f98MZDV=Lc(a#18U}Exgn&zSyb33S=yqP`UBtv$=b;z9^k8~5Ey-z(2TzgXYxz1u&@rNwCF$^OeGao$Z&+;ok%vV!T0s`JzNz+m8gU{%@ zrL}|=b=3x&%=YNn4P55R-hg5v2pTJhc&lqdR*EEn0%7LprwkiDobw)Gj~b995Ih|^ zEz8Y@ZY#4h?7mW_C0GPr#lUQys>`dT>8@Hi+&SMe=6&znAGIoX-WjD-v=#VdB@WXV zSsw6!is|*;>)YDzqq=wu zu%)3v5IZR;Qiq6p;Nxmn4n1I`Ucye0cGQoW-?(I<7y|;fR165O(rQd`d^QFUDs-UH z6pqaTiw8a|i;09LEl2{KpDx_#v`Ec%H_gh{hjN+1hLlNdivN||T>I-Rd7sh#Hwq7B z^s(~%n_S+?<7vJl3*0010j{9)UVvOdt4=Za~_>Fxpeh6xX zc*`L}4Nj9tfjf}Y2 z>DR_vaaZX_VYmrx3Y88NH@Z^BAj&ql-39vUMm9#gVC7E0%)ym3+h%yss(8zy{>qWEpiV;^5rIM!Ba^C{EVI3DHcU>#Q^reUA9L|ns7fuv%KcCTx|-x9iJ}c?C8A3r%=5P@ z*Y8z5RBV*?K2=OgYBB_eX{uLP03gKj5WC#pNac-1?vI>jTREBEd!J*|b1&Vil?x~; z)cQV>$@RoxpvMW))`z3ZsQcs!mm9!CTb2hqOONo$y#B(@4D6%fZqEn zX_@)*#&d+-M2ErSn)I#`k+f@b$8V>YI+3*J6EuHDA}zEW?h`e_4)tCT*K@!hY zEUT5tICP2u{)-eEfJXqOSQ3&vcR|X~NOxG0L)ybW(J~A=wWC0#Sw8n>k!{O;Y{Id% z-Kx8}%8E89hJrmuFrwFDDsSr9!cN2U5Cdns;uG$iZf>@*(Q9gy=AF7g&`VUF2AY!_x&%&RQh!o9#P#T}yplR7I!e&E0(xxUZ z2NAVp?}mv1fB{4{92rJ(0NzU!5<-YTlqRP;K|DNnq0@`TnhRK(qb~vgdnix)=q?{@ z_(dM8%Xo>e+}dwMD`-5CT<(Yikjgv0!8S!3_M_@LkRUjF)JqUfsl`V=Y&Vr<5rM~> zo7pURYp&T`%gl*d08PAy@tY54GdZx1X>2}Le4FCZVxpF%$Jh6sY$&8`k|7QTGy~Wi0Rt%h+ZlkfRSb^X3IP6#)CPllp%OU$2%aw7 z>4nh8b3*f??V(&k_kr%af|~HrS4S;x?ysp+7%r<*9+z+Fx)n)A=O(e&reNQNRahNxI3fazVnsNa0pE2Dhg^~n~Pyd8)wl^a9YK&BpP2Q16c9g5T;Z9YsCQF8Fuv{#8b+)BNYS16#FhmDyv zmQKL2A%cGkvpmY=4+khcY658wlvNmC%S~uPObD>GL<0#3%Et>V(TVx{po^$Nr>IT@ zEuk&}7=$e!Rs(V}gfRf#do&J1k3c6Bj=wO^9!;l3`xGn*d9eW43AVadNp0b<9jL}f zx=&h&uh>Qz^dGY6h20hoSReQf3X%>^M+(FI?EfK=6-K;?5vCSyoGBE-KU1WTgNO15c{6uTv8|%M&O(@ zHx8|OrVhVT2?voUiQ5P*Wsu3porL=yPX~j!a_X4pwTXtb)xofI?)y@j{kgc7xfP!f zA0s}{Xx#`$Sg23`7noW6*O;}KPDQ!gNzoS+0u36fQfGSP08eLUQvd)!oeTf~0002E zQ&k-$0002t722E~uArc+qP!$9EW^3Axu>$at+=$WqO>I~EiJpRs{VW&M*(<&1j4*q z^blqpdVP}`B;>07Q0Ies*d)ArxX-Ya>~><)v!}hHogswAVGsJtw9!rtqd?eJWy*-)p7DAu~J^&C>IVvFRoOCe@fw(r--x6)3golkx9YkKI49g*%!&D~os&z=#-7{a(&(JOQGkZ5F zbEv3BhMm%@df+3}`j%x-&z7yrUBa;VXY3>h*L31D*znIv?@|OyAbtK|A|24)^u%%nM--Ip@ zTc!RIm)QIJlg?Lsd2YntGemLScqQmnRM05n)(lNCy$l*8q8K$u_r~ZArPU)YuUDuw zTo^YPwVJ)baHou*!FB=uOEi^%iXszI(0CJ&;ModyL0Wcri-?35ZTAY>*atgJHe@%i zb4ZDJdd%_6Jz*PXs|x#-GF23)q9AF~_d>BT)rCzJjB!{is zVH>S`PU?Y3-J_4#T94CC^>B8$A^J4rNeFH{kk%lZLSKF>d7HC#6dM;y+!M1HMF+uG zX@dOe5yE5>`$)@R6uv!b+ki4SKtVu3W(d?M6#za=ER}+50f{+b!LtR0PUty~XqKAQ zdml=sf$p0GUSxJkv{{~IIj4oA*JHtq6V*Lj-mbFMj4Sift!w{nLnH3{a8};{-Is7S z*)*oJptGkim&@k~pc4{~tw1Jr;4$o-KAlwIU)b>kHYuU>FzjHa7Bl6!orM~$b7T=x z7|^U5rSOT6X!)3vb&sOAheAZMIDpDC&L|kgG6a@EqF}55c1mQ4qKHyp2;;!>%@yYl z74^qS*QN0_f7**%CT%|Ku$9aQkA0VTduz|{`?Nl*H)+dm?^Dx!zv1^)Fx<1}IZl^d zU((_LOmqbW5U@{ARWlV=T~;FiguKyI5v{xD+-p|H)Ik&XRKW>Cx7IOjK1U6Zm65YXe509thUk3; zL_bH6=e+;^Y&TQEnXaa}M#HL= zm6d%k8F~k`xz^{zx7;>P|vTwybx!TVjcaFNH)mO*Bt^dah6oorgT3R z0|}?s80pq4yqOU>8*8lJs)=r@VShH{EvZ>3)y?yMg{^MQtbUZO(xz;3$~8>-Y%)V|VRKnG z=O^$IfHTT*}U2uvw8XzYbiU2Np688(7B!fk{q)p(N@Y01@Ckri=|0izS$18J%*fc3h<|e z!cc-~0rx+Q^cqpr^hwJ0WIdMibObjrISgRm?8H@%yy&30KH@DoWkZ6#!WB*J zpwui=4FLY@6B0vr0}5IUI6qtHP$%s0IN;bB>9CjUJ|w$Nu!~rPgjl=mv$7vOLK30; z)U}RC>Y+!&AsE8cdoZ@72s(}8lNTi@;f|{(q`B?xC(9<8X5<8>F$=?AZcHU5!(@<2+3#?KUugTM;PIgGzSE8xJZD-{7@(14n2gJ zY`8A3P2*f&lqq~xNA4Bnb`iCXDi|p#ap*F^l$+!be1(apt^2}=9@9T+bBNw`%IQEO zILa{Ia+hf){u?wJ3*W}wk!}C^Nxot)b&l))y~6Z+_C{insPYZED#i|vLhs$d#?}lN z;tSUpM+tIb_aI^Nth=^o!xR84cYYi6G;)ad_{y5;TaUh_c~woUNGSnE^Qc}b_Ffg=egh1u ziiSKKj+IuQ4os=zao$&RP^_oA%|q<&DCXcEJZ>`+ITdqs@VnlBX^G8Aac=PQP(8~b zaewLoPY1+7a6W=#4OuX)s|k863RM7fDlr)!qa?wOOkkdn z0A9>1GsXab2*LEc!WNn)!@ZU!xmnlpsFm<&DYZiD7CXS=lXY&<&=)2Ci`;DA>Tx?{ zA4$>v9Q?!Hw5}?99ctc^eBdVar@zWUIqPtHz!_*rPtmyu!*?=5vODPc?31m)Z&7AA zz}w7;MJ$%SM&@xLw`iSF<#*l{HC9C0aM1t*uuX8-GRDMCx%K&` zB=W@X-ld)Q86~xy>ETt6R0ji1BMx8;R7lA{;5>@&7s+J2Uj=JeQA6 z3f;=#sREiQ%FA`uoq>M~nukgPsCow737+U70XNMVNY0bKYto8|!t!I)XTU4gO=JcW zA)WF$c4l;Vo3J4hvY>lLhXFK8rBva5Gcyba60QI&M|gw2`g_cfGd1q+BtZlQ=O<=#BNsV8JLG@fR2Pmvcr#mFp`pcEoo&hmd0yLxtJ~Svi;8t|yaJ@hvX%vqfM)mB{F;0QNjs_awDS-~q z|7P1!yodT`6Q9$k-MF|>^}_&(wUxTbpAsHzY!TyKkznA=tB*`@C!O2F8W@f^5hQ?E zRm}IYV;_>T^`~Qd^Lak0WQ;b0EJ<=T{TZ@w(!?oo`YIFyL_xEcZ_g1@0d)`0LI##& zkZDiEuFDx{&5SwTe!njp!I!kkB7Sr%XnN|U8fRnq94#77>?$;N`&W^*czqC%uVnRM z5+}3@zQS~yH@38=2`d3tr2sYXNPzu~^Hvo82iT$XJ=CY0KobT000000Ju|C9VP$(0AYaSOB$`Hp}MiCud1!RD=#g?z`V4u zv$(ObudBVuJ}gX*fTjX;AW9(LZBw+Af#~Oh8V2Wr1klq&_A+tWTf7O%)nJVg{VnIN zJ2$I-?;li4clYK9C%rzT$y9(CD(+57tL9^35X?>D^rv9N)uP(t9!NTjO+~rlXRi3T zlvU5;<)$xPkDt~O&ZUzi1DfxIVfjuzI6zC%?dlI9Czb04*?vJ&;Xb7`7bVk&xPeUQ z_u9bmqUIo$cNN8`u2(RraSdR(RCIn21zv272EcL$H3j=3>AOimdj%__lYwpeFpato z$JmtH9$sDY={|!N-yp>>MC!Z;XV*6FRxL5HN6@nKW;=U#WRKnyTF0F>Woz_sfc!Qw z{o+j0fQeV-ib%7p>5>4#vgO73k!Pn}83t8h6|C1LI56Hj#Ys1m<8#NiODTxP2lGjc z?c63ynb&+oO5k5WjP^@47y>h81ABo&_qH*P1e$w_qOBb&0DfC^h5+0_#Yk9wgWP~` z5V_r02=*{dbtg#nmc$fGsFiL%iS#Dh+p1|rDSE6Wy}GG0xgH{{tm;nQ!?RqJzqHY! z6S2%cLR;lv#?* zwRMVfy_s&Sypx`)DfR zF3QQl?mdKvKDWcW9Jw9HoEV=(FTAB zC~to@YtpD9IDRX~QUH@>>cm3?Ui_=Y0?I)blR1HthBp@*8yvUR?v=*?X+Er!D5*Cb6=mop$T4twUfb0aCiHbV771s&C#x%+&=^J+yRJU01_kYW0Z%=4f~Yy zP&L&{wiUyS3eA3|4{notn7d!h1Ver9?itb!9+w{Wf`CUs8Z}5Gr&?1O85O)+#P?zk39ue%Ljf+J^aOSlW8{dZKqtW)M0 zkjVb|HR43*y%l8pFn=o*O;@523%B=$75PXM1ioX)Fk$%=JhoZg{>8bTi30G{oO=q@ z0A4I?6@gPg1Jd`3wUBeH_3AllopiRGuasfiYl(*eiAe3{8gEY}^PwWsCF%?#MKkqU z&->;>0evx6&)9Fgme;)s+-xb7VMljVxB~FTv{KAWGCgxbJmg7-h=Kt3<)Kqu$QY&t z6eHi{kXBcaOI7z=IQi#so~*&7Q^JNoDx_c^XC_Hq>fMFi;p+^zrE^g@7Z|BWfxE9_%ZLfDjHejF;~fFAH84k(&%G;&3R_IYBb212r42 z702}M1e@1m{%X=xz1p4A!);7b^mMw4UgO)L8M?f^xzV;a`a+j=s1xK4?^K?*PUXFc zHY=6<7GZ|>IsdTi0`4LHC2@oaUsAKYM{{j<*T*Ey0LH7laMO8wzZ!r-kzMXZTg_mk zAvLZ9Cat&5 zwb-B!D=Tz92pvOG%T0Wu*kO%rkzns92lpmVXOLNkKk?#xQ53|`qfuoLGlVkNRGy+% zFpzP|g~4$fZuxiDAe2Mgn7(?KPAH~X6&d)@RQ_~g1SQ)yOLDj;Hk+xwZ-N#l_Fr1K zH?|yL*Y1vn8=3**p;|@DiCCsy%oSDwOY{vww}Seca*Q2w|Wm;Y|t4<$Erw=O;dytBb0cWz0_-b)s0WRRgwZJ_wzv7`OL5-aYSjt?xdc zG1fy6{WMs+g>5#U<^2bhAy!8v_|1QYDRiV(UgGPR?r^W*Rsov#D^@t{!%Uyu8JTCO zy_T5}W(I-S?2r!&D9c)OdNZsf(a+z-bs~H1b=R<0Zu@H;AsT8X_wK;U8u70|_Ph=m z_0B6b2MOY1Ey8B{^+Ah=#auWQ(R>4J=7NF+X5b4J~39)usIE~(FAC=A0^8?M;~ zkIi_eOihk2HMatq>%&@57za%br!j^2>hqsT(-+3$$JOU}cq-%qF8tw2QJT0c}Fm3ufG^sAPUHzY`xlZ%6Jx@DE!0uQ8WqsMjQE6Hh86IY{W`EwW zefohm1}N8&b3MlWV^(Kv*=*R@Nuth~n2w<1ZZ6%iq-W9Q-jfhgzWePUXm@O9lQ|q8 zQ9iQkwnEWKD2CVs2o-k8SZsy=LXM3OH28Z3B_)5s#+`-Y#M+8aP5>AJDynl!e5nLp z>~n%aBanm+rxe1wZL+a8*t!@t8cx@C)ym4UmzZ(jj!zG=Qm*}tGxV>vO8dQf>@vq> zJAVB*=d-+iZ1ff<0TDasM>O!nEfxiwbx|T}tI&?laMmfYLdxlz0cp7o^r$l-{wnO= zK!IxOpl&VE`m-N7>=bp*TW9Z+AuI4Y*iY@Z5kc`@>`M$lmjO})%XgC&L?Bm; zj9!6WwO;0@+5$j1bgX5Xd58gz)5!i0{^QTB_A<$~F?8w8pFoa5(96l6sy+~Ln)Kcl zMSaw?==`<#EM}2&Fr<%3B#G6C+r5bj{mI0;%4lel)YRBZme_PZ<=v(M6IE!A`z!aP zXLge?teCP6pb_5br7t`(wz)?9VKd|#!Ujjx95yGBrc1_F*d-VZi!XV~v;=^gI4UNqL@fbcEUX5@qyPbUHz~4lZ$Fo6$=zea40 z@v=|d%=Yh<_j5{^ZKz0hZO1EJ+D!%l!;ZyE`ErP;?bz1iOG-JT@u6Bq+2)rL;bLr_ zI_Z@NB@^KfU7GFgH2XWxs17&lou&}XqGvC_{kP4&_SxP6cmHbc(dv|+KEXFfLVzbV z{g6witM@g@Wsj8*zS|j>?;L85EuZ5cSk2X zhBiEp=TkZuA?Z_s1(39;Yr;ILgm*sn?Rk9i=-0fRIT1s6Yl^A=`OUwKNrn)u7|cq= zbv$b`{wL2-Cf`r*C)8W24ZOa0&QcSnW|=|4hBf%ptijVV&yF*wo{M><_!PS}vd=efu&IqRi^%P!BPPM=f%8UaN#Z|2<2V@iSnS;V9bCP|aWA9_hE6O< znlxxkxXA!M+tZYQNdQ2i_=Yam;%!Aevy5)!kS8KEfLovnjcYbF75O`F5Cc4{nF3s2LGzMO}a0HJS zhsfomp7uicy34~Izg#PwI%3~iwXC~KII`+m+p20OaBr+=qy-KOKdTZp0la}%{(bA5T|-k3J4yL0Z7 zz@vJE2VDC6AOlC88Vxm@>+N}{zU@h*OEX=wRnO+P`QB-f{JwcovxKMU$k$Bd(=L-M*uq+r;9x(paP>HEtv2;GUy2bBG7P zp4u3|SO8w^tCffF1_%biiQ=1MV-NC`G+Un*8suTsyq5Ij*d_j%vaH?qMwq;V#vMcbNUuG8mw9?zktfDCPG1 zHuXh88alT3mLZ?mq!gz@zyI48t7!=ZlV_Sing}KEC;1#ZO$TP=QhJr#kY4iSG!li2 zi4hwb6rxKx^XFb4HTN1yigFiQVs;^c#_MOz;uUC; z8b)l5dM$(XwLbJ*XEd=-edrAF@y*J_s2E}%yP-)LYi!l0xDFu=68wNq^!mg-K0GW1 z!tw$_VivzwyeznL1-;g>POLbr6w85-CPS;(xY610DYdOn8!Xw9VV7jqZ0h9XDFVrs^9BYr4hI(w>8|i)?g&BeeuLf8rvlH zp|NfS3;-^aZIp0dG{QJO3`~VWcYws|z-OjD$|&uu`V0s!Em8Yh^}JSswD1 z!#2N`T=!rGH>&f-*CmXZjwjrjbC}kwc*t5e9kzWUL!`FOV`~(e_a?E?xuC_y%v6FT zJ(a=3-U3#8Khv&Z$j70G;xuy=fxVpSTRc{1&YgW6I;?Ho8u_N6SS@2(?`CifeyAM4 zO}k1-%#$bzbzW97AHmk>jEw9A1-!iX6}mpKoil#yYbyhUgOr#@hxdxjND(qxot+OF zNMhBhiLNTASC!fFJ%!gb=e9Kd-H5qNCCoY>(Vpp9WuU9{*<@dfl3>(MZEkOL5IIkG z=P1%3lG=TOc(-7U^@`o0bhlIDMA&ksM(3i>{raFT2?Xu&U_shD!HQV0R5e>S}=PexLD}b-XL=CA4gy${PmhO);pc z=u-hOVHsvCf^h)$sUtl^X{sQH8g5-g5XtF1N5^O7yW+CnF*cve0OWA5Do@3-LP=3D zW*Oi5^|Ljq^0uAllOzSR?)&NI@6N8uI(o+Zgcf)&mhxTOX*2Ve15=Dt2YmW)vhdsZ z67yW>=EatK9umCFZEY|b#u(r$EwrXHM`Fgi?#_I}#}e5Q#gitU=g*ItrJ$ZQ?!4H> zk|0T^jt0GCl9%;9HbK-#Z&r|VNSQQU5I}Re!s;OG)}C+yU2pH60{r|km{tLnjUBZ@ z1d5pePiJRS00eh34FCWD006jCRUIb)000J)WbayB_OV-fnc2q-_{d? z)bv){pfyT}Uv4{U*uQqjNA{kLM{QcS=fVlv`%9BeAScjTzzzw8_sR3qsWO(b@`{r! z+T=A-oy@CNR<;8i?lg2Yp0_vIwS6JTZGF`9$5lDaWMy}qD!2~ELr;VGv`gSv8tCMF ziJgszGdJr2WAwY)>Y6m*aO#7gU0_W8oocm!6JbZwv|5Og+ zg~?igIfOKXdQ7nt06z6`F%GJ_r7%zmu?_BH4D#|XNn>c4?UNQx;~MyVCoxswuf$#B zx%B`FNkwqYj~0GRY=wtb3z5`R4!oN((Ow!N!0HUKuT1p-11H;%co{Q>DMh1cAQ2PB zxGk0)Q^I`J8_kQd|*WGURp)Wjbk~@`}4@(B*pQdOSe8 z(jmkQ#9S@j*ZO-B7K?;FiP$fmIm4I3RZ-K|y z%c6}fDXMx} zoKpRnkP9SVX7nxQ|5xR4yOnn1?5mAMEG9i6908XCrnoXTgm)vKc|U*=91jkFqUN8T zv;}6<>?u)t5gPTB5l`3D#h=R30tb=w5~f5@g54Q{gIgiDTH}Gf3{AZ5PE-P3T&yF5 z<_%=b2@BpGosbcwSerzWPIGqHrm;_?;k(9inXfFnR) zd}z^RYV!D9*xcb+Hhv^hZ|KrK4-*?GA$(1TTz5Wc)!lGuc5zhbsSzOFOCl4=h@_{b zOoVKH03bEE8wQU6UhHcHLK6-o9G9=7b0InxowK$OG;Dhb-OH56LBdX`orUC0b`$gv zMD=Q8KTC4>s(%qVYL>T?3aUif!L%a-kwt`}b2nuZ^xS*hREQFVKe-VY3U`b3T!X5a zDl}`xR+XEm+EbldFKwbFcnsIzA&4HPsooYM>9mag|O1(5WG?&JLbCj0FPKfE=uol)R2RY3oMoo>4=v7c_t< zPw+!^>_ejed|k~IWcnlBGsYrQL#;lv1e9-q0#1lv^&Z>cPiN^aX{3oK(Toy|kBK*! zd!3^hX*qv5`qXtzs0&=zYk-1%WU7)^O44d%(%`BLQ?gzv@VZ-Dk7YF!Y^c{=xr$ws zps`kEkiY{pmv0ZFwxHr|5Bn+7UM9+~r1OAcsgQCYIX+D620^0%Er95$_uU9)J2$rH zj)Xp~SFI|Fog$rvaC~mZxBU{%!yhx*Z_wVnc{|RT+Hu&b+wmTsu1UWqc9b#uEYkJ6 z7#-&Ep{~cq&lS}(N=WdA<{_?LcIAJoKNXtX#hmSO;YYRbf`OQ}e~h-)I+$^r)O0oky-+e|b;H-c|igt@SXG*OP#1kavs8hjF90Q${>u-{yGR2ViTxuFa^ z=rGwz9e`2*ejMx(LX!myAboOPsBIpc8P<~IVTYmf!a4?&;Lm%W!B%yZ)4tE#TKq9> z51n>N}e*VBr* zcEo-|y==mZoFRghkFkK=_b&|_gr1*)`(wwOueD+s9vnh*TB7P|N0*Oba%?n#T>`Z; z?0YPpDgPR4M#>j)TV(*~GpUQz1pdq$7m5s55F6_x#h=nV)|>`KIn_s~US3?0mF`=qSqDN_~wV9&UN9dQL?_^_yq%(B1G`Qsni0 z4L$35^K2L}96X=u&S5iLICHi>EW5Wke|mA6Z3*4(pp6~ycLiFbeJEAQCo`{nJepGY z>L?qUTPPK?sL`Bc^Be(9$K81`6}RqF?E&pZ6(iU?nv1ul(Z9ImxoRt@0x8{K8>qzv z_5hx|D~-Vn0fcJ1NDgmf9kkZcC|aANK^*pptYJegQ%=1XJME=~%|5fK$=yXg|3q8+ z9ZqT$gcb@PfiT%I!aFq4dKuzrI_2is40pK~!?zxJZsQ4qQ}H9)fPPxx68J65WvASo z>VvMQNOQ+2;UBOnk)}Rvx*-rOvXxZOmhQ0QUD+%P~Ljt`G((MO9Zs-PYBY2QYC z_jccrboCCPB4EfuAA{-u;eG2vCVTJIz#r%5gd`YbgNcSD<9Gd~j-G(SM0eXPbYliy z-0KqtL;^?DGzK?5V!j-b{Q5q4 z)kXXe*Knmmp=&O`4n6zRGfLZA&z-$h+t2M-x5_eaXWoxhDnBpBHD-~ep4{%%L(7{h z&j5(B>hv?`u~IrPdi~LxS6(UY3veA^FJOjfGgv*eq)I85BbK?GRFsr|PUnJ}N6#sV zV1>F_KCR$6em&kQq06DCeJ-Z?_I4uwC4PMDGUq{bBUkTNZbt#^72&Y%J@2gmE^)K@qUZGxc-sdYN^U+9|N#1P!*RPMP{a?@b-x%6` zPkY!g$Ji@*etqujX_(_?KK4+ Date: Thu, 3 Sep 2020 14:23:00 +0200 Subject: [PATCH 24/41] mehr Deutsch --- locale/de/LC_MESSAGES/django.po | 432 ++++++++++++++++++-------------- 1 file changed, 238 insertions(+), 194 deletions(-) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 9a8e4ad3..7ceefe42 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-09-02 23:17+0200\n" -"PO-Revision-Date: 2020-09-03 00:02+0200\n" +"PO-Revision-Date: 2020-09-03 11:40+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: de\n" @@ -68,7 +68,7 @@ msgstr "Einträge verwalten" #: apps/activity/models.py:33 msgid "Enable the support of entries for this activity." -msgstr "" +msgstr "Aktivieren Sie die Eintrittmöglichkeit für diese Aktivität." #: apps/activity/models.py:38 #: apps/activity/templates/activity/includes/activity_info.html:42 @@ -486,7 +486,7 @@ msgstr "Bericht Frequenz" #: apps/member/forms.py:43 msgid "Last report date" -msgstr "Letzen Bericht Datum." +msgstr "Letzen Bericht Datum" #: apps/member/forms.py:48 msgid "You can't register to the note if you come from the future." @@ -656,7 +656,7 @@ msgstr "bezahlt" #: apps/member/models.py:91 msgid "Tells if the user receive a salary." -msgstr " User ist bezahlt" +msgstr "User ist bezahlt." #: apps/member/models.py:101 apps/treasury/tables.py:146 msgid "No" @@ -1290,7 +1290,7 @@ msgstr "Schauen" #: apps/note/models/transactions.py:77 msgid "highlighted" -msgstr "" +msgstr "hervorgehoben" #: apps/note/models/transactions.py:87 msgid "transaction template" @@ -1425,13 +1425,13 @@ msgstr "Bearbeiten" #: apps/note/templates/note/conso_form.html:32 msgid "Consum" -msgstr "konsumieren" +msgstr "Konsumieren" #: apps/note/templates/note/conso_form.html:43 #: apps/note/templates/note/transaction_form.html:65 #: apps/note/templates/note/transaction_form.html:92 msgid "Name or alias..." -msgstr "Name oder Alias" +msgstr "Name oder Alias..." #: apps/note/templates/note/conso_form.html:52 msgid "Select consumptions" @@ -1456,7 +1456,7 @@ msgstr "Doppelte Modus" #: apps/note/templates/note/conso_form.html:154 #: apps/note/templates/note/transaction_form.html:159 msgid "Recent transactions history" -msgstr " Verlauf der letzten Transaktionen" +msgstr "Verlauf der letzten Transaktionen" #: apps/note/templates/note/mails/negative_balance.html:43 #: apps/note/templates/note/mails/negative_balance.txt:25 @@ -1466,7 +1466,7 @@ msgstr " Verlauf der letzten Transaktionen" #: apps/registration/templates/registration/mails/email_validation_email.html:40 #: apps/registration/templates/registration/mails/email_validation_email.txt:16 msgid "Mail generated by the Note Kfet on the" -msgstr " Mail generiert vom Note Kfet auf dem" +msgstr "Mail generiert vom Note Kfet auf dem" #: apps/note/templates/note/transaction_form.html:54 #: apps/note/templates/note/transaction_form.html:174 @@ -1520,7 +1520,7 @@ msgstr "Verlaufene Preisen" #: apps/note/templates/note/transactiontemplate_form.html:27 msgid "Obsolete since" -msgstr "veraltet seit" +msgstr "Veraltet seit" #: apps/note/templates/note/transactiontemplate_form.html:27 msgid "Current price" @@ -1528,7 +1528,7 @@ msgstr "Aktueller Preis" #: apps/note/templates/note/transactiontemplate_list.html:13 msgid "Name of the button..." -msgstr "Name dessen Tatsen" +msgstr "Name dessen Tatse" #: apps/note/templates/note/transactiontemplate_list.html:15 msgid "New button" @@ -1536,15 +1536,15 @@ msgstr "Neue Tatsen" #: apps/note/templates/note/transactiontemplate_list.html:22 msgid "buttons listing " -msgstr "Tatsenliste" +msgstr "Tatsenliste " #: apps/note/templates/note/transactiontemplate_list.html:73 msgid "button successfully deleted " -msgstr "Taste erfolgreich gelöscht" +msgstr "Taste erfolgreich gelöscht " #: apps/note/templates/note/transactiontemplate_list.html:77 msgid "Unable to delete button " -msgstr "Tatse kann nicht gelöscht werden" +msgstr "Tatse kann nicht gelöscht werden " #: apps/note/views.py:36 msgid "Transfer money" @@ -1572,84 +1572,86 @@ msgstr "Sie können keine Taste sehen." #: apps/note/views.py:200 msgid "Search transactions" -msgstr "" +msgstr "Transaktion finden" #: apps/permission/models.py:89 #, python-brace-format msgid "Can {type} {model}.{field} in {query}" -msgstr "" +msgstr "Kann {type} {model}.{field} in {query}" #: apps/permission/models.py:91 #, python-brace-format msgid "Can {type} {model} in {query}" -msgstr "" +msgstr "Kann {type} {model} in {query}" #: apps/permission/models.py:104 msgid "rank" -msgstr "" +msgstr "Rank" #: apps/permission/models.py:117 msgid "permission mask" -msgstr "" +msgstr "Berechtigungsmaske" #: apps/permission/models.py:118 msgid "permission masks" -msgstr "" +msgstr "Berechtigungsmasken" #: apps/permission/models.py:124 msgid "add" -msgstr "" +msgstr "hinzufügen" #: apps/permission/models.py:125 msgid "view" -msgstr "" +msgstr "Schauen" #: apps/permission/models.py:126 msgid "change" -msgstr "" +msgstr "bearbeiten" #: apps/permission/models.py:158 msgid "query" -msgstr "" +msgstr "Abfrage" #: apps/permission/models.py:171 msgid "mask" -msgstr "" +msgstr "Maske" #: apps/permission/models.py:177 msgid "field" -msgstr "" +msgstr "Feld" #: apps/permission/models.py:182 msgid "" "Tells if the permission should be granted even if the membership of the user " "is expired." msgstr "" +"Gibt an, ob die Berechtigung auch erteilt werden soll, wenn die " +"Mitgliedschaft des Benutzers abgelaufen ist." #: apps/permission/models.py:183 #: apps/permission/templates/permission/all_rights.html:89 msgid "permanent" -msgstr "" +msgstr "permanent" #: apps/permission/models.py:194 msgid "permission" -msgstr "" +msgstr "Berechtigung" #: apps/permission/models.py:195 apps/permission/models.py:334 msgid "permissions" -msgstr "" +msgstr "Berechtigungen" #: apps/permission/models.py:200 msgid "Specifying field applies only to view and change permission types." -msgstr "" +msgstr "Angabefeld gilt nur zum Anzeigen und Ändern von Berechtigungstypen." #: apps/permission/models.py:339 msgid "for club" -msgstr "" +msgstr "Für Club" #: apps/permission/models.py:349 apps/permission/models.py:350 msgid "role permissions" -msgstr "" +msgstr "Berechtigung Rollen" #: apps/permission/signals.py:63 #, python-brace-format @@ -1657,6 +1659,8 @@ msgid "" "You don't have the permission to change the field {field} on this instance " "of model {app_label}.{model_name}." msgstr "" +"Sie haben nicht die Berechtigung, das Feld {field} in dieser Instanz von " +"Modell {app_Label} zu ändern. {model_name}" #: apps/permission/signals.py:73 apps/permission/views.py:89 #, python-brace-format @@ -1664,6 +1668,8 @@ msgid "" "You don't have the permission to add an instance of model {app_label}." "{model_name}." msgstr "" +"Sie haben nicht die Berechtigung, eine Instanz von model {app_label}. " +"{model_name} hinzufügen." #: apps/permission/signals.py:101 #, python-brace-format @@ -1671,46 +1677,48 @@ msgid "" "You don't have the permission to delete this instance of model {app_label}." "{model_name}." msgstr "" +"Sie haben nicht die Berechtigung, eine Instanz von model {app_label}. " +"{model_name} zulöschen." #: apps/permission/templates/permission/all_rights.html:12 msgid "Users that have surnormal rights" -msgstr "" +msgstr "User die Oberberechtigung haben" #: apps/permission/templates/permission/all_rights.html:16 msgid "Superusers have all rights on everything, to manage the website." -msgstr "" +msgstr "Superuser haben alle Berechtigung, um das Website zu handeln." #: apps/permission/templates/permission/all_rights.html:21 msgid "Superusers" -msgstr "" +msgstr "Superusers" #: apps/permission/templates/permission/all_rights.html:45 msgid "Roles description" -msgstr "" +msgstr "Rolle Beschreibung" #: apps/permission/templates/permission/all_rights.html:52 msgid "Filter with roles that I have in at least one club" -msgstr "" +msgstr "Filtern Sie nach Rollen, die ich in mindestens einem Club habe" #: apps/permission/templates/permission/all_rights.html:69 msgid "Owned" -msgstr "" +msgstr "Besetzt" #: apps/permission/templates/permission/all_rights.html:80 msgid "Own this role in the clubs" -msgstr "" +msgstr "Besitze diese Rolle in den Clubs" #: apps/permission/templates/permission/all_rights.html:86 msgid "Mask:" -msgstr "" +msgstr "Mask:" #: apps/permission/templates/permission/all_rights.html:86 msgid "Query:" -msgstr "" +msgstr "Abfrage:" #: apps/permission/templates/permission/all_rights.html:92 msgid "No associated permission" -msgstr "" +msgstr "Keine zugehörige Berechtigung" #: apps/permission/views.py:56 #, python-brace-format @@ -1718,6 +1726,9 @@ msgid "" "You don't have the permission to update this instance of the model " "\"{model}\" with these parameters. Please correct your data and retry." msgstr "" +"Sie haben nicht die Berechtigung, diese Instanz des Modells \"{model}\" mit " +"diesen Parametern zu aktualisieren. Bitte korrigieren Sie Ihre Daten und " +"versuchen Sie es erneut." #: apps/permission/views.py:60 #, python-brace-format @@ -1725,99 +1736,113 @@ msgid "" "You don't have the permission to create an instance of the model \"{model}\" " "with these parameters. Please correct your data and retry." msgstr "" +"Sie haben nicht die Berechtigung, eine Instanz des Modells \"{model}\" mit " +"diesen Parametern zu erstellen. Bitte korrigieren Sie Ihre Daten und " +"versuchen Sie es erneut." #: apps/permission/views.py:96 note_kfet/templates/base.html:104 msgid "Rights" -msgstr "" +msgstr "Rechten" #: apps/permission/views.py:101 msgid "All rights" -msgstr "" +msgstr "Alle Rechten" #: apps/registration/apps.py:10 msgid "registration" -msgstr "" +msgstr "Anmeldung" #: apps/registration/forms.py:39 msgid "This email address is already used." -msgstr "" +msgstr "Diese email adresse ist schon benutzt." #: apps/registration/forms.py:49 msgid "Register to the WEI" -msgstr "" +msgstr "zu WEI anmelden" #: apps/registration/forms.py:51 msgid "" "Check this case if you want to register to the WEI. If you hesitate, you " "will be able to register later, after validating your account in the Kfet." msgstr "" +"Überprüfen Sie diesen Fall, wenn Sie sich beim WEI registrieren möchten. " +"falls Zweifel, können Sie sich später nach Bestätigung Ihres Kontos im Kfet " +"registrieren." #: apps/registration/forms.py:96 msgid "Join BDE Club" -msgstr "" +msgstr "BDE Mitglieder werden" #: apps/registration/forms.py:103 msgid "Join Kfet Club" -msgstr "" +msgstr "Kfet Mitglieder werden" #: apps/registration/templates/registration/email_validation_complete.html:9 msgid "Your email have successfully been validated." -msgstr "" +msgstr "Ihre E-Mail wurde erfolgreich validiert." #: apps/registration/templates/registration/email_validation_complete.html:11 #, python-format msgid "You can now log in." -msgstr "" +msgstr "Sie können sich jetz anmelden ." #: apps/registration/templates/registration/email_validation_complete.html:13 msgid "" "You must pay now your membership in the Kfet to complete your registration." msgstr "" +"Sie müssen jetzt Ihre Mitgliedschaft im Kfet bezahlen, um Ihre Registrierung " +"abzuschließen." #: apps/registration/templates/registration/email_validation_complete.html:16 msgid "" "The link was invalid. The token may have expired. Please send us an email to " "activate your account." msgstr "" +"Der Link war ungültig. Das Token ist möglicherweise abgelaufen. Bitte senden " +"Sie uns eine E-Mail, um Ihr Konto zu aktivieren." #: apps/registration/templates/registration/email_validation_email_sent.html:8 msgid "Account activation" -msgstr "" +msgstr "Kontoaktivierung" #: apps/registration/templates/registration/email_validation_email_sent.html:11 msgid "" "An email has been sent. Please click on the link to activate your account." msgstr "" +"Eine E-Mail wurde gesendet. Bitte klicken Sie auf den Link, um Ihr Konto zu " +"aktivieren." #: apps/registration/templates/registration/email_validation_email_sent.html:15 msgid "" "You must also go to the Kfet to pay your membership. The WEI registration " "includes the BDE membership." msgstr "" +"Sie müssen auch zum Kfet gehen, um Ihre Mitgliedschaft zu bezahlen. Die WEI-" +"Registrierung beinhaltet die BDE-Mitgliedschaft." #: apps/registration/templates/registration/future_profile_detail.html:49 #: apps/wei/templates/wei/weiregistration_confirm_delete.html:11 msgid "Delete registration" -msgstr "" +msgstr "Registrierung löschen" #: apps/registration/templates/registration/future_profile_detail.html:57 msgid "Validate account" -msgstr "" +msgstr "Konto validieren" #: apps/registration/templates/registration/future_profile_detail.html:64 #: apps/wei/templates/wei/weimembership_form.html:127 #: apps/wei/templates/wei/weimembership_form.html:186 msgid "Validate registration" -msgstr "" +msgstr "Registrierung validieren" #: apps/registration/templates/registration/future_user_list.html:9 msgid "New user" -msgstr "" +msgstr "Neue User" #: apps/registration/templates/registration/mails/email_validation_email.html:12 #: apps/registration/templates/registration/mails/email_validation_email.txt:3 msgid "Hi" -msgstr "" +msgstr "Hallo" #: apps/registration/templates/registration/mails/email_validation_email.html:16 #: apps/registration/templates/registration/mails/email_validation_email.txt:5 @@ -1825,6 +1850,8 @@ msgid "" "You recently registered on the Note Kfet. Please click on the link below to " "confirm your registration." msgstr "" +"Sie haben sich kürzlich beim Note Kfet registriert. Bitte klicken Sie auf " +"den Link unten, um Ihre Registrierung zu bestätigen." #: apps/registration/templates/registration/mails/email_validation_email.html:26 #: apps/registration/templates/registration/mails/email_validation_email.txt:9 @@ -1832,6 +1859,8 @@ msgid "" "This link is only valid for a couple of days, after that you will need to " "contact us to validate your email." msgstr "" +"Dieser Link ist nur einige Tage gültig. Danach müssen Sie uns kontaktieren, " +"um Ihre E-Mail zu bestätigen." #: apps/registration/templates/registration/mails/email_validation_email.html:30 #: apps/registration/templates/registration/mails/email_validation_email.txt:11 @@ -1840,69 +1869,75 @@ msgid "" "you can log in. You will need to pay your membership in the Kfet. Note that " "the WEI registration includes the Kfet membership." msgstr "" +"Danach müssen Sie warten, bis jemand Ihr Konto validiert, bevor Sie sich " +"anmelden können. Sie müssen Ihre Mitgliedschaft im Kfet bezahlen. Beachten " +"Sie, dass die WEI-Registrierung die Kfet-Mitgliedschaft enthält." #: apps/registration/templates/registration/mails/email_validation_email.html:34 #: apps/registration/templates/registration/mails/email_validation_email.txt:13 msgid "Thanks" -msgstr "" +msgstr "Danke" #: apps/registration/templates/registration/mails/email_validation_email.html:39 #: apps/registration/templates/registration/mails/email_validation_email.txt:15 msgid "The Note Kfet team." -msgstr "" +msgstr "Die NoteKfet Team" #: apps/registration/views.py:38 msgid "Register new user" -msgstr "" +msgstr "Neuen User registrieren" #: apps/registration/views.py:82 msgid "Email validation" -msgstr "" +msgstr "Email validierung" #: apps/registration/views.py:84 msgid "Validate email" -msgstr "" +msgstr "Email validieren" #: apps/registration/views.py:126 msgid "Email validation unsuccessful" -msgstr "" +msgstr "Email validierung unerfolgreich" #: apps/registration/views.py:137 msgid "Email validation email sent" -msgstr "" +msgstr "Validierungsemail wurde gesendet" #: apps/registration/views.py:145 msgid "Resend email validation link" -msgstr "" +msgstr "E-Mail-Validierungslink erneut senden" #: apps/registration/views.py:163 msgid "Pre-registered users list" -msgstr "" +msgstr "Vorregistrierte Userliste," #: apps/registration/views.py:190 msgid "Unregistered users" -msgstr "" +msgstr "Unregistrierte Users" #: apps/registration/views.py:203 msgid "Registration detail" -msgstr "" +msgstr "Registrierung Detailen" #: apps/registration/views.py:258 msgid "You must join the BDE." -msgstr "" +msgstr "Sie müssen die BDE beitreten." #: apps/registration/views.py:280 msgid "You must join BDE club before joining Kfet club." msgstr "" +"Sie müssen dem BDE-Club beitreten, bevor Sie dem Kfet-Club beitreten können." #: apps/registration/views.py:285 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" +"Der eingegebene Betrag reicht für die Mitgliedschaft nicht aus, sollte " +"mindestens {} betragen" #: apps/registration/views.py:360 msgid "Invalidate pre-registration" -msgstr "" +msgstr "Ungültige Vorregistrierung" #: apps/treasury/apps.py:12 note_kfet/templates/base.html:92 msgid "Treasury" @@ -1934,228 +1969,235 @@ msgstr "" #: apps/treasury/models.py:37 msgid "BDE" -msgstr "" +msgstr "BDE" #: apps/treasury/models.py:42 msgid "Object" -msgstr "" +msgstr "Objekt" #: apps/treasury/models.py:46 msgid "Description" -msgstr "" +msgstr "Beschreibung" #: apps/treasury/models.py:55 msgid "Address" -msgstr "" +msgstr "Adresse" #: apps/treasury/models.py:60 apps/treasury/models.py:180 msgid "Date" -msgstr "" +msgstr "Datum" #: apps/treasury/models.py:64 msgid "Acquitted" -msgstr "" +msgstr "bezahlt" #: apps/treasury/models.py:69 msgid "Locked" -msgstr "" +msgstr "gesperrt" #: apps/treasury/models.py:70 msgid "An invoice can't be edited when it is locked." -msgstr "" +msgstr "Eine Rechnung kann nicht bearbeitet werden, wenn sie gesperrt ist." #: apps/treasury/models.py:76 msgid "tex source" -msgstr "" +msgstr "Tex Quelle" #: apps/treasury/models.py:89 #: apps/treasury/templates/treasury/invoice_form.html:22 msgid "This invoice is locked and can no longer be edited." -msgstr "" +msgstr "Diese Rechnung ist gesperrt und kann nicht mehr bearbeitet werden." #: apps/treasury/models.py:109 apps/treasury/models.py:122 msgid "invoice" -msgstr "" +msgstr "Rechnung" #: apps/treasury/models.py:110 msgid "invoices" -msgstr "" +msgstr "Rechnungen" #: apps/treasury/models.py:127 msgid "Designation" -msgstr "" +msgstr "Bezeichnung" #: apps/treasury/models.py:131 msgid "Quantity" -msgstr "" +msgstr "Qualität" #: apps/treasury/models.py:135 msgid "Unit price" -msgstr "" +msgstr "Einzelpreis" #: apps/treasury/models.py:151 msgid "product" -msgstr "" +msgstr "Produkt" #: apps/treasury/models.py:152 msgid "products" -msgstr "" +msgstr "Produkten" #: apps/treasury/models.py:169 msgid "remittance type" -msgstr "" +msgstr "Überweisungstyp" #: apps/treasury/models.py:170 msgid "remittance types" -msgstr "" +msgstr "Überweisungstypen" #: apps/treasury/models.py:191 msgid "Comment" -msgstr "" +msgstr "Kommentar" #: apps/treasury/models.py:196 msgid "Closed" -msgstr "" +msgstr "Geschlossen" #: apps/treasury/models.py:200 msgid "remittance" -msgstr "" +msgstr "Überweisung" #: apps/treasury/models.py:201 msgid "remittances" -msgstr "" +msgstr "Überweisungen" #: apps/treasury/models.py:233 msgid "Remittance #{:d}: {}" -msgstr "" +msgstr "Überweisung #{:d}:{}" #: apps/treasury/models.py:256 msgid "special transaction proxy" -msgstr "" +msgstr "spezielle Transaktion Proxy" #: apps/treasury/models.py:257 msgid "special transaction proxies" -msgstr "" +msgstr "spezielle Transaktion Proxies" #: apps/treasury/models.py:279 msgid "credit transaction" -msgstr "" +msgstr "Kredit Transaktion" #: apps/treasury/models.py:343 msgid "" "This user doesn't have enough money to pay the memberships with its note. " "Please ask her/him to credit the note before invalidating this credit." msgstr "" +"Dieser Benutzer hat nicht genug Geld, um die Mitgliedschaften mit seiner " +"Note zu bezahlen." #: apps/treasury/models.py:355 #: apps/treasury/templates/treasury/sogecredit_detail.html:10 msgid "Credit from the Société générale" -msgstr "" +msgstr "Kredit von der Société générale" #: apps/treasury/models.py:356 msgid "Credits from the Société générale" -msgstr "" +msgstr "Krediten von der Société générale" #: apps/treasury/tables.py:20 msgid "Invoice #{:d}" -msgstr "" +msgstr "Rechnung #{:d}" #: apps/treasury/tables.py:25 #: apps/treasury/templates/treasury/invoice_list.html:13 #: apps/treasury/templates/treasury/remittance_list.html:13 #: apps/treasury/templates/treasury/sogecredit_list.html:13 msgid "Invoice" -msgstr "" +msgstr "Rechnung" #: apps/treasury/tables.py:65 msgid "Transaction count" -msgstr "" +msgstr "Transaktionanzahl" #: apps/treasury/tables.py:70 apps/treasury/tables.py:72 msgid "View" -msgstr "" +msgstr "Schauen" #: apps/treasury/tables.py:146 msgid "Yes" -msgstr "" +msgstr "Ja" #: apps/treasury/templates/treasury/invoice_confirm_delete.html:10 #: apps/treasury/views.py:166 msgid "Delete invoice" -msgstr "" +msgstr "Rechnung löschen" #: apps/treasury/templates/treasury/invoice_confirm_delete.html:15 msgid "This invoice is locked and can't be deleted." -msgstr "" +msgstr "Eine Rechnung kann nicht gelöscht werden, wenn sie gesperrt ist." #: apps/treasury/templates/treasury/invoice_confirm_delete.html:21 msgid "" "Are you sure you want to delete this invoice? This action can't be undone." msgstr "" +"Möchten Sie diese Rechnung wirklich löschen? Diese Aktion kann nicht " +"rückgängig gemacht werden." #: apps/treasury/templates/treasury/invoice_confirm_delete.html:28 msgid "Return to invoices list" -msgstr "" +msgstr "Zurück zur Rechnungsliste" #: apps/treasury/templates/treasury/invoice_form.html:15 msgid "" "Warning: the LaTeX template is saved with this object. Updating the invoice " "implies regenerate it. Be careful if you manipulate old invoices." msgstr "" +"Warnung: Die LaTeX-Vorlage wird mit diesem Objekt gespeichert. Wenn Sie die " +"Rechnung aktualisieren, müssen Sie sie neu generieren. Seien Sie vorsichtig, " +"wenn Sie alte Rechnungen bearbeiten." #: apps/treasury/templates/treasury/invoice_form.html:69 msgid "Add product" -msgstr "" +msgstr "Produkt hinzufügen" #: apps/treasury/templates/treasury/invoice_form.html:70 msgid "Remove product" -msgstr "" +msgstr "Produkt entfernen" #: apps/treasury/templates/treasury/invoice_list.html:19 #: apps/treasury/templates/treasury/remittance_list.html:19 #: apps/treasury/templates/treasury/sogecredit_list.html:19 msgid "Société générale credits" -msgstr "" +msgstr "Krediten von der Société générale" #: apps/treasury/templates/treasury/invoice_list.html:31 msgid "New invoice" -msgstr "" +msgstr "Neue Rechnung" #: apps/treasury/templates/treasury/remittance_form.html:12 msgid "Remittance #" -msgstr "" +msgstr "Überweisung #" #: apps/treasury/templates/treasury/remittance_form.html:17 msgid "Count" -msgstr "" +msgstr "Anzahl" #: apps/treasury/templates/treasury/remittance_form.html:35 msgid "Linked transactions" -msgstr "" +msgstr "Transaktionen" #: apps/treasury/templates/treasury/remittance_form.html:42 msgid "There is no transaction linked with this remittance." -msgstr "" +msgstr "Mit dieser Überweisung ist keine Transaktion verbunden" #: apps/treasury/templates/treasury/remittance_list.html:27 msgid "Opened remittances" -msgstr "" +msgstr "Geöffnete Überweisungen" #: apps/treasury/templates/treasury/remittance_list.html:34 msgid "There is no opened remittance." -msgstr "" +msgstr "Es gibt keine offene Überweisung" #: apps/treasury/templates/treasury/remittance_list.html:39 msgid "New remittance" -msgstr "" +msgstr "Neue Überweisung" #: apps/treasury/templates/treasury/remittance_list.html:45 msgid "Transfers without remittances" -msgstr "" +msgstr "Transfer ohne Überweisungen" #: apps/treasury/templates/treasury/remittance_list.html:52 msgid "There is no transaction without any linked remittance." -msgstr "" +msgstr "Mit dieser Überweisung ist keine Transaktion verbunden" #: apps/treasury/templates/treasury/remittance_list.html:60 msgid "Transfers with opened remittances" @@ -2317,15 +2359,15 @@ msgstr "" #: apps/wei/models.py:24 apps/wei/templates/wei/base.html:36 msgid "year" -msgstr "" +msgstr "Jahr" #: apps/wei/models.py:28 apps/wei/templates/wei/base.html:30 msgid "date start" -msgstr "" +msgstr "Anfangsdatum" #: apps/wei/models.py:32 apps/wei/templates/wei/base.html:33 msgid "date end" -msgstr "" +msgstr "Abschlussdatum" #: apps/wei/models.py:77 msgid "survey information" @@ -2337,35 +2379,35 @@ msgstr "" #: apps/wei/models.py:100 msgid "Bus" -msgstr "" +msgstr "Bus" #: apps/wei/models.py:101 apps/wei/templates/wei/weiclub_detail.html:51 msgid "Buses" -msgstr "" +msgstr "Buses" #: apps/wei/models.py:122 msgid "color" -msgstr "" +msgstr "Farbe" #: apps/wei/models.py:123 msgid "The color of the T-Shirt, stored with its number equivalent" -msgstr "" +msgstr "Die Farbe des T-Shirts, gespeichert mit der entsprechenden Nummer" #: apps/wei/models.py:137 msgid "Bus team" -msgstr "" +msgstr "Bus Team" #: apps/wei/models.py:138 msgid "Bus teams" -msgstr "" +msgstr "Bus Teams" #: apps/wei/models.py:147 msgid "WEI Role" -msgstr "" +msgstr "WEI Rolle" #: apps/wei/models.py:172 msgid "Credit from Société générale" -msgstr "" +msgstr "Kredit von der Société générale" #: apps/wei/models.py:177 msgid "Caution check given" @@ -2373,200 +2415,202 @@ msgstr "" #: apps/wei/models.py:181 apps/wei/templates/wei/weimembership_form.html:64 msgid "birth date" -msgstr "" +msgstr "Geburtsdatum" #: apps/wei/models.py:187 apps/wei/models.py:197 msgid "Male" -msgstr "" +msgstr "Männlich" #: apps/wei/models.py:188 apps/wei/models.py:198 msgid "Female" -msgstr "" +msgstr "Weiblich" #: apps/wei/models.py:189 msgid "Non binary" -msgstr "" +msgstr "nicht binär" #: apps/wei/models.py:191 apps/wei/templates/wei/weimembership_form.html:55 msgid "gender" -msgstr "" +msgstr "Geschlecht" #: apps/wei/models.py:200 apps/wei/templates/wei/weimembership_form.html:58 msgid "clothing cut" -msgstr "" +msgstr "Kleidung Schnitt" #: apps/wei/models.py:213 apps/wei/templates/wei/weimembership_form.html:61 msgid "clothing size" -msgstr "" +msgstr "Kleidergröße" #: apps/wei/models.py:219 apps/wei/templates/wei/weimembership_form.html:67 msgid "health issues" -msgstr "" +msgstr "Gesundheitsprobleme" #: apps/wei/models.py:224 apps/wei/templates/wei/weimembership_form.html:70 msgid "emergency contact name" -msgstr "" +msgstr "Notfall-Kontakt" #: apps/wei/models.py:229 apps/wei/templates/wei/weimembership_form.html:73 msgid "emergency contact phone" -msgstr "" +msgstr "Notfallkontakttelefon" #: apps/wei/models.py:234 apps/wei/templates/wei/weimembership_form.html:52 msgid "first year" -msgstr "" +msgstr "Erste Jahr" #: apps/wei/models.py:235 msgid "Tells if the user is new in the school." -msgstr "" +msgstr "Gibt an, ob der USer neu in der Schule ist." #: apps/wei/models.py:240 msgid "registration information" -msgstr "" +msgstr "Registrierung Detailen" #: apps/wei/models.py:241 msgid "" "Information about the registration (buses for old members, survey fot the " "new members), encoded in JSON" msgstr "" +"Informationen zur Registrierung (Busse für alte Mitglieder, Umfrage für neue " +"Mitglieder), verschlüsselt in JSON" #: apps/wei/models.py:272 msgid "WEI User" -msgstr "" +msgstr "WEI User" #: apps/wei/models.py:273 msgid "WEI Users" -msgstr "" +msgstr "WEI Users" #: apps/wei/models.py:293 msgid "team" -msgstr "" +msgstr "Team" #: apps/wei/models.py:303 msgid "WEI registration" -msgstr "" +msgstr "WEI Registrierung" #: apps/wei/models.py:307 msgid "WEI membership" -msgstr "" +msgstr "WEI Mitgliedschaft" #: apps/wei/models.py:308 msgid "WEI memberships" -msgstr "" +msgstr "WEI Mitgliedschaften" #: apps/wei/tables.py:127 msgid "Year" -msgstr "" +msgstr "Jahr" #: apps/wei/tables.py:165 apps/wei/templates/wei/bus_detail.html:32 #: apps/wei/templates/wei/busteam_detail.html:50 msgid "Teams" -msgstr "" +msgstr "Teams" #: apps/wei/tables.py:174 apps/wei/tables.py:215 msgid "Members count" -msgstr "" +msgstr "Anzahl Mitgliedern" #: apps/wei/tables.py:181 apps/wei/tables.py:212 msgid "members" -msgstr "" +msgstr "Mitglieder" #: apps/wei/templates/wei/base.html:44 msgid "WEI fee (paid students)" -msgstr "" +msgstr "WEI Preis (bezahlte Studenten)" #: apps/wei/templates/wei/base.html:47 apps/wei/templates/wei/base.html:54 msgid "The BDE membership is included in the WEI registration." -msgstr "" +msgstr "Die BDE-Mitgliedschaft ist in der WEI-Registrierung enthalten" #: apps/wei/templates/wei/base.html:51 msgid "WEI fee (unpaid students)" -msgstr "" +msgstr "WEI Preis (unbezahlte Studenten)" #: apps/wei/templates/wei/base.html:76 msgid "WEI list" -msgstr "" +msgstr "WEI Liste" #: apps/wei/templates/wei/base.html:81 apps/wei/views.py:506 msgid "Register 1A" -msgstr "" +msgstr "1A Registrieren" #: apps/wei/templates/wei/base.html:85 apps/wei/views.py:573 msgid "Register 2A+" -msgstr "" +msgstr "2A+ Registrieren" #: apps/wei/templates/wei/base.html:93 msgid "Add bus" -msgstr "" +msgstr "Neue Bus" #: apps/wei/templates/wei/base.html:97 msgid "View WEI" -msgstr "" +msgstr "WEI schauen" #: apps/wei/templates/wei/bus_detail.html:22 #: apps/wei/templates/wei/busteam_detail.html:22 msgid "Add team" -msgstr "" +msgstr "Neue Team" #: apps/wei/templates/wei/bus_detail.html:45 msgid "Members" -msgstr "" +msgstr "Mitglied" #: apps/wei/templates/wei/bus_detail.html:54 #: apps/wei/templates/wei/busteam_detail.html:60 #: apps/wei/templates/wei/weimembership_list.html:29 msgid "View as PDF" -msgstr "" +msgstr "Als PDF schauen" #: apps/wei/templates/wei/survey.html:11 #: apps/wei/templates/wei/survey_closed.html:11 #: apps/wei/templates/wei/survey_end.html:11 apps/wei/views.py:978 #: apps/wei/views.py:1032 apps/wei/views.py:1042 msgid "Survey WEI" -msgstr "" +msgstr "WEI Umfrage" #: apps/wei/templates/wei/survey.html:23 msgid "Next" -msgstr "" +msgstr "Weiter" #: apps/wei/templates/wei/survey_closed.html:15 msgid "The inscription for this WEI are now closed." -msgstr "" +msgstr " Die Inschrift für diese WEI ist nun geschlossen." #: apps/wei/templates/wei/survey_closed.html:19 msgid "Return to WEI detail" -msgstr "" +msgstr "Zurück zur WEI Detaillen" #: apps/wei/templates/wei/survey_end.html:15 msgid "The survey is now ended. Your answers have been saved." -msgstr "" +msgstr "Die Umfrage ist nun beendet. Ihre Antworten wurden gespeichert." #: apps/wei/templates/wei/weiclub_detail.html:32 msgid "Register to the WEI! – 1A" -msgstr "" +msgstr "1A Registrieren" #: apps/wei/templates/wei/weiclub_detail.html:36 msgid "Register to the WEI! – 2A+" -msgstr "" +msgstr "2A+ Registrieren" #: apps/wei/templates/wei/weiclub_detail.html:40 msgid "Update my registration" -msgstr "" +msgstr "Meine Registrierung bearbeiten" #: apps/wei/templates/wei/weiclub_detail.html:63 msgid "Members of the WEI" -msgstr "" +msgstr "Mitglied der WEI" #: apps/wei/templates/wei/weiclub_detail.html:89 msgid "Unvalidated registrations" -msgstr "" +msgstr "unvalidierte Registrierungen" #: apps/wei/templates/wei/weiclub_list.html:14 apps/wei/views.py:76 msgid "Create WEI" -msgstr "" +msgstr "Neue WEI" #: apps/wei/templates/wei/weiclub_list.html:22 msgid "WEI listing" -msgstr "" +msgstr "WEI List" #: apps/wei/templates/wei/weimembership_form.html:10 msgid "Review registration" @@ -2691,55 +2735,55 @@ msgstr "" #: apps/wei/views.py:55 msgid "Search WEI" -msgstr "" +msgstr "WEI finden" #: apps/wei/views.py:105 msgid "WEI Detail" -msgstr "" +msgstr "WEI Infos" #: apps/wei/views.py:200 msgid "View members of the WEI" -msgstr "" +msgstr "Mitglied der WEI schauen" #: apps/wei/views.py:228 msgid "Find WEI Membership" -msgstr "" +msgstr "WEI Mitgliedschaft finden" #: apps/wei/views.py:238 msgid "View registrations to the WEI" -msgstr "" +msgstr "Mitglied der WEI schauen" #: apps/wei/views.py:262 msgid "Find WEI Registration" -msgstr "" +msgstr "WEI Registrierung finden" #: apps/wei/views.py:273 msgid "Update the WEI" -msgstr "" +msgstr "WEI bearbeiten" #: apps/wei/views.py:294 msgid "Create new bus" -msgstr "" +msgstr "Neue Bus" #: apps/wei/views.py:332 msgid "Update bus" -msgstr "" +msgstr "Bus bearbeiten" #: apps/wei/views.py:362 msgid "Manage bus" -msgstr "" +msgstr "Bus ändern" #: apps/wei/views.py:389 msgid "Create new team" -msgstr "" +msgstr "Neue Bus Team" #: apps/wei/views.py:429 msgid "Update team" -msgstr "" +msgstr "Team bearbeiten" #: apps/wei/views.py:460 msgid "Manage WEI team" -msgstr "" +msgstr "WEI Team bearbeiten" #: apps/wei/views.py:482 msgid "Register first year student to the WEI" @@ -2849,15 +2893,15 @@ msgstr "" #: note_kfet/templates/base.html:74 msgid "Users" -msgstr "" +msgstr "Users" #: note_kfet/templates/base.html:80 msgid "Clubs" -msgstr "" +msgstr "CLubs" #: note_kfet/templates/base.html:109 msgid "Admin" -msgstr "" +msgstr "Admin" #: note_kfet/templates/base.html:153 msgid "" @@ -2875,7 +2919,7 @@ msgstr "" #: note_kfet/templates/cas_server/base.html:7 msgid "Central Authentication Service" -msgstr "" +msgstr "Central Authentication Service" #: note_kfet/templates/cas_server/base.html:43 #, python-format From f7f6f053f799d43392601823fdbcc77075f965b4 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Thu, 3 Sep 2020 14:33:26 +0200 Subject: [PATCH 25/41] Format date to ISO standard --- apps/note/tables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/note/tables.py b/apps/note/tables.py index 12ec58a9..0ca50306 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -46,7 +46,7 @@ class HistoryTable(tables.Table): } ) - created_at = tables.DateColumn( + created_at = tables.DateTimeColumn(format='Y-m-d H:i:s', attrs={ "td": { "class": "text-nowrap", From 4b85a35a9d4429f8ee35c6328a728a08775f042d Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 3 Sep 2020 16:10:29 +0200 Subject: [PATCH 26/41] Fix double consumptions --- note_kfet/static/js/consos.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/note_kfet/static/js/consos.js b/note_kfet/static/js/consos.js index ec5a0940..25e8113e 100644 --- a/note_kfet/static/js/consos.js +++ b/note_kfet/static/js/consos.js @@ -27,7 +27,7 @@ $(document).ready(function() { }); // Switching in double consumptions mode should update the layout - $("#double_conso").click(function() { + $("#double_conso").change(function() { $("#consos_list_div").removeClass('d-none'); $("#user_select_div").attr('class', 'col-xl-4'); $("#infos_div").attr('class', 'col-sm-5 col-xl-6'); @@ -47,7 +47,7 @@ $(document).ready(function() { } }); - $("#single_conso").click(function() { + $("#single_conso").change(function() { $("#consos_list_div").addClass('d-none'); $("#user_select_div").attr('class', 'col-xl-7'); $("#infos_div").attr('class', 'col-sm-5 col-md-4'); From f02efd3b39f5478d09a77bb176873c3835c5c92c Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 3 Sep 2020 20:03:40 +0200 Subject: [PATCH 27/41] 100% coverage on registration app --- apps/member/models.py | 10 +- apps/member/views.py | 4 +- apps/registration/tests/__init__.py | 0 apps/registration/tests/test_registration.py | 386 +++++++++++++++++++ apps/registration/views.py | 21 +- 5 files changed, 401 insertions(+), 20 deletions(-) create mode 100644 apps/registration/tests/__init__.py create mode 100644 apps/registration/tests/test_registration.py diff --git a/apps/member/models.py b/apps/member/models.py index a1628fae..d1218e94 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -172,19 +172,21 @@ class Profile(models.Model): def send_email_validation_link(self): subject = "[Note Kfet] " + str(_("Activate your Note Kfet account")) + token = email_validation_token.make_token(self.user) + uid = urlsafe_base64_encode(force_bytes(self.user_id)) message = loader.render_to_string('registration/mails/email_validation_email.txt', { 'user': self.user, 'domain': os.getenv("NOTE_URL", "note.example.com"), - 'token': email_validation_token.make_token(self.user), - 'uid': urlsafe_base64_encode(force_bytes(self.user.pk)), + 'token': token, + 'uid': uid, }) html = loader.render_to_string('registration/mails/email_validation_email.html', { 'user': self.user, 'domain': os.getenv("NOTE_URL", "note.example.com"), - 'token': email_validation_token.make_token(self.user), - 'uid': urlsafe_base64_encode(force_bytes(self.user.pk)), + 'token': token, + 'uid': uid, }) self.user.email_user(subject, message, html_message=html) diff --git a/apps/member/views.py b/apps/member/views.py index c2f9f136..4534c9e8 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -140,9 +140,7 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): """ We can't display information of a not registered user. """ - qs = super().get_queryset() - return qs if self.request.user.is_superuser and self.request.session.get("permission_mask", -1) >= 42\ - else qs.filter(profile__registration_valid=True) + return super().get_queryset().filter(profile__registration_valid=True) def get_context_data(self, **kwargs): """ diff --git a/apps/registration/tests/__init__.py b/apps/registration/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/registration/tests/test_registration.py b/apps/registration/tests/test_registration.py new file mode 100644 index 00000000..e2191445 --- /dev/null +++ b/apps/registration/tests/test_registration.py @@ -0,0 +1,386 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.contrib.auth.models import User +from django.db.models import Q +from django.test import TestCase +from django.urls import reverse +from django.utils.encoding import force_bytes +from django.utils.http import urlsafe_base64_encode +from member.models import Club, Membership +from note.models import NoteUser, NoteSpecial, Transaction +from registration.tokens import email_validation_token +from treasury.models import SogeCredit + +""" +Check that pre-registrations and validations are working as well. +""" + + +class TestSignup(TestCase): + """ + Assume we are a new user. + Check that it can pre-register without any problem. + """ + + fixtures = ("initial", ) + + def test_signup(self): + """ + A first year member signs up and validates its email address. + """ + response = self.client.get(reverse("registration:signup")) + self.assertEqual(response.status_code, 200) + + # Signup + response = self.client.post(reverse("registration:signup"), dict( + first_name="Toto", + last_name="TOTO", + username="toto", + email="toto@example.com", + password1="toto1234", + password2="toto1234", + phone_number="+33123456789", + department="EXT", + promotion=Club.objects.get(name="BDE").membership_start.year, + address="Earth", + paid=False, + ml_events_registration="en", + ml_sport_registration=True, + ml_art_registration=True, + )) + self.assertRedirects(response, reverse("registration:email_validation_sent"), 302, 200) + self.assertTrue(User.objects.filter(username="toto").exists()) + user = User.objects.get(username="toto") + # A preregistred user has no note + self.assertFalse(NoteUser.objects.filter(user=user).exists()) + self.assertFalse(user.profile.registration_valid) + self.assertFalse(user.profile.email_confirmed) + self.assertFalse(user.is_active) + + response = self.client.get(reverse("registration:email_validation_sent")) + self.assertEqual(response.status_code, 200) + + # Check that the email validation link is valid + token = email_validation_token.make_token(user) + uid = urlsafe_base64_encode(force_bytes(user.pk)) + response = self.client.get(reverse("registration:email_validation", kwargs=dict(uidb64=uid, token=token))) + self.assertEqual(response.status_code, 200) + user.profile.refresh_from_db() + self.assertTrue(user.profile.email_confirmed) + + # Token has expired + response = self.client.get(reverse("registration:email_validation", kwargs=dict(uidb64=uid, token=token))) + self.assertEqual(response.status_code, 400) + + # Uid does not exist + response = self.client.get(reverse("registration:email_validation", kwargs=dict(uidb64=0, token="toto"))) + self.assertEqual(response.status_code, 400) + + def test_invalid_signup(self): + """ + Send wrong data and check that it is not valid + """ + User.objects.create_superuser( + first_name="Toto", + last_name="TOTO", + username="toto", + email="toto@example.com", + password="toto1234", + ) + + # The email is already used + response = self.client.post(reverse("registration:signup"), dict( + first_name="Toto", + last_name="TOTO", + username="tôtö", + email="toto@example.com", + password1="toto1234", + password2="toto1234", + phone_number="+33123456789", + department="EXT", + promotion=Club.objects.get(name="BDE").membership_start.year, + address="Earth", + paid=False, + ml_events_registration="en", + ml_sport_registration=True, + ml_art_registration=True, + )) + self.assertTrue(response.status_code, 200) + + # The username is similar to a known alias + response = self.client.post(reverse("registration:signup"), dict( + first_name="Toto", + last_name="TOTO", + username="tôtö", + email="othertoto@example.com", + password1="toto1234", + password2="toto1234", + phone_number="+33123456789", + department="EXT", + promotion=Club.objects.get(name="BDE").membership_start.year, + address="Earth", + paid=False, + ml_events_registration="en", + ml_sport_registration=True, + ml_art_registration=True, + )) + self.assertTrue(response.status_code, 200) + + # The phone number is invalid + response = self.client.post(reverse("registration:signup"), dict( + first_name="Toto", + last_name="TOTO", + username="Ihaveanotherusername", + email="othertoto@example.com", + password1="toto1234", + password2="toto1234", + phone_number="invalid phone number", + department="EXT", + promotion=Club.objects.get(name="BDE").membership_start.year, + address="Earth", + paid=False, + ml_events_registration="en", + ml_sport_registration=True, + ml_art_registration=True, + )) + self.assertTrue(response.status_code, 200) + + +class TestValidateRegistration(TestCase): + """ + Test the admin interface to validate users + """ + + fixtures = ('initial',) + + def setUp(self) -> None: + self.superuser = User.objects.create_superuser( + username="admintoto", + password="toto1234", + email="admin.toto@example.com", + ) + self.client.force_login(self.superuser) + + self.user = User.objects.create( + username="toto", + first_name="Toto", + last_name="TOTO", + email="toto@example.com", + ) + + sess = self.client.session + sess["permission_mask"] = 42 + sess.save() + + def test_future_user_list(self): + """ + Display the list of pre-registered users + """ + response = self.client.get(reverse("registration:future_user_list")) + self.assertEqual(response.status_code, 200) + + response = self.client.get(reverse("registration:future_user_list") + "?search=toto") + self.assertEqual(response.status_code, 200) + + def test_invalid_registrations(self): + """ + Send wrong data and check that errors are detected + """ + + # BDE Membership is mandatory + response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict( + soge=False, + credit_type=NoteSpecial.objects.get(special_type="Chèque").id, + credit_amount=4200, + last_name="TOTO", + first_name="Toto", + bank="Société générale", + join_BDE=False, + join_Kfet=False, + )) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.context["form"].errors) + + # Same + response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict( + soge=False, + credit_type="", + credit_amount=0, + last_name="TOTO", + first_name="Toto", + bank="Société générale", + join_BDE=False, + join_Kfet=True, + )) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.context["form"].errors) + + # The BDE membership is not free + response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict( + soge=False, + credit_type=NoteSpecial.objects.get(special_type="Espèces").id, + credit_amount=0, + last_name="TOTO", + first_name="Toto", + bank="J'ai pas d'argent", + join_BDE=True, + join_Kfet=True, + )) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.context["form"].errors) + + # Last and first names are required for a credit + response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict( + soge=False, + credit_type=NoteSpecial.objects.get(special_type="Chèque").id, + credit_amount=4000, + last_name="", + first_name="", + bank="", + join_BDE=True, + join_Kfet=True, + )) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.context["form"].errors) + + # The username admïntoto is too similar with the alias admintoto. + # Since the form is valid, the user must update its username. + self.user.username = "admïntoto" + self.user.save() + response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict( + soge=False, + credit_type=NoteSpecial.objects.get(special_type="Chèque").id, + credit_amount=500, + last_name="TOTO", + first_name="Toto", + bank="Société générale", + join_BDE=True, + join_Kfet=False, + )) + self.assertEqual(response.status_code, 200) + self.assertTrue(response.context["form"].errors) + + def test_validate_bde_registration(self): + """ + The user wants only to join the BDE. We validate the registration. + """ + response = self.client.get(reverse("registration:future_user_detail", args=(self.user.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.get(self.user.profile.get_absolute_url()) + self.assertEqual(response.status_code, 404) + + self.user.profile.email_confirmed = True + self.user.profile.save() + + response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict( + soge=False, + credit_type=NoteSpecial.objects.get(special_type="Chèque").id, + credit_amount=500, + last_name="TOTO", + first_name="Toto", + bank="Société générale", + join_BDE=True, + join_Kfet=False, + )) + self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200) + self.user.profile.refresh_from_db() + self.assertTrue(self.user.profile.registration_valid) + self.assertTrue(NoteUser.objects.filter(user=self.user).exists()) + self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists()) + self.assertFalse(Membership.objects.filter(club__name="Kfet", user=self.user).exists()) + self.assertFalse(SogeCredit.objects.filter(user=self.user).exists()) + self.assertEqual(Transaction.objects.filter( + Q(source=self.user.note) | Q(destination=self.user.note)).count(), 2) + + response = self.client.get(self.user.profile.get_absolute_url()) + self.assertEqual(response.status_code, 200) + + def test_validate_kfet_registration(self): + """ + The user joins the BDE and the Kfet. + """ + response = self.client.get(reverse("registration:future_user_detail", args=(self.user.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.get(self.user.profile.get_absolute_url()) + self.assertEqual(response.status_code, 404) + + self.user.profile.email_confirmed = True + self.user.profile.save() + + response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict( + soge=False, + credit_type=NoteSpecial.objects.get(special_type="Espèces").id, + credit_amount=4000, + last_name="TOTO", + first_name="Toto", + bank="Société générale", + join_BDE=True, + join_Kfet=True, + )) + self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200) + self.user.profile.refresh_from_db() + self.assertTrue(self.user.profile.registration_valid) + self.assertTrue(NoteUser.objects.filter(user=self.user).exists()) + self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists()) + self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists()) + self.assertFalse(SogeCredit.objects.filter(user=self.user).exists()) + self.assertEqual(Transaction.objects.filter( + Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3) + + response = self.client.get(self.user.profile.get_absolute_url()) + self.assertEqual(response.status_code, 200) + + def test_validate_kfet_registration_with_soge(self): + """ + The user joins the BDE and the Kfet, but the membership is paid by the Société générale. + """ + response = self.client.get(reverse("registration:future_user_detail", args=(self.user.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.get(self.user.profile.get_absolute_url()) + self.assertEqual(response.status_code, 404) + + self.user.profile.email_confirmed = True + self.user.profile.save() + + response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict( + soge=True, + credit_type=NoteSpecial.objects.get(special_type="Espèces").id, + credit_amount=4000, + last_name="TOTO", + first_name="Toto", + bank="Société générale", + join_BDE=True, + join_Kfet=True, + )) + self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200) + self.user.profile.refresh_from_db() + self.assertTrue(self.user.profile.registration_valid) + self.assertTrue(NoteUser.objects.filter(user=self.user).exists()) + self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists()) + self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists()) + self.assertTrue(SogeCredit.objects.filter(user=self.user).exists()) + self.assertEqual(Transaction.objects.filter( + Q(source=self.user.note) | Q(destination=self.user.note)).count(), 2) + self.assertFalse(Transaction.objects.filter(valid=True).exists()) + + response = self.client.get(self.user.profile.get_absolute_url()) + self.assertEqual(response.status_code, 200) + + def test_invalidate_registration(self): + """ + Try to invalidate (= delete) pre-registration. + """ + response = self.client.get(reverse("registration:future_user_invalidate", args=(self.user.pk,))) + self.assertRedirects(response, reverse("registration:future_user_list"), 302, 200) + self.assertFalse(User.objects.filter(pk=self.user.pk).exists()) + + def test_resend_email_validation_link(self): + """ + Resend email validation linK. + """ + response = self.client.get(reverse("registration:email_validation_resend", args=(self.user.pk,))) + self.assertRedirects(response, reverse("registration:future_user_detail", args=(self.user.pk,)), 302, 200) diff --git a/apps/registration/views.py b/apps/registration/views.py index bf68a8ed..7a924591 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -16,7 +16,7 @@ from django.views.generic.edit import FormMixin from django_tables2 import SingleTableView from member.forms import ProfileForm from member.models import Membership, Club -from note.models import SpecialTransaction +from note.models import SpecialTransaction, Alias from note.templatetags.pretty_money import pretty_money from permission.backends import PermissionBackend from permission.models import Role @@ -101,7 +101,7 @@ class UserValidateView(TemplateView): user.profile.email_confirmed = True user.save() user.profile.save() - return self.render_to_response(self.get_context_data()) + return self.render_to_response(self.get_context_data(), status=200 if self.validlink else 400) def get_user(self, uidb64): """ @@ -169,12 +169,9 @@ class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi :return: """ qs = super().get_queryset().distinct().filter(profile__registration_valid=False) - if "search" in self.request.GET: + if "search" in self.request.GET and self.request.GET["search"]: pattern = self.request.GET["search"] - if not pattern: - return qs.none() - qs = qs.filter( Q(first_name__iregex=pattern) | Q(last_name__iregex=pattern) @@ -205,10 +202,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, def post(self, request, *args, **kwargs): form = self.get_form() self.object = self.get_object() - if form.is_valid(): - return self.form_valid(form) - else: - return self.form_invalid(form) + return self.form_valid(form) if form.is_valid() else self.form_invalid(form) def get_queryset(self, **kwargs): """ @@ -239,6 +233,10 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, def form_valid(self, form): user = self.get_object() + if Alias.objects.filter(normalized_name=Alias.normalize(user.username)).exists(): + form.add_error(None, _("An alias with a similar name already exists.")) + return self.form_invalid(form) + # Get form data soge = form.cleaned_data["soge"] credit_type = form.cleaned_data["credit_type"] @@ -276,9 +274,6 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, if credit_type is None: credit_amount = 0 - if join_Kfet and not join_BDE: - form.add_error('join_Kfet', _("You must join BDE club before joining Kfet club.")) - if fee > credit_amount and not soge: # Check if the user credits enough money form.add_error('credit_type', From ff187581c9d0aaa6c1aae3697d5adfc9ac76d360 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 3 Sep 2020 21:21:09 +0200 Subject: [PATCH 28/41] Remove useless blank lines and spaces in api app --- apps/api/serializers.py | 1 + apps/api/viewsets.py | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/api/serializers.py b/apps/api/serializers.py index 1f658217..d59bdc43 100644 --- a/apps/api/serializers.py +++ b/apps/api/serializers.py @@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import User from rest_framework.serializers import ModelSerializer + class UserSerializer(ModelSerializer): """ REST API Serializer for Users. diff --git a/apps/api/viewsets.py b/apps/api/viewsets.py index 825082c0..f7d1e481 100644 --- a/apps/api/viewsets.py +++ b/apps/api/viewsets.py @@ -6,11 +6,8 @@ from django_filters.rest_framework import DjangoFilterBackend from django.db.models import Q from django.conf import settings from django.contrib.auth.models import User - from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet - from permission.backends import PermissionBackend - from note_kfet.middlewares import get_current_session from note.models import Alias @@ -47,7 +44,6 @@ class ReadOnlyProtectedModelViewSet(ReadOnlyModelViewSet): return self.model.objects.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct() - class UserViewSet(ReadProtectedModelViewSet): """ REST API View set. @@ -67,7 +63,7 @@ class UserViewSet(ReadProtectedModelViewSet): if "search" in self.request.GET: pattern = self.request.GET["search"] - + # We match first a user by its username, then if an alias is matched without normalization # And finally if the normalized pattern matches a normalized alias. queryset = queryset.filter( From d29e1d69d18e323c8b35e4639d253622d1014851 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 3 Sep 2020 21:47:08 +0200 Subject: [PATCH 29/41] Format api viewsets --- apps/api/urls.py | 1 + apps/api/viewsets.py | 50 +++++++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/apps/api/urls.py b/apps/api/urls.py index 4addbdf1..7131c657 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -4,6 +4,7 @@ from django.conf import settings from django.conf.urls import url, include from rest_framework import routers + from .viewsets import ContentTypeViewSet, UserViewSet # Routers provide an easy way of automatically determining the URL conf. diff --git a/apps/api/viewsets.py b/apps/api/viewsets.py index f7d1e481..333ae5e3 100644 --- a/apps/api/viewsets.py +++ b/apps/api/viewsets.py @@ -64,28 +64,36 @@ class UserViewSet(ReadProtectedModelViewSet): if "search" in self.request.GET: pattern = self.request.GET["search"] - # We match first a user by its username, then if an alias is matched without normalization - # And finally if the normalized pattern matches a normalized alias. + # Filter with different rules + # We use union-all to keep each filter rule sorted in result queryset = queryset.filter( - username__iregex="^" + pattern).union( - queryset.filter( - Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern)), all=True).union( - queryset.filter( - Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) - & ~Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern)), all=True).union( - queryset.filter( - Q(note__alias__normalized_name__iregex="^" + pattern.lower()) - & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) - & ~Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern)), all=True).union( - queryset.filter( - (Q(last_name__iregex="^" + pattern) | Q(first_name__iregex="^" + pattern)) - & ~Q(note__alias__normalized_name__iregex="^" + pattern.lower()) - & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) - & ~Q(note__alias__name__iregex="^" + pattern) - & ~Q(username__iregex="^" + pattern)), all=True) + # Match without normalization + note__alias__name__iregex="^" + pattern + ).union( + queryset.filter( + # Match with normalization + Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) + & ~Q(note__alias__name__iregex="^" + pattern) + ), + all=True, + ).union( + queryset.filter( + # Match on lower pattern + Q(note__alias__normalized_name__iregex="^" + pattern.lower()) + & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) + & ~Q(note__alias__name__iregex="^" + pattern) + ), + all=True, + ).union( + queryset.filter( + # Match on firstname or lastname + (Q(last_name__iregex="^" + pattern) | Q(first_name__iregex="^" + pattern)) + & ~Q(note__alias__normalized_name__iregex="^" + pattern.lower()) + & ~Q(note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) + & ~Q(note__alias__name__iregex="^" + pattern) + ), + all=True, + ) queryset = queryset if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' \ else queryset.order_by("username") From c66d66bc643626ce3cd3314998dd5e3fbdaffce1 Mon Sep 17 00:00:00 2001 From: Pierre-antoine Comby Date: Thu, 3 Sep 2020 23:47:31 +0200 Subject: [PATCH 30/41] fertig ! --- locale/de/LC_MESSAGES/django.po | 268 +++++++++++++++++++++----------- 1 file changed, 173 insertions(+), 95 deletions(-) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 7ceefe42..1f5c0403 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-09-02 23:17+0200\n" -"PO-Revision-Date: 2020-09-03 11:40+0200\n" +"PO-Revision-Date: 2020-09-03 23:47+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: de\n" @@ -1528,7 +1528,7 @@ msgstr "Aktueller Preis" #: apps/note/templates/note/transactiontemplate_list.html:13 msgid "Name of the button..." -msgstr "Name dessen Tatse" +msgstr "Name dessen Tatse." #: apps/note/templates/note/transactiontemplate_list.html:15 msgid "New button" @@ -1660,7 +1660,7 @@ msgid "" "of model {app_label}.{model_name}." msgstr "" "Sie haben nicht die Berechtigung, das Feld {field} in dieser Instanz von " -"Modell {app_Label} zu ändern. {model_name}" +"Modell {app_label} zu ändern. {model_name}" #: apps/permission/signals.py:73 apps/permission/views.py:89 #, python-brace-format @@ -1758,7 +1758,7 @@ msgstr "Diese email adresse ist schon benutzt." #: apps/registration/forms.py:49 msgid "Register to the WEI" -msgstr "zu WEI anmelden" +msgstr "Zu WEI anmelden" #: apps/registration/forms.py:51 msgid "" @@ -1881,7 +1881,7 @@ msgstr "Danke" #: apps/registration/templates/registration/mails/email_validation_email.html:39 #: apps/registration/templates/registration/mails/email_validation_email.txt:15 msgid "The Note Kfet team." -msgstr "Die NoteKfet Team" +msgstr "Die NoteKfet Team." #: apps/registration/views.py:38 msgid "Register new user" @@ -1909,7 +1909,7 @@ msgstr "E-Mail-Validierungslink erneut senden" #: apps/registration/views.py:163 msgid "Pre-registered users list" -msgstr "Vorregistrierte Userliste," +msgstr "Vorregistrierte Userliste" #: apps/registration/views.py:190 msgid "Unregistered users" @@ -1941,15 +1941,15 @@ msgstr "Ungültige Vorregistrierung" #: apps/treasury/apps.py:12 note_kfet/templates/base.html:92 msgid "Treasury" -msgstr "" +msgstr "Quaestor" #: apps/treasury/forms.py:104 msgid "Remittance is already closed." -msgstr "" +msgstr "Überweisung ist bereits geschlossen." #: apps/treasury/forms.py:109 msgid "You can't change the type of the remittance." -msgstr "" +msgstr "Sie können die Art der Überweisung nicht ändern." #: apps/treasury/forms.py:129 apps/treasury/models.py:252 #: apps/treasury/tables.py:97 apps/treasury/tables.py:105 @@ -1957,15 +1957,15 @@ msgstr "" #: apps/treasury/templates/treasury/remittance_list.html:16 #: apps/treasury/templates/treasury/sogecredit_list.html:16 msgid "Remittance" -msgstr "" +msgstr "Überweisung" #: apps/treasury/forms.py:130 msgid "No attached remittance" -msgstr "" +msgstr "Keine beigefügte Überweisung" #: apps/treasury/models.py:23 msgid "Invoice identifier" -msgstr "" +msgstr "Rechnungskennung" #: apps/treasury/models.py:37 msgid "BDE" @@ -1989,11 +1989,11 @@ msgstr "Datum" #: apps/treasury/models.py:64 msgid "Acquitted" -msgstr "bezahlt" +msgstr "Bezahlt" #: apps/treasury/models.py:69 msgid "Locked" -msgstr "gesperrt" +msgstr "Gesperrt" #: apps/treasury/models.py:70 msgid "An invoice can't be edited when it is locked." @@ -2177,7 +2177,7 @@ msgstr "Transaktionen" #: apps/treasury/templates/treasury/remittance_form.html:42 msgid "There is no transaction linked with this remittance." -msgstr "Mit dieser Überweisung ist keine Transaktion verbunden" +msgstr "Mit dieser Überweisung ist keine Transaktion verbunden." #: apps/treasury/templates/treasury/remittance_list.html:27 msgid "Opened remittances" @@ -2185,7 +2185,7 @@ msgstr "Geöffnete Überweisungen" #: apps/treasury/templates/treasury/remittance_list.html:34 msgid "There is no opened remittance." -msgstr "Es gibt keine offene Überweisung" +msgstr "Es gibt keine offene Überweisung." #: apps/treasury/templates/treasury/remittance_list.html:39 msgid "New remittance" @@ -2197,165 +2197,185 @@ msgstr "Transfer ohne Überweisungen" #: apps/treasury/templates/treasury/remittance_list.html:52 msgid "There is no transaction without any linked remittance." -msgstr "Mit dieser Überweisung ist keine Transaktion verbunden" +msgstr "Mit dieser Überweisung ist keine Transaktion verbunden." #: apps/treasury/templates/treasury/remittance_list.html:60 msgid "Transfers with opened remittances" -msgstr "" +msgstr "Transfer mit geöffneten Überweisungen" #: apps/treasury/templates/treasury/remittance_list.html:67 msgid "There is no transaction with an opened linked remittance." msgstr "" +"Es gibt keine Transaktion mit einer geöffneten verknüpften Überweisung." #: apps/treasury/templates/treasury/remittance_list.html:75 msgid "Closed remittances" -msgstr "" +msgstr "Geschlossene Überweisungen" #: apps/treasury/templates/treasury/remittance_list.html:82 msgid "There is no closed remittance yet." -msgstr "" +msgstr "Es gibt noch keine geschlossene Überweisung." #: apps/treasury/templates/treasury/sogecredit_detail.html:29 msgid "total amount" -msgstr "" +msgstr "Totalanzahlt" #: apps/treasury/templates/treasury/sogecredit_detail.html:35 msgid "" "Warning: Validating this credit implies that all membership transactions " "will be validated." msgstr "" +"Achtung: Die Validierung dieses Guthabens bedeutet, dass alle " +"Mitgliedschaftstransaktionen validiert werden." #: apps/treasury/templates/treasury/sogecredit_detail.html:36 msgid "" "If you delete this credit, there all membership transactions will be also " "validated, but no credit will be operated." msgstr "" +"Wenn Sie dieses Kredit löschen, werden dort auch alle " +"Mitgliedschaftstransaktionen validiert, es wird jedoch kein Kredit betrieben." #: apps/treasury/templates/treasury/sogecredit_detail.html:37 msgid "" "If this credit is validated, then the user won't be able to ask for a credit " "from the Société générale." msgstr "" +"Wenn dieses Kredit validiert ist, kann der Benutzer bei der Société générale " +"kein Guthaben anfordern." #: apps/treasury/templates/treasury/sogecredit_detail.html:38 msgid "If you think there is an error, please contact the \"respos info\"." msgstr "" +"Wenn Sie glauben, dass ein Fehler vorliegt, wenden Sie sich bitte an die " +"\"respos info\"." #: apps/treasury/templates/treasury/sogecredit_detail.html:44 msgid "This credit is already validated." -msgstr "" +msgstr "Dieser Kredit ist bereits validiert." #: apps/treasury/templates/treasury/sogecredit_detail.html:49 msgid "" "Warning: if you don't validate this credit, the note of the user doesn't " "have enough money to pay its memberships." msgstr "" +"Achtung: Wenn Sie diese Kredit nicht bestätigen, die Note von dem Benutzer " +"nicht genug Geld hat, um seine Mitgliedschaft zu zahlen." #: apps/treasury/templates/treasury/sogecredit_detail.html:50 msgid "Please ask the user to credit its note before deleting this credit." msgstr "" +"Bitte bitten Sie den Benutzer, seine Note gutzuschreiben, bevor Sie diese " +"Kredit löschen." #: apps/treasury/templates/treasury/sogecredit_detail.html:57 #: apps/wei/tables.py:59 apps/wei/tables.py:60 apps/wei/tables.py:99 msgid "Validate" -msgstr "" +msgstr "Validieren" #: apps/treasury/templates/treasury/sogecredit_detail.html:65 msgid "Return to credit list" -msgstr "" +msgstr "Zurück zur Kreditlist" #: apps/treasury/templates/treasury/sogecredit_list.html:34 msgid "Filter with unvalidated credits only" -msgstr "" +msgstr "Filtern Sie nur mit nicht validierten Kredits" #: apps/treasury/templates/treasury/sogecredit_list.html:44 msgid "There is no matched user that have asked for a Société générale credit." msgstr "" +"Es gibt keinen übereinstimmenden User, der eine Kredit für die Société " +"générale beantragt hat." #: apps/treasury/views.py:38 msgid "Create new invoice" -msgstr "" +msgstr "Neue Rechnung" #: apps/treasury/views.py:89 msgid "Invoices list" -msgstr "" +msgstr "Rechnunglist" #: apps/treasury/views.py:104 apps/treasury/views.py:265 #: apps/treasury/views.py:391 msgid "You are not able to see the treasury interface." -msgstr "" +msgstr "Sie können die Quaestor-App nicht sehen." #: apps/treasury/views.py:114 msgid "Update an invoice" -msgstr "" +msgstr "Rechnung bearbeiten" #: apps/treasury/views.py:226 msgid "Create a new remittance" -msgstr "" +msgstr "Neue Überweisung" #: apps/treasury/views.py:253 msgid "Remittances list" -msgstr "" +msgstr "Überweisungliste" #: apps/treasury/views.py:316 msgid "Update a remittance" -msgstr "" +msgstr "Überweisung bearbeiten" #: apps/treasury/views.py:339 msgid "Attach a transaction to a remittance" -msgstr "" +msgstr "Fügen Sie einer Überweisung eine Transaktion hinzu" #: apps/treasury/views.py:383 msgid "List of credits from the Société générale" -msgstr "" +msgstr "Kreditliste von Société générale" #: apps/treasury/views.py:426 msgid "Manage credits from the Société générale" -msgstr "" +msgstr "Krediten von der Société générale handeln" #: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50 #: apps/wei/models.py:61 apps/wei/models.py:167 #: note_kfet/templates/base.html:98 msgid "WEI" -msgstr "" +msgstr "WEI" #: apps/wei/forms/registration.py:51 apps/wei/models.py:113 #: apps/wei/models.py:283 msgid "bus" -msgstr "" +msgstr "Bus" #: apps/wei/forms/registration.py:52 msgid "" "This choice is not definitive. The WEI organizers are free to attribute for " "you a bus and a team, in particular if you are a free eletron." msgstr "" +"Diese Wahl ist nicht endgültig. Den WEI-Organisatoren steht es frei, Ihnen " +"einen Bus und ein Team zuzuweisen, insbesondere wenn Sie ein freies Elektron " +"sind." #: apps/wei/forms/registration.py:59 msgid "Team" -msgstr "" +msgstr "Team" #: apps/wei/forms/registration.py:61 msgid "" "Leave this field empty if you won't be in a team (staff, bus chief, free " "electron)" msgstr "" +"Lassen Sie dieses Feld leer, wenn Sie nicht in einem Team sind (Mitarbeiter, " +"Buschef, freies Elektron)" #: apps/wei/forms/registration.py:67 apps/wei/forms/registration.py:77 #: apps/wei/models.py:148 msgid "WEI Roles" -msgstr "" +msgstr "WEI Rollen" #: apps/wei/forms/registration.py:68 msgid "Select the roles that you are interested in." -msgstr "" +msgstr "Wählen Sie die Rollen aus, an denen Sie interessiert sind." #: apps/wei/forms/registration.py:113 msgid "This team doesn't belong to the given bus." -msgstr "" +msgstr "Dieses Team gehört nicht zum angegebenen Bus." #: apps/wei/forms/surveys/wei2020.py:29 msgid "Choose a word:" -msgstr "" +msgstr "Wählen Sie ein Wort:" #: apps/wei/models.py:24 apps/wei/templates/wei/base.html:36 msgid "year" @@ -2371,11 +2391,11 @@ msgstr "Abschlussdatum" #: apps/wei/models.py:77 msgid "survey information" -msgstr "" +msgstr "Umfrage Infos" #: apps/wei/models.py:78 msgid "Information about the survey for new members, encoded in JSON" -msgstr "" +msgstr "Informationen zur Umfrage für neue Mitglieder, codiert in JSON" #: apps/wei/models.py:100 msgid "Bus" @@ -2411,7 +2431,7 @@ msgstr "Kredit von der Société générale" #: apps/wei/models.py:177 msgid "Caution check given" -msgstr "" +msgstr "Caution check given" #: apps/wei/models.py:181 apps/wei/templates/wei/weimembership_form.html:64 msgid "birth date" @@ -2427,7 +2447,7 @@ msgstr "Weiblich" #: apps/wei/models.py:189 msgid "Non binary" -msgstr "nicht binär" +msgstr "Nicht binär" #: apps/wei/models.py:191 apps/wei/templates/wei/weimembership_form.html:55 msgid "gender" @@ -2520,7 +2540,7 @@ msgstr "WEI Preis (bezahlte Studenten)" #: apps/wei/templates/wei/base.html:47 apps/wei/templates/wei/base.html:54 msgid "The BDE membership is included in the WEI registration." -msgstr "Die BDE-Mitgliedschaft ist in der WEI-Registrierung enthalten" +msgstr "Die BDE-Mitgliedschaft ist in der WEI-Registrierung enthalten." #: apps/wei/templates/wei/base.html:51 msgid "WEI fee (unpaid students)" @@ -2574,7 +2594,7 @@ msgstr "Weiter" #: apps/wei/templates/wei/survey_closed.html:15 msgid "The inscription for this WEI are now closed." -msgstr " Die Inschrift für diese WEI ist nun geschlossen." +msgstr "Die Inschrift für diese WEI ist nun geschlossen." #: apps/wei/templates/wei/survey_closed.html:19 msgid "Return to WEI detail" @@ -2602,7 +2622,7 @@ msgstr "Mitglied der WEI" #: apps/wei/templates/wei/weiclub_detail.html:89 msgid "Unvalidated registrations" -msgstr "unvalidierte Registrierungen" +msgstr "Unvalidierte Registrierungen" #: apps/wei/templates/wei/weiclub_list.html:14 apps/wei/views.py:76 msgid "Create WEI" @@ -2614,68 +2634,70 @@ msgstr "WEI List" #: apps/wei/templates/wei/weimembership_form.html:10 msgid "Review registration" -msgstr "" +msgstr "Registrierung schauen" #: apps/wei/templates/wei/weimembership_form.html:35 msgid "ENS year" -msgstr "" +msgstr "ENS Jahr" #: apps/wei/templates/wei/weimembership_form.html:76 msgid "Payment from Société générale" -msgstr "" +msgstr "Kredit von der Société générale" #: apps/wei/templates/wei/weimembership_form.html:80 msgid "Suggested bus from the survey:" -msgstr "" +msgstr "Vorgeschlagener Bus aus der Umfrage:" #: apps/wei/templates/wei/weimembership_form.html:85 msgid "Raw survey information" -msgstr "" +msgstr "Rohe Umfrageinformationen" #: apps/wei/templates/wei/weimembership_form.html:95 msgid "The algorithm didn't run." -msgstr "" +msgstr "Der Algorithmus wurde nicht ausgeführt." #: apps/wei/templates/wei/weimembership_form.html:98 msgid "caution check given" -msgstr "" +msgstr "Vorsichtsprüfung gegeben" #: apps/wei/templates/wei/weimembership_form.html:102 msgid "preferred bus" -msgstr "" +msgstr "bevorzugter Bus" #: apps/wei/templates/wei/weimembership_form.html:105 msgid "preferred team" -msgstr "" +msgstr "bevorzugtes Team" #: apps/wei/templates/wei/weimembership_form.html:108 msgid "preferred roles" -msgstr "" +msgstr "bevorzugte Rollen" #: apps/wei/templates/wei/weimembership_form.html:115 #: apps/wei/templates/wei/weiregistration_confirm_delete.html:30 msgid "Update registration" -msgstr "" +msgstr "Registrierung aktualisieren" #: apps/wei/templates/wei/weimembership_form.html:131 msgid "The registration is already validated and can't be unvalidated." msgstr "" +"Die Registrierung ist bereits validiert und kann nicht ungültig gemacht " +"werden." #: apps/wei/templates/wei/weimembership_form.html:132 msgid "The user joined the bus" -msgstr "" +msgstr "Der Benutzer ist dem Bus beigetreten" #: apps/wei/templates/wei/weimembership_form.html:133 msgid "in the team" -msgstr "" +msgstr "In der Team" #: apps/wei/templates/wei/weimembership_form.html:134 msgid "in no team (staff)" -msgstr "" +msgstr "In keinem Team (staff)" #: apps/wei/templates/wei/weimembership_form.html:134 msgid "with the following roles:" -msgstr "" +msgstr "mit den folgenden Rollen:" #: apps/wei/templates/wei/weimembership_form.html:139 msgid "" @@ -2684,6 +2706,11 @@ msgid "" "created but will be invalid. You will have to validate it once the bank " "validated the creation of the account, or to change the payment method." msgstr "" +"Das WEI wird von der Société générale bezahlt. Die Mitgliedschaft wird auch " +"dann erstellt, wenn die Bank die BDE noch nicht bezahlt hat. Die " +"Mitgliedschaftstransaktion wird erstellt, ist jedoch ungültig. Sie müssen es " +"validieren, sobald die Bank die Erstellung des Kontos validiert hat, oder " +"die Zahlungsmethode ändern." #: apps/wei/templates/wei/weimembership_form.html:149 #, python-format @@ -2691,6 +2718,8 @@ msgid "" "The note don't have enough money (%(balance)s, %(pretty_fee)s required). The " "registration may fail if you don't credit the note now." msgstr "" +"Die Note hat nicht genug Geld (%(balance)s,%(pretty_fee)s erforderlich). Die " +"Registrierung kann fehlschlagen, wenn Sie die Note jetzt nicht gutschreiben." #: apps/wei/templates/wei/weimembership_form.html:157 #, python-format @@ -2698,10 +2727,12 @@ msgid "" "The note has enough money (%(pretty_fee)s required), the registration is " "possible." msgstr "" +"Die Note hat genug Geld (%(pretty_fee)s erforderlich), die Registrierung ist " +"möglich." #: apps/wei/templates/wei/weimembership_form.html:166 msgid "The user didn't give her/his caution check." -msgstr "" +msgstr "Der User hat nicht sein Vorsichtsprüfung gegeben." #: apps/wei/templates/wei/weimembership_form.html:174 msgid "" @@ -2709,14 +2740,18 @@ msgid "" "membership will be processed automatically, the WEI registration includes " "the membership fee." msgstr "" +"Dieser Benutzer ist für das kommende Jahr kein Mitglied des Kfet-Clubs. Die " +"Mitgliedschaft wird automatisch bearbeitet, die WEI-Registrierung beinhaltet " +"den Mitgliedsbeitrag." #: apps/wei/templates/wei/weimembership_list.html:23 msgid "View unvalidated registrations..." -msgstr "" +msgstr "Nicht validierte Registrierungen anzeigen ..." #: apps/wei/templates/wei/weiregistration_confirm_delete.html:16 msgid "This registration is already validated and can't be deleted." msgstr "" +"Diese Registrierung ist bereits validiert und kann nicht gelöscht werden." #: apps/wei/templates/wei/weiregistration_confirm_delete.html:23 #, python-format @@ -2724,14 +2759,16 @@ msgid "" "Are you sure you want to delete the registration of %(user)s for the WEI " "%(wei_name)s? This action can't be undone." msgstr "" +"Möchten Sie die Registrierung von %(user)s für die WEI %(wei_name)s wirklich " +"löschen? Diese Aktion kann nicht rückgängig gemacht werden." #: apps/wei/templates/wei/weiregistration_list.html:17 msgid "There is no pre-registration found with this pattern." -msgstr "" +msgstr "Bei diesem Muster wurde keine Vorregistrierung gefunden." #: apps/wei/templates/wei/weiregistration_list.html:23 msgid "View validated memberships..." -msgstr "" +msgstr "Validierte Mitgliedschaften anzeigen ..." #: apps/wei/views.py:55 msgid "Search WEI" @@ -2787,45 +2824,47 @@ msgstr "WEI Team bearbeiten" #: apps/wei/views.py:482 msgid "Register first year student to the WEI" -msgstr "" +msgstr "Registrieren Sie den Erstsemester beim WEI" #: apps/wei/views.py:527 apps/wei/views.py:607 msgid "This user is already registered to this WEI." -msgstr "" +msgstr "Dieser Benutzer ist bereits bei dieser WEI registriert." #: apps/wei/views.py:532 msgid "" "This user can't be in her/his first year since he/she has already " "participated to a WEI." msgstr "" +"Dieser Benutzer kann nicht in seinem ersten Jahr sein, da er bereits an " +"einer WEI teilgenommen hat." #: apps/wei/views.py:549 msgid "Register old student to the WEI" -msgstr "" +msgstr "Registrieren Sie einen alten Studenten beim WEI" #: apps/wei/views.py:592 apps/wei/views.py:680 msgid "You already opened an account in the Société générale." -msgstr "" +msgstr "Sie haben bereits ein Konto in der Société générale eröffnet." #: apps/wei/views.py:637 msgid "Update WEI Registration" -msgstr "" +msgstr "WEI Registrierung aktualisieren" #: apps/wei/views.py:739 msgid "Delete WEI registration" -msgstr "" +msgstr "WEI Registrierung löschen" #: apps/wei/views.py:750 msgid "You don't have the right to delete this WEI registration." -msgstr "" +msgstr "Sie haben nicht das Recht, diese WEI-Registrierung zu löschen." #: apps/wei/views.py:769 msgid "Validate WEI registration" -msgstr "" +msgstr "Überprüfen Sie die WEI-Registrierung" #: apps/wei/views.py:916 msgid "This user didn't give her/his caution check." -msgstr "" +msgstr "Dieser User hat seine / ihre Vorsicht nicht überprüft." #: note_kfet/settings/base.py:155 msgid "German" @@ -2841,7 +2880,7 @@ msgstr "Französich" #: note_kfet/templates/400.html:10 msgid "Bad request" -msgstr "" +msgstr "Ungültige Anfrage" #: note_kfet/templates/400.html:14 msgid "" @@ -2849,32 +2888,37 @@ msgid "" "been sent to webmasters with the details of the error. You can now drink a " "coke." msgstr "" +"Entschuldigung, Ihre Anfrage war ungültig. Ich weiß nicht, was falsch sein " +"könnte. An Webmaster wurde eine E-Mail mit den Details des Fehlers gesendet. " +"Sie können jetzt eine Cola trinken." #: note_kfet/templates/403.html:10 msgid "Permission denied" -msgstr "" +msgstr "Zugang verweigert" #: note_kfet/templates/403.html:13 msgid "You don't have the right to perform this request." -msgstr "" +msgstr "Sie haben nicht das Recht, diese Anfrage auszuführen." #: note_kfet/templates/403.html:15 note_kfet/templates/404.html:19 msgid "Exception message:" -msgstr "" +msgstr "Ausnahmemeldung:" #: note_kfet/templates/404.html:10 msgid "Page not found" -msgstr "" +msgstr "Seite nicht gefunden" #: note_kfet/templates/404.html:14 #, python-format msgid "" "The requested path %(request_path)s was not found on the server." msgstr "" +"Der angeforderte Pfad %(request_path) s wurde auf dem Server " +"nicht gefunden." #: note_kfet/templates/500.html:10 msgid "Server error" -msgstr "" +msgstr "Serverfehler" #: note_kfet/templates/500.html:14 msgid "" @@ -2882,14 +2926,17 @@ msgid "" "sent to webmasters with the detail of the error, and this will be fixed " "soon. You can now drink a beer." msgstr "" +"Bei der Bearbeitung Ihrer Anfrage ist leider ein Fehler aufgetreten. Es " +"wurde eine E-Mail mit den Details des Fehlers an die Webmaster gesendet, die " +"in Kürze behoben wird. Sie können jetzt ein Bier trinken." #: note_kfet/templates/autocomplete_model.html:14 msgid "Reset" -msgstr "" +msgstr "Reset" #: note_kfet/templates/base.html:13 msgid "The ENS Paris-Saclay BDE note." -msgstr "" +msgstr "Die BDE ENS-Paris-Saclay Note." #: note_kfet/templates/base.html:74 msgid "Users" @@ -2908,14 +2955,16 @@ msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." msgstr "" +"Ihre E-Mail-Adresse ist nicht validiert. Bitte überprüfen Sie Ihren " +"Posteingang und klicken Sie auf den Validierungslink." #: note_kfet/templates/base_search.html:15 msgid "Search by attribute such as name…" -msgstr "" +msgstr "Suche nach Attributen wie Name…" #: note_kfet/templates/base_search.html:23 msgid "There is no results." -msgstr "" +msgstr "Es gibt keine Ergebnisse." #: note_kfet/templates/cas_server/base.html:7 msgid "Central Authentication Service" @@ -2928,21 +2977,25 @@ msgid "" "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider " "upgrading." msgstr "" +"Eine neue Version der Anwendung ist verfügbar. Diese Instanz führt " +"%(VERSION) s aus und die letzte Version ist %(LAST_VERSION) s. Bitte erwägen " +"Sie ein Upgrade." #: note_kfet/templates/registration/logged_out.html:13 msgid "Thanks for spending some quality time with the Web site today." msgstr "" +"Vielen Dank, dass Sie heute einige Zeit mit dieser Website verbracht haben." #: note_kfet/templates/registration/logged_out.html:14 msgid "Log in again" -msgstr "" +msgstr "Nochmal anmelden" #: note_kfet/templates/registration/login.html:6 #: note_kfet/templates/registration/login.html:15 #: note_kfet/templates/registration/login.html:38 #: note_kfet/templates/registration/password_reset_complete.html:15 msgid "Log in" -msgstr "" +msgstr "Anmelden" #: note_kfet/templates/registration/login.html:20 #, python-format @@ -2951,75 +3004,95 @@ msgid "" "page. Would you like to login to a different account, or with a higher " "permission mask?" msgstr "" +"Sie sind als %(username)s authentifiziert, aber nicht berechtigt, auf diese " +"Seite zuzugreifen. Möchten Sie sich bei einem anderen Konto oder mit einer " +"Maske mit höherer Berechtigung anmelden?" #: note_kfet/templates/registration/login.html:30 msgid "" "You must be logged with a staff account with the higher mask to access " "Django Admin." msgstr "" +"Sie müssen mit einem staffkonto mit der höheren Maske angemeldet sein, um " +"auf Django Admin zugreifen zu können." #: note_kfet/templates/registration/login.html:40 msgid "Forgotten your password or username?" -msgstr "" +msgstr "Passwort oder Username vergessen?" #: note_kfet/templates/registration/password_change_done.html:13 msgid "Your password was changed." -msgstr "" +msgstr "Ihr Passwort wurde geändert." #: note_kfet/templates/registration/password_change_form.html:14 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 "" +"Bitte geben Sie aus Sicherheitsgründen Ihr altes Passwort ein und geben Sie " +"dann Ihr neues Passwort zweimal ein, damit wir überprüfen können, ob Sie es " +"richtig eingegeben haben." #: note_kfet/templates/registration/password_change_form.html:16 #: note_kfet/templates/registration/password_reset_confirm.html:17 msgid "Change my password" -msgstr "" +msgstr "Mein Passwort ändern" #: note_kfet/templates/registration/password_reset_complete.html:13 msgid "Your password has been set. You may go ahead and log in now." -msgstr "" +msgstr "Ihr Passwort wurde festgelegt. Sie können sich jetzt anmelden." #: note_kfet/templates/registration/password_reset_confirm.html:14 msgid "" "Please enter your new password twice so we can verify you typed it in " "correctly." msgstr "" +"Bitte geben Sie Ihr neues Passwort zweimal ein, damit wir überprüfen können, " +"ob Sie es richtig eingegeben haben." #: note_kfet/templates/registration/password_reset_confirm.html:21 msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" +"Der Link zum Zurücksetzen des Passworts war ungültig, möglicherweise weil er " +"bereits verwendet wurde. Bitte fordern Sie ein neues Passwort änderung." #: note_kfet/templates/registration/password_reset_done.html:13 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 "" +"Wir haben Ihnen Anweisungen zum Festlegen Ihres Passworts per E-Mail " +"gesendet, falls ein Konto mit der von Ihnen eingegebenen E-Mail-Adresse " +"vorhanden ist. Sie sollten sie in Kürze erhalten." #: note_kfet/templates/registration/password_reset_done.html:14 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 "" +"Wenn Sie keine E-Mail erhalten, stellen Sie sicher, dass Sie die Adresse " +"eingegeben haben, unter der Sie sich registriert haben, und überprüfen Sie " +"Ihren Spam-Ordner." #: note_kfet/templates/registration/password_reset_form.html:13 msgid "" "Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" +"Passwort vergessen? Geben Sie unten Ihre E-Mail-Adresse ein, und wir senden " +"Ihnen Anweisungen zum Festlegen einer neuen E-Mail." #: note_kfet/templates/registration/password_reset_form.html:18 msgid "Reset my password" -msgstr "" +msgstr "Mein Passwort zurücksetzen" #: note_kfet/templates/registration/signup.html:6 #: note_kfet/templates/registration/signup.html:11 #: note_kfet/templates/registration/signup.html:27 msgid "Sign up" -msgstr "" +msgstr "Registrieren" #: note_kfet/templates/registration/signup.html:15 msgid "" @@ -3028,3 +3101,8 @@ msgid "" "Kfet and pay the registration fee. You must also validate your email address " "by following the link you received." msgstr "" +"Wenn Sie sich bereits registriert haben, wird Ihre Registrierung " +"berücksichtigt. Die BDE muss Ihr Konto validieren, bevor Sie sich anmelden " +"können. Sie müssen zum Kfet gehen und die Registrierungbeitrag bezahlen. Sie " +"müssen Ihre E-Mail-Adresse auch überprüfen, indem Sie dem Link folgen, den " +"Sie erhalten haben." From f8a0e207728af6535097f537bc43631a2e464a25 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 4 Sep 2020 07:44:59 +0200 Subject: [PATCH 31/41] Regenerate locales --- locale/de/LC_MESSAGES/django.po | 149 +++-- locale/fr/LC_MESSAGES/django.po | 1059 +++---------------------------- 2 files changed, 144 insertions(+), 1064 deletions(-) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 1f5c0403..e27135fa 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-02 23:17+0200\n" +"POT-Creation-Date: 2020-09-04 07:44+0200\n" "PO-Revision-Date: 2020-09-03 23:47+0200\n" "Last-Translator: \n" "Language-Team: \n" @@ -49,7 +49,7 @@ msgid "You can't invite more than 3 people to this activity." msgstr "Sie dürfen höchstens 3 Leute zu dieser Veranstaltung einladen." #: apps/activity/models.py:27 apps/activity/models.py:62 -#: apps/member/models.py:198 +#: apps/member/models.py:200 #: apps/member/templates/member/includes/club_info.html:4 #: apps/member/templates/member/includes/profile_info.html:4 #: apps/note/models/notes.py:247 apps/note/models/transactions.py:26 @@ -110,7 +110,7 @@ msgstr "Wo findet die Veranstaltung statt ? (z.B Kfet)" msgid "type" msgstr "Type" -#: apps/activity/models.py:88 apps/logs/models.py:22 apps/member/models.py:303 +#: apps/activity/models.py:88 apps/logs/models.py:22 apps/member/models.py:305 #: apps/note/models/notes.py:138 apps/treasury/models.py:267 #: apps/treasury/templates/treasury/sogecredit_detail.html:14 #: apps/wei/models.py:160 apps/wei/templates/wei/survey.html:15 @@ -273,7 +273,7 @@ msgstr "Gastliste" #: apps/note/models/transactions.py:259 #: apps/note/templates/note/transaction_form.html:16 #: apps/note/templates/note/transaction_form.html:148 -#: note_kfet/templates/base.html:68 +#: note_kfet/templates/base.html:69 msgid "Transfer" msgstr "Überweisen" @@ -356,7 +356,7 @@ msgid "validate" msgstr "validate" #: apps/activity/templates/activity/includes/activity_info.html:71 -#: apps/logs/models.py:62 apps/note/tables.py:162 +#: apps/logs/models.py:62 apps/note/tables.py:194 msgid "edit" msgstr "bearbeiten" @@ -368,7 +368,7 @@ msgstr "Einladen" msgid "Create new activity" msgstr "Neue Veranstaltung schaffen" -#: apps/activity/views.py:59 note_kfet/templates/base.html:86 +#: apps/activity/views.py:59 note_kfet/templates/base.html:87 msgid "Activities" msgstr "Veranstaltungen" @@ -432,7 +432,7 @@ msgstr "neue Daten" msgid "create" msgstr "schaffen" -#: apps/logs/models.py:63 apps/note/tables.py:132 apps/note/tables.py:168 +#: apps/logs/models.py:63 apps/note/tables.py:164 apps/note/tables.py:200 #: apps/permission/models.py:127 apps/treasury/tables.py:38 #: apps/wei/tables.py:75 msgid "delete" @@ -458,21 +458,21 @@ msgstr "Changelog" msgid "changelogs" msgstr "Changelogs" -#: apps/member/admin.py:52 apps/member/models.py:225 +#: apps/member/admin.py:50 apps/member/models.py:227 #: apps/member/templates/member/includes/club_info.html:34 msgid "membership fee (paid students)" msgstr "Mitgliedschaftpreis (bezahlte Studenten)" -#: apps/member/admin.py:53 apps/member/models.py:230 +#: apps/member/admin.py:51 apps/member/models.py:232 #: apps/member/templates/member/includes/club_info.html:37 msgid "membership fee (unpaid students)" msgstr "Mitgliedschaftpreis (unbezahlte Studenten)" -#: apps/member/admin.py:67 apps/member/models.py:314 +#: apps/member/admin.py:65 apps/member/models.py:316 msgid "roles" msgstr "Rollen" -#: apps/member/admin.py:68 apps/member/models.py:328 +#: apps/member/admin.py:66 apps/member/models.py:330 msgid "fee" msgstr "Preis" @@ -500,8 +500,8 @@ msgstr "Wählen sie ein Bild aus" msgid "Maximal size: 2MB" msgstr "Maximal Größe: 2MB" -#: apps/member/forms.py:87 apps/member/views.py:101 -#: apps/registration/forms.py:33 +#: apps/member/forms.py:87 apps/member/views.py:100 +#: apps/registration/forms.py:33 apps/registration/views.py:237 msgid "An alias with a similar name already exists." msgstr "Ein ähnliches Alias ist schon benutzt." @@ -718,7 +718,7 @@ msgstr "Userprofile" msgid "Activate your Note Kfet account" msgstr "Ihre Note Kfet Konto bestätigen" -#: apps/member/models.py:203 +#: apps/member/models.py:205 #: apps/member/templates/member/includes/club_info.html:55 #: apps/member/templates/member/includes/profile_info.html:31 #: apps/registration/templates/registration/future_profile_detail.html:22 @@ -727,88 +727,88 @@ msgstr "Ihre Note Kfet Konto bestätigen" msgid "email" msgstr "Email" -#: apps/member/models.py:210 +#: apps/member/models.py:212 msgid "parent club" msgstr "Urclub" -#: apps/member/models.py:219 +#: apps/member/models.py:221 msgid "require memberships" msgstr "erfordern Mitgliedschaft" -#: apps/member/models.py:220 +#: apps/member/models.py:222 msgid "Uncheck if this club don't require memberships." msgstr "" "Deaktivieren Sie diese Option, wenn für diesen Club keine Mitgliedschaft " "erforderlich ist." -#: apps/member/models.py:236 +#: apps/member/models.py:238 #: apps/member/templates/member/includes/club_info.html:26 msgid "membership duration" msgstr "Mitgliedscahftzeit" -#: apps/member/models.py:237 +#: apps/member/models.py:239 msgid "The longest time (in days) a membership can last (NULL = infinite)." msgstr "Wie lang am höchsten eine Mitgliedschaft dauern kann." -#: apps/member/models.py:244 +#: apps/member/models.py:246 #: apps/member/templates/member/includes/club_info.html:16 msgid "membership start" msgstr "Mitgliedschaftanfangsdatum" -#: apps/member/models.py:245 +#: apps/member/models.py:247 msgid "Date from which the members can renew their membership." msgstr "Ab wann kann man sein Mitgliedschaft erneuern." -#: apps/member/models.py:251 +#: apps/member/models.py:253 #: apps/member/templates/member/includes/club_info.html:21 msgid "membership end" msgstr "Mitgliedschaftenddatum" -#: apps/member/models.py:252 +#: apps/member/models.py:254 msgid "Maximal date of a membership, after which members must renew it." msgstr "" "Maximales Datum einer Mitgliedschaft, nach dem Mitglieder es erneuern müssen." -#: apps/member/models.py:284 apps/member/models.py:309 +#: apps/member/models.py:286 apps/member/models.py:311 #: apps/note/models/notes.py:179 msgid "club" msgstr "Club" -#: apps/member/models.py:285 +#: apps/member/models.py:287 msgid "clubs" msgstr "Clubs" -#: apps/member/models.py:319 +#: apps/member/models.py:321 msgid "membership starts on" msgstr "Mitgliedschaft fängt an" -#: apps/member/models.py:323 +#: apps/member/models.py:325 msgid "membership ends on" msgstr "Mitgliedschaft endet am" -#: apps/member/models.py:374 +#: apps/member/models.py:375 #, python-brace-format msgid "The role {role} does not apply to the club {club}." msgstr "Die Rolle {role} ist nicht erlaubt für das Club {club}." -#: apps/member/models.py:385 apps/member/views.py:676 +#: apps/member/models.py:384 apps/member/views.py:669 msgid "User is already a member of the club" msgstr "User ist schon ein Mitglied dieser club" -#: apps/member/models.py:433 +#: apps/member/models.py:432 msgid "User is not a member of the parent club" msgstr "User ist noch nicht Mitglied des Urclubs" -#: apps/member/models.py:486 +#: apps/member/models.py:480 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "Mitgliedschaft von {user} für das Club {club}" -#: apps/member/models.py:489 +#: apps/member/models.py:483 apps/note/models/transactions.py:359 msgid "membership" msgstr "Mitgliedschaft" -#: apps/member/models.py:490 +#: apps/member/models.py:484 msgid "memberships" msgstr "Mitgliedschaften" @@ -902,8 +902,8 @@ msgstr "" "erlaubt." #: apps/member/templates/member/club_alias.html:10 -#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:240 -#: apps/member/views.py:450 +#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:238 +#: apps/member/views.py:443 msgid "Note aliases" msgstr "Note Aliases" @@ -1034,43 +1034,43 @@ msgstr "Anmeldung" msgid "This address must be valid." msgstr "Diese Adresse muss gültig sein." -#: apps/member/views.py:138 +#: apps/member/views.py:137 msgid "Profile detail" msgstr "Profile detail" -#: apps/member/views.py:201 +#: apps/member/views.py:197 msgid "Search user" msgstr "User finden" -#: apps/member/views.py:260 +#: apps/member/views.py:258 msgid "Update note picture" msgstr "Notebild ändern" -#: apps/member/views.py:318 +#: apps/member/views.py:311 msgid "Manage auth token" msgstr "Auth token bearbeiten" -#: apps/member/views.py:346 +#: apps/member/views.py:338 msgid "Create new club" msgstr "Neue Club" -#: apps/member/views.py:364 +#: apps/member/views.py:357 msgid "Search club" msgstr "Club finden" -#: apps/member/views.py:397 +#: apps/member/views.py:390 msgid "Club detail" msgstr "Club Details" -#: apps/member/views.py:473 +#: apps/member/views.py:466 msgid "Update club" msgstr "Club bearbeiten" -#: apps/member/views.py:507 +#: apps/member/views.py:500 msgid "Add new member to the club" msgstr "Neue Mitglieder" -#: apps/member/views.py:667 apps/wei/views.py:922 +#: apps/member/views.py:660 apps/wei/views.py:922 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -1078,25 +1078,25 @@ msgstr "" "Diese User hat nicht genug Geld um Mitglied zu werden, und darf nich im Rot " "sein." -#: apps/member/views.py:680 +#: apps/member/views.py:673 msgid "The membership must start after {:%m-%d-%Y}." msgstr "Die Mitgliedschaft muss nach {:%m-%d-Y} anfängen." -#: apps/member/views.py:685 +#: apps/member/views.py:678 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "Die Mitgliedschaft muss vor {:%m-%d-Y} anfängen." -#: apps/member/views.py:701 apps/member/views.py:703 apps/member/views.py:705 -#: apps/registration/views.py:292 apps/registration/views.py:294 -#: apps/registration/views.py:296 apps/wei/views.py:927 apps/wei/views.py:931 +#: apps/member/views.py:694 apps/member/views.py:696 apps/member/views.py:698 +#: apps/registration/views.py:287 apps/registration/views.py:289 +#: apps/registration/views.py:291 apps/wei/views.py:927 apps/wei/views.py:931 msgid "This field is required." msgstr "Dies ist ein Pflichtfeld." -#: apps/member/views.py:789 +#: apps/member/views.py:771 msgid "Manage roles of an user in the club" msgstr "Rollen in diesen Club bearbeiten" -#: apps/member/views.py:814 +#: apps/member/views.py:796 msgid "Members of the club" msgstr "Mitlglieder dieses Club" @@ -1301,7 +1301,7 @@ msgid "transaction templates" msgstr "Transaktionsvorlagen" #: apps/note/models/transactions.py:112 apps/note/models/transactions.py:125 -#: apps/note/tables.py:33 apps/note/tables.py:42 +#: apps/note/tables.py:34 apps/note/tables.py:44 msgid "used alias" msgstr "benutzte Aliasen" @@ -1313,7 +1313,7 @@ msgstr "Anzahl" msgid "reason" msgstr "Grund" -#: apps/note/models/transactions.py:151 apps/note/tables.py:107 +#: apps/note/models/transactions.py:151 apps/note/tables.py:139 msgid "invalidity reason" msgstr "Ungültigkeit Grund" @@ -1386,7 +1386,7 @@ msgstr "Sondertransaktion" msgid "Special transactions" msgstr "Sondertranskationen" -#: apps/note/models/transactions.py:354 apps/note/models/transactions.py:359 +#: apps/note/models/transactions.py:354 msgid "membership transaction" msgstr "Mitgliedschafttransaktion" @@ -1394,19 +1394,19 @@ msgstr "Mitgliedschafttransaktion" msgid "membership transactions" msgstr "Mitgliedschaftttransaktionen" -#: apps/note/tables.py:61 +#: apps/note/tables.py:93 msgid "Click to invalidate" msgstr "Klicken Sie zum Ungültigmachen" -#: apps/note/tables.py:61 +#: apps/note/tables.py:93 msgid "Click to validate" msgstr "Klicken Sie zum gültigmachen" -#: apps/note/tables.py:105 +#: apps/note/tables.py:137 msgid "No reason specified" msgstr "Kein Grund gegeben" -#: apps/note/tables.py:136 apps/note/tables.py:170 apps/treasury/tables.py:39 +#: apps/note/tables.py:168 apps/note/tables.py:202 apps/treasury/tables.py:39 #: apps/treasury/templates/treasury/invoice_confirm_delete.html:30 #: apps/treasury/templates/treasury/sogecredit_detail.html:59 #: apps/wei/tables.py:76 apps/wei/tables.py:103 @@ -1414,7 +1414,7 @@ msgstr "Kein Grund gegeben" msgid "Delete" msgstr "Löschen" -#: apps/note/tables.py:164 apps/note/templates/note/conso_form.html:132 +#: apps/note/tables.py:196 apps/note/templates/note/conso_form.html:132 #: apps/wei/tables.py:47 apps/wei/tables.py:48 #: apps/wei/templates/wei/base.html:89 #: apps/wei/templates/wei/bus_detail.html:20 @@ -1562,7 +1562,7 @@ msgstr "Tatsen finden" msgid "Update button" msgstr "Tatse bearbeiten" -#: apps/note/views.py:151 note_kfet/templates/base.html:62 +#: apps/note/views.py:151 note_kfet/templates/base.html:63 msgid "Consumptions" msgstr "Verbräuche" @@ -1740,7 +1740,7 @@ msgstr "" "diesen Parametern zu erstellen. Bitte korrigieren Sie Ihre Daten und " "versuchen Sie es erneut." -#: apps/permission/views.py:96 note_kfet/templates/base.html:104 +#: apps/permission/views.py:96 note_kfet/templates/base.html:105 msgid "Rights" msgstr "Rechten" @@ -1911,35 +1911,30 @@ msgstr "E-Mail-Validierungslink erneut senden" msgid "Pre-registered users list" msgstr "Vorregistrierte Userliste" -#: apps/registration/views.py:190 +#: apps/registration/views.py:187 msgid "Unregistered users" msgstr "Unregistrierte Users" -#: apps/registration/views.py:203 +#: apps/registration/views.py:200 msgid "Registration detail" msgstr "Registrierung Detailen" -#: apps/registration/views.py:258 +#: apps/registration/views.py:256 msgid "You must join the BDE." msgstr "Sie müssen die BDE beitreten." #: apps/registration/views.py:280 -msgid "You must join BDE club before joining Kfet club." -msgstr "" -"Sie müssen dem BDE-Club beitreten, bevor Sie dem Kfet-Club beitreten können." - -#: apps/registration/views.py:285 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" "Der eingegebene Betrag reicht für die Mitgliedschaft nicht aus, sollte " "mindestens {} betragen" -#: apps/registration/views.py:360 +#: apps/registration/views.py:355 msgid "Invalidate pre-registration" msgstr "Ungültige Vorregistrierung" -#: apps/treasury/apps.py:12 note_kfet/templates/base.html:92 +#: apps/treasury/apps.py:12 note_kfet/templates/base.html:93 msgid "Treasury" msgstr "Quaestor" @@ -2330,7 +2325,7 @@ msgstr "Krediten von der Société générale handeln" #: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50 #: apps/wei/models.py:61 apps/wei/models.py:167 -#: note_kfet/templates/base.html:98 +#: note_kfet/templates/base.html:99 msgid "WEI" msgstr "WEI" @@ -2938,19 +2933,19 @@ msgstr "Reset" msgid "The ENS Paris-Saclay BDE note." msgstr "Die BDE ENS-Paris-Saclay Note." -#: note_kfet/templates/base.html:74 +#: note_kfet/templates/base.html:75 msgid "Users" msgstr "Users" -#: note_kfet/templates/base.html:80 +#: note_kfet/templates/base.html:81 msgid "Clubs" msgstr "CLubs" -#: note_kfet/templates/base.html:109 +#: note_kfet/templates/base.html:110 msgid "Admin" msgstr "Admin" -#: note_kfet/templates/base.html:153 +#: note_kfet/templates/base.html:154 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 96feaac7..53b84ef2 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-09-02 23:17+0200\n" +"POT-Creation-Date: 2020-09-04 07:44+0200\n" "PO-Revision-Date: 2020-09-02 23:18+0200\n" "Last-Translator: \n" "Language-Team: \n" @@ -49,7 +49,7 @@ msgid "You can't invite more than 3 people to this activity." msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." #: apps/activity/models.py:27 apps/activity/models.py:62 -#: apps/member/models.py:198 +#: apps/member/models.py:200 #: apps/member/templates/member/includes/club_info.html:4 #: apps/member/templates/member/includes/profile_info.html:4 #: apps/note/models/notes.py:247 apps/note/models/transactions.py:26 @@ -110,7 +110,7 @@ msgstr "Lieu où l'activité est organisée, par exemple la Kfet." msgid "type" msgstr "type" -#: apps/activity/models.py:88 apps/logs/models.py:22 apps/member/models.py:303 +#: apps/activity/models.py:88 apps/logs/models.py:22 apps/member/models.py:305 #: apps/note/models/notes.py:138 apps/treasury/models.py:267 #: apps/treasury/templates/treasury/sogecredit_detail.html:14 #: apps/wei/models.py:160 apps/wei/templates/wei/survey.html:15 @@ -273,7 +273,7 @@ msgstr "Liste des invités" #: apps/note/models/transactions.py:259 #: apps/note/templates/note/transaction_form.html:16 #: apps/note/templates/note/transaction_form.html:148 -#: note_kfet/templates/base.html:68 +#: note_kfet/templates/base.html:69 msgid "Transfer" msgstr "Virement" @@ -356,7 +356,7 @@ msgid "validate" msgstr "valider" #: apps/activity/templates/activity/includes/activity_info.html:71 -#: apps/logs/models.py:62 apps/note/tables.py:162 +#: apps/logs/models.py:62 apps/note/tables.py:194 msgid "edit" msgstr "modifier" @@ -368,7 +368,7 @@ msgstr "Inviter" msgid "Create new activity" msgstr "Créer une nouvelle activité" -#: apps/activity/views.py:59 note_kfet/templates/base.html:86 +#: apps/activity/views.py:59 note_kfet/templates/base.html:87 msgid "Activities" msgstr "Activités" @@ -434,7 +434,7 @@ msgstr "ouvelles données" msgid "create" msgstr "créer" -#: apps/logs/models.py:63 apps/note/tables.py:132 apps/note/tables.py:168 +#: apps/logs/models.py:63 apps/note/tables.py:164 apps/note/tables.py:200 #: apps/permission/models.py:127 apps/treasury/tables.py:38 #: apps/wei/tables.py:75 msgid "delete" @@ -460,21 +460,21 @@ msgstr "journal de modification" msgid "changelogs" msgstr "journaux de modifications" -#: apps/member/admin.py:52 apps/member/models.py:225 +#: apps/member/admin.py:50 apps/member/models.py:227 #: apps/member/templates/member/includes/club_info.html:34 msgid "membership fee (paid students)" msgstr "cotisation pour adhérer (normalien élève)" -#: apps/member/admin.py:53 apps/member/models.py:230 +#: apps/member/admin.py:51 apps/member/models.py:232 #: apps/member/templates/member/includes/club_info.html:37 msgid "membership fee (unpaid students)" msgstr "cotisation pour adhérer (normalien étudiant)" -#: apps/member/admin.py:67 apps/member/models.py:314 +#: apps/member/admin.py:65 apps/member/models.py:316 msgid "roles" msgstr "rôles" -#: apps/member/admin.py:68 apps/member/models.py:328 +#: apps/member/admin.py:66 apps/member/models.py:330 msgid "fee" msgstr "cotisation" @@ -502,8 +502,8 @@ msgstr "choisissez une image" msgid "Maximal size: 2MB" msgstr "Taille maximale : 2 Mo" -#: apps/member/forms.py:87 apps/member/views.py:101 -#: apps/registration/forms.py:33 +#: apps/member/forms.py:87 apps/member/views.py:100 +#: apps/registration/forms.py:33 apps/registration/views.py:237 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." @@ -720,7 +720,7 @@ msgstr "profil utilisateur" msgid "Activate your Note Kfet account" msgstr "Activez votre compte Note Kfet" -#: apps/member/models.py:203 +#: apps/member/models.py:205 #: apps/member/templates/member/includes/club_info.html:55 #: apps/member/templates/member/includes/profile_info.html:31 #: apps/registration/templates/registration/future_profile_detail.html:22 @@ -729,88 +729,88 @@ msgstr "Activez votre compte Note Kfet" msgid "email" msgstr "courriel" -#: apps/member/models.py:210 +#: apps/member/models.py:212 msgid "parent club" msgstr "club parent" -#: apps/member/models.py:219 +#: apps/member/models.py:221 msgid "require memberships" msgstr "nécessite des adhésions" -#: apps/member/models.py:220 +#: apps/member/models.py:222 msgid "Uncheck if this club don't require memberships." msgstr "Décochez si ce club n'utilise pas d'adhésions." -#: apps/member/models.py:236 +#: apps/member/models.py:238 #: apps/member/templates/member/includes/club_info.html:26 msgid "membership duration" msgstr "durée de l'adhésion" -#: apps/member/models.py:237 +#: apps/member/models.py:239 msgid "The longest time (in days) a membership can last (NULL = infinite)." msgstr "La durée maximale (en jours) d'une adhésion (NULL = infinie)." -#: apps/member/models.py:244 +#: apps/member/models.py:246 #: apps/member/templates/member/includes/club_info.html:16 msgid "membership start" msgstr "début de l'adhésion" -#: apps/member/models.py:245 +#: apps/member/models.py:247 msgid "Date from which the members can renew their membership." msgstr "" "Date à partir de laquelle les adhérents peuvent renouveler leur adhésion." -#: apps/member/models.py:251 +#: apps/member/models.py:253 #: apps/member/templates/member/includes/club_info.html:21 msgid "membership end" msgstr "fin de l'adhésion" -#: apps/member/models.py:252 +#: apps/member/models.py:254 msgid "Maximal date of a membership, after which members must renew it." msgstr "" "Date maximale d'une fin d'adhésion, après laquelle les adhérents doivent la " "renouveler." -#: apps/member/models.py:284 apps/member/models.py:309 +#: apps/member/models.py:286 apps/member/models.py:311 #: apps/note/models/notes.py:179 msgid "club" msgstr "club" -#: apps/member/models.py:285 +#: apps/member/models.py:287 msgid "clubs" msgstr "clubs" -#: apps/member/models.py:319 +#: apps/member/models.py:321 msgid "membership starts on" msgstr "l'adhésion commence le" -#: apps/member/models.py:323 +#: apps/member/models.py:325 msgid "membership ends on" msgstr "l'adhésion finit le" -#: apps/member/models.py:374 +#: apps/member/models.py:375 #, python-brace-format msgid "The role {role} does not apply to the club {club}." msgstr "Le rôle {role} ne s'applique pas au club {club}." -#: apps/member/models.py:385 apps/member/views.py:676 +#: apps/member/models.py:384 apps/member/views.py:669 msgid "User is already a member of the club" msgstr "L'utilisateur est déjà membre du club" -#: apps/member/models.py:433 +#: apps/member/models.py:432 msgid "User is not a member of the parent club" msgstr "L'utilisateur n'est pas membre du club parent" -#: apps/member/models.py:486 +#: apps/member/models.py:480 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "Adhésion de {user} pour le club {club}" -#: apps/member/models.py:489 +#: apps/member/models.py:483 apps/note/models/transactions.py:359 msgid "membership" msgstr "adhésion" -#: apps/member/models.py:490 +#: apps/member/models.py:484 msgid "memberships" msgstr "adhésions" @@ -904,8 +904,8 @@ msgstr "" "à nouveau possible." #: apps/member/templates/member/club_alias.html:10 -#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:240 -#: apps/member/views.py:450 +#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:238 +#: apps/member/views.py:443 msgid "Note aliases" msgstr "Alias de la note" @@ -1036,43 +1036,43 @@ msgstr "Inscriptions" msgid "This address must be valid." msgstr "Cette adresse doit être valide." -#: apps/member/views.py:138 +#: apps/member/views.py:137 msgid "Profile detail" msgstr "Détails de l'utilisateur" -#: apps/member/views.py:201 +#: apps/member/views.py:197 msgid "Search user" msgstr "Chercher un utilisateur" -#: apps/member/views.py:260 +#: apps/member/views.py:258 msgid "Update note picture" msgstr "Modifier la photo de la note" -#: apps/member/views.py:318 +#: apps/member/views.py:311 msgid "Manage auth token" msgstr "Gérer les jetons d'authentification" -#: apps/member/views.py:346 +#: apps/member/views.py:338 msgid "Create new club" msgstr "Créer un nouveau club" -#: apps/member/views.py:364 +#: apps/member/views.py:357 msgid "Search club" msgstr "Chercher un club" -#: apps/member/views.py:397 +#: apps/member/views.py:390 msgid "Club detail" msgstr "Détails du club" -#: apps/member/views.py:473 +#: apps/member/views.py:466 msgid "Update club" msgstr "Modifier le club" -#: apps/member/views.py:507 +#: apps/member/views.py:500 msgid "Add new member to the club" msgstr "Ajouter un nouveau membre au club" -#: apps/member/views.py:667 apps/wei/views.py:922 +#: apps/member/views.py:660 apps/wei/views.py:922 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -1080,25 +1080,25 @@ msgstr "" "Cet utilisateur n'a pas assez d'argent pour rejoindre ce club et ne peut pas " "avoir un solde négatif." -#: apps/member/views.py:680 +#: apps/member/views.py:673 msgid "The membership must start after {:%m-%d-%Y}." msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}." -#: apps/member/views.py:685 +#: apps/member/views.py:678 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." -#: apps/member/views.py:701 apps/member/views.py:703 apps/member/views.py:705 -#: apps/registration/views.py:292 apps/registration/views.py:294 -#: apps/registration/views.py:296 apps/wei/views.py:927 apps/wei/views.py:931 +#: apps/member/views.py:694 apps/member/views.py:696 apps/member/views.py:698 +#: apps/registration/views.py:287 apps/registration/views.py:289 +#: apps/registration/views.py:291 apps/wei/views.py:927 apps/wei/views.py:931 msgid "This field is required." msgstr "Ce champ est requis." -#: apps/member/views.py:789 +#: apps/member/views.py:771 msgid "Manage roles of an user in the club" msgstr "Gérer les rôles d'un utilisateur dans le club" -#: apps/member/views.py:814 +#: apps/member/views.py:796 msgid "Members of the club" msgstr "Membres du club" @@ -1304,7 +1304,7 @@ msgid "transaction templates" msgstr "modèles de transaction" #: apps/note/models/transactions.py:112 apps/note/models/transactions.py:125 -#: apps/note/tables.py:33 apps/note/tables.py:42 +#: apps/note/tables.py:34 apps/note/tables.py:44 msgid "used alias" msgstr "alias utilisé" @@ -1316,7 +1316,7 @@ msgstr "quantité" msgid "reason" msgstr "raison" -#: apps/note/models/transactions.py:151 apps/note/tables.py:107 +#: apps/note/models/transactions.py:151 apps/note/tables.py:139 msgid "invalidity reason" msgstr "motif d'invalidité" @@ -1391,7 +1391,7 @@ msgstr "Transaction de crédit/retrait" msgid "Special transactions" msgstr "Transactions de crédit/retrait" -#: apps/note/models/transactions.py:354 apps/note/models/transactions.py:359 +#: apps/note/models/transactions.py:354 msgid "membership transaction" msgstr "transaction d'adhésion" @@ -1399,19 +1399,19 @@ msgstr "transaction d'adhésion" msgid "membership transactions" msgstr "transactions d'adhésion" -#: apps/note/tables.py:61 +#: apps/note/tables.py:93 msgid "Click to invalidate" msgstr "Cliquez pour dévalider" -#: apps/note/tables.py:61 +#: apps/note/tables.py:93 msgid "Click to validate" msgstr "Cliquez pour valider" -#: apps/note/tables.py:105 +#: apps/note/tables.py:137 msgid "No reason specified" msgstr "Pas de motif spécifié" -#: apps/note/tables.py:136 apps/note/tables.py:170 apps/treasury/tables.py:39 +#: apps/note/tables.py:168 apps/note/tables.py:202 apps/treasury/tables.py:39 #: apps/treasury/templates/treasury/invoice_confirm_delete.html:30 #: apps/treasury/templates/treasury/sogecredit_detail.html:59 #: apps/wei/tables.py:76 apps/wei/tables.py:103 @@ -1419,7 +1419,7 @@ msgstr "Pas de motif spécifié" msgid "Delete" msgstr "Supprimer" -#: apps/note/tables.py:164 apps/note/templates/note/conso_form.html:132 +#: apps/note/tables.py:196 apps/note/templates/note/conso_form.html:132 #: apps/wei/tables.py:47 apps/wei/tables.py:48 #: apps/wei/templates/wei/base.html:89 #: apps/wei/templates/wei/bus_detail.html:20 @@ -1567,7 +1567,7 @@ msgstr "Chercher un bouton" msgid "Update button" msgstr "Modifier le bouton" -#: apps/note/views.py:151 note_kfet/templates/base.html:62 +#: apps/note/views.py:151 note_kfet/templates/base.html:63 msgid "Consumptions" msgstr "Consommations" @@ -1746,7 +1746,7 @@ msgstr "" "Vous n'avez pas la permission d'ajouter une instance du modèle « {model} » " "avec ces paramètres. Merci de les corriger et de réessayer." -#: apps/permission/views.py:96 note_kfet/templates/base.html:104 +#: apps/permission/views.py:96 note_kfet/templates/base.html:105 msgid "Rights" msgstr "Droits" @@ -1915,34 +1915,30 @@ msgstr "Renvoyer le lien de validation" msgid "Pre-registered users list" msgstr "Liste des utilisateurs en attente d'inscription" -#: apps/registration/views.py:190 +#: apps/registration/views.py:187 msgid "Unregistered users" msgstr "Utilisateurs en attente d'inscription" -#: apps/registration/views.py:203 +#: apps/registration/views.py:200 msgid "Registration detail" msgstr "Détails de l'inscription" -#: apps/registration/views.py:258 +#: apps/registration/views.py:256 msgid "You must join the BDE." msgstr "Vous devez adhérer au BDE." #: apps/registration/views.py:280 -msgid "You must join BDE club before joining Kfet club." -msgstr "Vous devez adhérer au club BDE avant d'adhérer au club Kfet." - -#: apps/registration/views.py:285 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" "Le montant crédité est trop faible pour adhérer, il doit être au minimum de " "{}" -#: apps/registration/views.py:360 +#: apps/registration/views.py:355 msgid "Invalidate pre-registration" msgstr "Invalider l'inscription" -#: apps/treasury/apps.py:12 note_kfet/templates/base.html:92 +#: apps/treasury/apps.py:12 note_kfet/templates/base.html:93 msgid "Treasury" msgstr "Trésorerie" @@ -2331,7 +2327,7 @@ msgstr "Gérer les crédits de la Société générale" #: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50 #: apps/wei/models.py:61 apps/wei/models.py:167 -#: note_kfet/templates/base.html:98 +#: note_kfet/templates/base.html:99 msgid "WEI" msgstr "WEI" @@ -2939,19 +2935,19 @@ msgstr "Réinitialiser" msgid "The ENS Paris-Saclay BDE note." msgstr "La note du BDE de l'ENS Paris-Saclay." -#: note_kfet/templates/base.html:74 +#: note_kfet/templates/base.html:75 msgid "Users" msgstr "Utilisateurs" -#: note_kfet/templates/base.html:80 +#: note_kfet/templates/base.html:81 msgid "Clubs" msgstr "Clubs" -#: note_kfet/templates/base.html:109 +#: note_kfet/templates/base.html:110 msgid "Admin" msgstr "Admin" -#: note_kfet/templates/base.html:153 +#: note_kfet/templates/base.html:154 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -3106,914 +3102,3 @@ msgstr "" "vous connecter. Vous devez vous rendre à la Kfet et payer les frais " "d'adhésion. Vous devez également valider votre adresse email en suivant le " "lien que vous avez reçu." - -#~ msgid "Enter a valid color." -#~ msgstr "Entrer une couleur valide." - -#~ msgid "i18n text" -#~ msgstr "i18n text" - -#~ msgid "i18n legend" -#~ msgstr "i18n legend" - -#~ msgid "Enter a valid value." -#~ msgstr "Entrer une valeur correcte." - -#~ msgid "Messages" -#~ msgstr "Messages" - -#~ msgid "Site Maps" -#~ msgstr "Plan du site" - -#~ msgid "Static Files" -#~ msgstr "Fichiers statiques" - -#~ msgid "Syndication" -#~ msgstr "Invitation" - -#~ msgid "That page number is not an integer" -#~ msgstr "Ce numéro de page n'est pas entier" - -#~ msgid "That page number is less than 1" -#~ msgstr "Ce numéro de page est inférieur à 1" - -#~ msgid "That page contains no results" -#~ msgstr "Il n'y a pas de résultat" - -#~ msgid "Enter a valid URL." -#~ msgstr "Entrer une URL valide." - -#~ msgid "Enter a valid integer." -#~ msgstr "Entrer un entier valid." - -#~ msgid "Enter a valid email address." -#~ msgstr "Entrer une adresse mail valide." - -#~ msgid "" -#~ "Enter a valid 'slug' consisting of letters, numbers, underscores or " -#~ "hyphens." -#~ msgstr "" -#~ "Entrer un 'slug' valide, constitué de lettres, chiffres, tirets ou tirets " -#~ "bas." - -#~ msgid "" -#~ "Enter a valid 'slug' consisting of Unicode letters, numbers, underscores, " -#~ "or hyphens." -#~ msgstr "" -#~ "Entrer un 'slug' valide, constitué de caractère unicode, chiffres, tirets " -#~ "ou underscores." - -#~ msgid "Enter a valid IPv4 address." -#~ msgstr "Entrer une adresse IPv4 valide." - -#~ msgid "Enter a valid IPv6 address." -#~ msgstr "Entrer une adresse IPv6 valide." - -#~ msgid "Enter a valid IPv4 or IPv6 address." -#~ msgstr "Entrer une adresse IPv4 ou IPv6 valide." - -#~ msgid "Enter only digits separated by commas." -#~ msgstr "Entrer seulement des chiffres séparés par des virgules." - -#~ msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." -#~ msgstr "" -#~ "Vérifier que cette valeur est %(limit_value)s (actuellement " -#~ "%(show_value)s)." - -#~ msgid "Ensure this value is less than or equal to %(limit_value)s." -#~ msgstr "Vérifier que cette valeur est plus petite que %(limit_value)s." - -#~ msgid "Ensure this value is greater than or equal to %(limit_value)s." -#~ msgstr "Vérifier que cette valeur est plus grande que %(limit_value)s." - -#~ msgid "" -#~ "Ensure this value has at least %(limit_value)d character (it has " -#~ "%(show_value)d)." -#~ msgid_plural "" -#~ "Ensure this value has at least %(limit_value)d characters (it has " -#~ "%(show_value)d)." -#~ msgstr[0] "" -#~ "Vérifier que cette valeur a au moins %(limit_value)d caractère " -#~ "(actuellement %(show_value)d)." -#~ msgstr[1] "" -#~ "Assurer vous que cette valeur a au moins %(limit_value)d caractères " -#~ "(actuellement %(show_value)d)." - -#~ msgid "" -#~ "Ensure this value has at most %(limit_value)d character (it has " -#~ "%(show_value)d)." -#~ msgid_plural "" -#~ "Ensure this value has at most %(limit_value)d characters (it has " -#~ "%(show_value)d)." -#~ msgstr[0] "" -#~ "Vérifier que cette valeur a au plus %(limit_value)d caractère " -#~ "(actuellement %(show_value)d)." -#~ msgstr[1] "" -#~ "Assurer vous que cette valeur a au plus %(limit_value)d caractères " -#~ "(actuellement %(show_value)d)." - -#~ msgid "Enter a number." -#~ msgstr "Numéro de téléphone." - -#~ msgid "Ensure that there are no more than %(max)s digit in total." -#~ msgid_plural "Ensure that there are no more than %(max)s digits in total." -#~ msgstr[0] "Vérifier qu'il n'y a pas plus de %(max)s chiffre au total." -#~ msgstr[1] "Vérifier qu'il n'y a pas plus de %(max)s chiffres au total." - -#~ msgid "Ensure that there are no more than %(max)s decimal place." -#~ msgid_plural "Ensure that there are no more than %(max)s decimal places." -#~ msgstr[0] "Vérifier qu'il y n'as pas plus de %(max)s chiffre décimal." -#~ msgstr[1] "Vérifier qu'il y n'as pas plus de %(max)s chiffres décimaux." - -#~ msgid "" -#~ "Ensure that there are no more than %(max)s digit before the decimal point." -#~ msgid_plural "" -#~ "Ensure that there are no more than %(max)s digits before the decimal " -#~ "point." -#~ msgstr[0] "" -#~ "Vérifier qu'il y n'as pas plus d'%(max)s chiffre avant la virgule." -#~ msgstr[1] "" -#~ "Vérifier qu'il y n'as pas plus de %(max)s chiffres avant la virgule." - -#~ msgid "" -#~ "File extension '%(extension)s' is not allowed. Allowed extensions are: " -#~ "'%(allowed_extensions)s'." -#~ msgstr "" -#~ "Les fichiers d'extension '%(extension)s' ne sont pas autorisé. Les " -#~ "extension autorisées sont: \"%(allowed_extensions)s'." - -#~ msgid "Null characters are not allowed." -#~ msgstr "Les caractères nuls ne sont pas autorisés." - -#~ msgid "and" -#~ msgstr "et" - -#~ msgid "%(model_name)s with this %(field_labels)s already exists." -#~ msgstr "Un %(model_name)s avec ce %(field_labels)s existe déjà." - -#~ msgid "Value %(value)r is not a valid choice." -#~ msgstr "Le choix %(value)r n'est pas possible." - -#~ msgid "This field cannot be null." -#~ msgstr "Ce champ est requis." - -#~ msgid "This field cannot be blank." -#~ msgstr "Ce champ est requis." - -#~ msgid "%(model_name)s with this %(field_label)s already exists." -#~ msgstr "Un %(model_name)s avec ce %(field_label)s existe déjà." - -#~ msgid "" -#~ "%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." -#~ msgstr "" -#~ "%(field_label)s doit être unique pour %(date_field_label)s " -#~ "%(lookup_type)s." - -#~ msgid "Field of type: %(field_type)s" -#~ msgstr "Champ du type %(field_type)s" - -#~ msgid "Integer" -#~ msgstr "Nombre entier" - -#~ msgid "'%(value)s' value must be an integer." -#~ msgstr "'%(value)s' doit être un nombre entier." - -#~ msgid "Big (8 byte) integer" -#~ msgstr "Gros nombre entier (8 octets)" - -#~ msgid "'%(value)s' value must be either True or False." -#~ msgstr "'%(value)s' doit être Vrai ou Faux." - -#~ msgid "'%(value)s' value must be either True, False, or None." -#~ msgstr "'%(value)s' doit être Vrai, Faux, ou None." - -#~ msgid "Boolean (Either True or False)" -#~ msgstr "Booléen (Vrai ou Faux)" - -#~ msgid "String (up to %(max_length)s)" -#~ msgstr "Chaîne de caractère (maximum %(max_length)s caractères)" - -#~ msgid "Comma-separated integers" -#~ msgstr "Liste d'entier séparer par des virgules" - -#~ msgid "" -#~ "'%(value)s' value has an invalid date format. It must be in YYYY-MM-DD " -#~ "format." -#~ msgstr "'%(value)s n'est pas formaté correctement (AAAA-MM-JJ)." - -#~ msgid "" -#~ "'%(value)s' value has the correct format (YYYY-MM-DD) but it is an " -#~ "invalid date." -#~ msgstr "" -#~ "'%(value)s possède le format date requis (AAAA-MM-JJ) mais n'est pas une " -#~ "date valide." - -#~ msgid "Date (without time)" -#~ msgstr "Date (sans horaire)" - -#~ msgid "" -#~ "'%(value)s' value has an invalid format. It must be in YYYY-MM-DD HH:MM[:" -#~ "ss[.uuuuuu]][TZ] format." -#~ msgstr "" -#~ "'%(value)s' n'est pas formatée correctement (AAAA-MM-JJ HH:MM[:ss[." -#~ "uuuuuu]][TZ])." - -#~ msgid "" -#~ "'%(value)s' value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" -#~ "[TZ]) but it is an invalid date/time." -#~ msgstr "" -#~ "'%(value)s' est formaté correctement, mais n'est pas une date/horaire " -#~ "valide." - -#~ msgid "Date (with time)" -#~ msgstr "Date (avec horaire)" - -#~ msgid "'%(value)s' value must be a decimal number." -#~ msgstr "'%(value)s doit être un nombre décimal." - -#~ msgid "Decimal number" -#~ msgstr "Nombre décimal" - -#~ msgid "" -#~ "'%(value)s' value has an invalid format. It must be in [DD] [HH:[MM:]]ss[." -#~ "uuuuuu] format." -#~ msgstr "" -#~ "'%(value)s' n'est pas formatée correctement (AAAA-MM-JJ HH:MM[:ss[." -#~ "uuuuuu]][TZ])." - -#~ msgid "Duration" -#~ msgstr "Durée" - -#~ msgid "Email address" -#~ msgstr "Courriel" - -#~ msgid "File path" -#~ msgstr "Chemin du fichier" - -#~ msgid "'%(value)s' value must be a float." -#~ msgstr "'%(value)s doit être un nombre décimal." - -#~ msgid "Floating point number" -#~ msgstr "Nombre décimal" - -#~ msgid "IPv4 address" -#~ msgstr "Adresse IPv4" - -#~ msgid "IP address" -#~ msgstr "Adresse IP" - -#~ msgid "'%(value)s' value must be either None, True or False." -#~ msgstr "'%(value)s' doit être Vrai, Faux, ou None." - -#~ msgid "Boolean (Either True, False or None)" -#~ msgstr "Booléen (Vrai ou Faux)" - -#~ msgid "Positive integer" -#~ msgstr "Nombre entier positif" - -#~ msgid "Positive small integer" -#~ msgstr "Nombre entier positif petit" - -#~ msgid "Slug (up to %(max_length)s)" -#~ msgstr "Slug (maximum %(max_length)s caractères)" - -#~ msgid "Small integer" -#~ msgstr "Petit entier" - -#~ msgid "Text" -#~ msgstr "Texte" - -#~ msgid "" -#~ "'%(value)s' value has an invalid format. It must be in HH:MM[:ss[." -#~ "uuuuuu]] format." -#~ msgstr "'%(value)s' n'est pas formatée correctement (HH:MM[:ss[.uuuuuu]])." - -#~ msgid "" -#~ "'%(value)s' value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is " -#~ "an invalid time." -#~ msgstr "" -#~ "'%(value)s' est formaté correctement, mais n'est pas un horaire valide." - -#~ msgid "Time" -#~ msgstr "Temps" - -#~ msgid "URL" -#~ msgstr "URL" - -#~ msgid "Raw binary data" -#~ msgstr "Donnée binaire brute" - -#~ msgid "'%(value)s' is not a valid UUID." -#~ msgstr "'%(value)s' n'est pas un UUID valide." - -#~ msgid "Universally unique identifier" -#~ msgstr "Identifiant unique universel" - -#~ msgid "File" -#~ msgstr "Fichier" - -#~ msgid "Image" -#~ msgstr "Image" - -#~ msgid "%(model)s instance with %(field)s %(value)r does not exist." -#~ msgstr "" -#~ "l'objet %(model)s avec les champs %(field)s %(value)r n'existe pas." - -#~ msgid "Foreign Key (type determined by related field)" -#~ msgstr "Relations plusieurs-à-un (type déterminé par le champ relié)" - -#~ msgid "One-to-one relationship" -#~ msgstr "Relation un-à-un" - -#~ msgid "%(from)s-%(to)s relationship" -#~ msgstr "Relation %(from)s-%(to)s" - -#~ msgid "%(from)s-%(to)s relationships" -#~ msgstr "Relations %(from)s-%(to)s" - -#~ msgid "Many-to-many relationship" -#~ msgstr "Relation plusieurs-à-plusieurs" - -#~ msgid ":?.!" -#~ msgstr ":?.!" - -#~ msgid "Enter a whole number." -#~ msgstr "Entrer un numéro complet." - -#~ msgid "Enter a valid date." -#~ msgstr "Entrer une date valide." - -#~ msgid "Enter a valid time." -#~ msgstr "Entrer un horaire valide." - -#~ msgid "Enter a valid date/time." -#~ msgstr "Entrer une date et un horaire valide." - -#~ msgid "Enter a valid duration." -#~ msgstr "Entrer une durée valide." - -#~ msgid "The number of days must be between {min_days} and {max_days}." -#~ msgstr "" -#~ "Le nombre de jours doit être compris entre {min_days} et {max_days}." - -#~ msgid "No file was submitted. Check the encoding type on the form." -#~ msgstr "Aucun fichier n'as été ajouté. Vérifier l'encodage du formulaire." - -#~ msgid "No file was submitted." -#~ msgstr "Aucun fichier n'as été ajouté." - -#~ msgid "The submitted file is empty." -#~ msgstr "Le fichier ajouté est vide." - -#~ msgid "" -#~ "Ensure this filename has at most %(max)d character (it has %(length)d)." -#~ msgid_plural "" -#~ "Ensure this filename has at most %(max)d characters (it has %(length)d)." -#~ msgstr[0] "" -#~ "Vérifier que le nom du fichier a au plus %(max)d caractère (actuellement " -#~ "%(length)d)." -#~ msgstr[1] "" -#~ "Vérifier que le nom du fichier a au plus %(max)d caractères (actuellement " -#~ "%(length)d)." - -#~ msgid "Please either submit a file or check the clear checkbox, not both." -#~ msgstr "Veuillez ajouter un fichier ou cocher la case envoi vide." - -#~ msgid "" -#~ "Upload a valid image. The file you uploaded was either not an image or a " -#~ "corrupted image." -#~ msgstr "Veuillez choisir une image valide." - -#~ msgid "" -#~ "Select a valid choice. %(value)s is not one of the available choices." -#~ msgstr "Faites un choix valide. %(value)s n'est pas autorisé." - -#~ msgid "Enter a list of values." -#~ msgstr "Entrer une liste de valeurs." - -#~ msgid "Enter a complete value." -#~ msgstr "Entrer une valeur complète." - -#~ msgid "Enter a valid UUID." -#~ msgstr "Entrer un UUID valid." - -#~ msgid ":" -#~ msgstr ":" - -#~ msgid "(Hidden field %(name)s) %(error)s" -#~ msgstr "(Champ caché %(name)s) %(error)s" - -#~ msgid "ManagementForm data is missing or has been tampered with" -#~ msgstr "Les données ManagementForm sont manquantes ou falsifiées" - -#~ msgid "Please submit %d or fewer forms." -#~ msgid_plural "Please submit %d or fewer forms." -#~ msgstr[0] "Veuillez remplir %d formulaire ou moins." -#~ msgstr[1] "Veuillez remplir %d formulaires ou moins." - -#~ msgid "Please submit %d or more forms." -#~ msgid_plural "Please submit %d or more forms." -#~ msgstr[0] "Veuillez remplir %d formulaire ou plus." -#~ msgstr[1] "Veuillez remplir %d formulaires ou plus." - -#~ msgid "Order" -#~ msgstr "Ordre" - -#~ msgid "Please correct the duplicate data for %(field)s." -#~ msgstr "Veuillez corriger les données dupliquées pour %(field)s." - -#~ msgid "" -#~ "Please correct the duplicate data for %(field)s, which must be unique." -#~ msgstr "Veuillez rendre uniques les valeurs des champs %(field)s." - -#~ msgid "" -#~ "Please correct the duplicate data for %(field_name)s which must be unique " -#~ "for the %(lookup)s in %(date_field)s." -#~ msgstr "" -#~ "Veuillez rendre unique les valeurs des champs %(field_name)s pour les " -#~ "%(lookup)s dans %(date_field)s." - -#~ msgid "Please correct the duplicate values below." -#~ msgstr "Veuillez rendre uniques les valeurs ci-dessous." - -#~ msgid "The inline value did not match the parent instance." -#~ msgstr "La valeur renseignée ne correspond pas à l'objet parent." - -#~ msgid "" -#~ "Select a valid choice. That choice is not one of the available choices." -#~ msgstr "Faites un choix possible." - -#~ msgid "\"%(pk)s\" is not a valid value." -#~ msgstr "\"%(pk)s\" n'est pas une valeur autorisée." - -#~ msgid "" -#~ "%(datetime)s couldn't be interpreted in time zone %(current_timezone)s; " -#~ "it may be ambiguous or it may not exist." -#~ msgstr "" -#~ "%(datetime)s n'a pas pu être interprété dans la zone " -#~ "%(current_timezone)s, La valeur est ambigüe ou impossible." - -#~ msgid "Clear" -#~ msgstr "Vider" - -#~ msgid "Currently" -#~ msgstr "Activité en cours" - -#~ msgid "Change" -#~ msgstr "Modifier" - -#~ msgid "Unknown" -#~ msgstr "Inconnu" - -#~ msgid "yes,no,maybe" -#~ msgstr "oui, non, peut-être" - -#~ msgid "%(size)d byte" -#~ msgid_plural "%(size)d bytes" -#~ msgstr[0] "%(size)d octet" -#~ msgstr[1] "%(size)d octets" - -#~ msgid "%s KB" -#~ msgstr "%s Ko" - -#~ msgid "%s MB" -#~ msgstr "%s Mo" - -#~ msgid "%s GB" -#~ msgstr "%s Go" - -#~ msgid "%s TB" -#~ msgstr "%s To" - -#~ msgid "%s PB" -#~ msgstr "%s Po" - -#~ msgid "p.m." -#~ msgstr "p.m." - -#~ msgid "a.m." -#~ msgstr "a.m." - -#~ msgid "PM" -#~ msgstr "PM" - -#~ msgid "AM" -#~ msgstr "AM" - -#~ msgid "midnight" -#~ msgstr "minuit" - -#~ msgid "noon" -#~ msgstr "midi" - -#~ msgid "Monday" -#~ msgstr "Lundi" - -#~ msgid "Tuesday" -#~ msgstr "Mardi" - -#~ msgid "Wednesday" -#~ msgstr "Mercredi" - -#~ msgid "Thursday" -#~ msgstr "Jeudi" - -#~ msgid "Friday" -#~ msgstr "Vendredi" - -#~ msgid "Saturday" -#~ msgstr "Samedi" - -#~ msgid "Sunday" -#~ msgstr "Dimanche" - -#~ msgid "Mon" -#~ msgstr "Lun" - -#~ msgid "Tue" -#~ msgstr "Mar" - -#~ msgid "Wed" -#~ msgstr "Mer" - -#~ msgid "Thu" -#~ msgstr "Jeu" - -#~ msgid "Fri" -#~ msgstr "Ven" - -#~ msgid "Sat" -#~ msgstr "Sam" - -#~ msgid "Sun" -#~ msgstr "Dim" - -#~ msgid "January" -#~ msgstr "Janvier" - -#~ msgid "February" -#~ msgstr "Février" - -#~ msgid "March" -#~ msgstr "Mars" - -#~ msgid "April" -#~ msgstr "Avril" - -#~ msgid "May" -#~ msgstr "Mai" - -#~ msgid "June" -#~ msgstr "Juin" - -#~ msgid "July" -#~ msgstr "Juillet" - -#~ msgid "August" -#~ msgstr "Août" - -#~ msgid "September" -#~ msgstr "Septembre" - -#~ msgid "October" -#~ msgstr "Octobre" - -#~ msgid "November" -#~ msgstr "Novembre" - -#~ msgid "December" -#~ msgstr "Décembre" - -#~ msgid "jan" -#~ msgstr "jan" - -#~ msgid "feb" -#~ msgstr "fev" - -#~ msgid "mar" -#~ msgstr "mar" - -#~ msgid "apr" -#~ msgstr "avr" - -#~ msgid "may" -#~ msgstr "mai" - -#~ msgid "jun" -#~ msgstr "jun" - -#~ msgid "jul" -#~ msgstr "jui" - -#~ msgid "aug" -#~ msgstr "aou" - -#~ msgid "sep" -#~ msgstr "sep" - -#~ msgid "oct" -#~ msgstr "oct" - -#~ msgid "nov" -#~ msgstr "nov" - -#~ msgid "dec" -#~ msgstr "dec" - -#~ msgctxt "abbrev. month" -#~ msgid "Jan." -#~ msgstr "[abbrev. month] Jan." - -#~ msgctxt "abbrev. month" -#~ msgid "Feb." -#~ msgstr "[abbrev. month] Fév." - -#~ msgctxt "abbrev. month" -#~ msgid "March" -#~ msgstr "[abbrev. month] Mars" - -#~ msgctxt "abbrev. month" -#~ msgid "April" -#~ msgstr "[abbrev. month] Avril" - -#~ msgctxt "abbrev. month" -#~ msgid "May" -#~ msgstr "[abbrev. month] Mai" - -#~ msgctxt "abbrev. month" -#~ msgid "June" -#~ msgstr "[abbrev. month] Juin" - -#~ msgctxt "abbrev. month" -#~ msgid "July" -#~ msgstr "[abbrev. month] Jui" - -#~ msgctxt "abbrev. month" -#~ msgid "Aug." -#~ msgstr "[abbrev. month] Août." - -#~ msgctxt "abbrev. month" -#~ msgid "Sept." -#~ msgstr "[abbrev. month] Sep." - -#~ msgctxt "abbrev. month" -#~ msgid "Oct." -#~ msgstr "[abbrev. month] Oct." - -#~ msgctxt "abbrev. month" -#~ msgid "Nov." -#~ msgstr "[abbrev. month] Nov." - -#~ msgctxt "abbrev. month" -#~ msgid "Dec." -#~ msgstr "[abbrev. month] Dec." - -#~ msgctxt "alt. month" -#~ msgid "January" -#~ msgstr "[alt. month] Janvier" - -#~ msgctxt "alt. month" -#~ msgid "February" -#~ msgstr "[alt. month] Février" - -#~ msgctxt "alt. month" -#~ msgid "March" -#~ msgstr "[alt. month] Mars" - -#~ msgctxt "alt. month" -#~ msgid "April" -#~ msgstr "[alt. month] Avril" - -#~ msgctxt "alt. month" -#~ msgid "May" -#~ msgstr "[alt. month] Mai" - -#~ msgctxt "alt. month" -#~ msgid "June" -#~ msgstr "[alt. month] Juin" - -#~ msgctxt "alt. month" -#~ msgid "July" -#~ msgstr "[alt. month] Juillet" - -#~ msgctxt "alt. month" -#~ msgid "August" -#~ msgstr "[alt. month] Août" - -#~ msgctxt "alt. month" -#~ msgid "September" -#~ msgstr "[alt. month] Septembre" - -#~ msgctxt "alt. month" -#~ msgid "October" -#~ msgstr "[alt. month] Octobre" - -#~ msgctxt "alt. month" -#~ msgid "November" -#~ msgstr "[alt. month] Novembre" - -#~ msgctxt "alt. month" -#~ msgid "December" -#~ msgstr "[alt. month] Décembre" - -#~ msgid "This is not a valid IPv6 address." -#~ msgstr "Adresse IPv6 non valide." - -#~ msgctxt "String to return when truncating text" -#~ msgid "%(truncated_text)s…" -#~ msgstr "[String to return when truncating text] %(truncated_text)s…" - -#~ msgid "or" -#~ msgstr "ou" - -#~ msgid ", " -#~ msgstr ", " - -#~ msgid "%d year" -#~ msgid_plural "%d years" -#~ msgstr[0] "%d année" -#~ msgstr[1] "%d années" - -#~ msgid "%d month" -#~ msgid_plural "%d months" -#~ msgstr[0] "%d mois" -#~ msgstr[1] "%d mois" - -#~ msgid "%d week" -#~ msgid_plural "%d weeks" -#~ msgstr[0] "%d semaine" -#~ msgstr[1] "%d semaines" - -#~ msgid "%d day" -#~ msgid_plural "%d days" -#~ msgstr[0] "%d jour" -#~ msgstr[1] "%d jours" - -#~ msgid "%d hour" -#~ msgid_plural "%d hours" -#~ msgstr[0] "%d heure" -#~ msgstr[1] "%d heures" - -#~ msgid "%d minute" -#~ msgid_plural "%d minutes" -#~ msgstr[0] "%d minute" -#~ msgstr[1] "%d minutes" - -#~ msgid "0 minutes" -#~ msgstr "0 minutes" - -#~ msgid "Forbidden" -#~ msgstr "Interdit" - -#~ msgid "CSRF verification failed. Request aborted." -#~ msgstr "Echec de la vérification CSRF. Requête interrompue." - -#~ msgid "" -#~ "You are seeing this message because this HTTPS site requires a 'Referer " -#~ "header' to be sent by your Web browser, but none was sent. This header is " -#~ "required for security reasons, to ensure that your browser is not being " -#~ "hijacked by third parties." -#~ msgstr "" -#~ "Vous voyez ce message car ce site HTTPS nécessite l'envoi d'un \"en-tête " -#~ "référent\" par votre navigateur. Cet en-tête est necessaire pour des " -#~ "raisons de sécurité, pour confirmer que votre navigateur n'est pas " -#~ "compromis par un tiers." - -#~ msgid "" -#~ "If you have configured your browser to disable 'Referer' headers, please " -#~ "re-enable them, at least for this site, or for HTTPS connections, or for " -#~ "'same-origin' requests." -#~ msgstr "" -#~ "Si vous avez configurer votre navigateur pour désactiver les \"en-tête " -#~ "référent\", veuillez les réactiver, au moins pour ce site, ou pour les " -#~ "requêtes HTTPS, ou les requête de même origine." - -#~ msgid "" -#~ "If you are using the tag " -#~ "or including the 'Referrer-Policy: no-referrer' header, please remove " -#~ "them. The CSRF protection requires the 'Referer' header to do strict " -#~ "referer checking. If you're concerned about privacy, use alternatives " -#~ "like for links to third-party sites." -#~ msgstr "" -#~ "Si vous utiliser le tag " -#~ "ou incluez l'en-tête 'Referrer-Policy: no-referrer', veuillez les " -#~ "retirer. La protection CSRF nécessite que l'en-tête référent fasse une " -#~ "vérification stricte. Si vous avez des préoccupations pour votre vie " -#~ "privée, vous pouvez utiliser des alternatives comme pour les liens vers des sites tiers." - -#~ msgid "" -#~ "You are seeing this message because this site requires a CSRF cookie when " -#~ "submitting forms. This cookie is required for security reasons, to ensure " -#~ "that your browser is not being hijacked by third parties." -#~ msgstr "" -#~ "Vous voyez ce message car ce site nécessite un cookie CSRF pour l'envoi " -#~ "de formulaire. Ce cookie est demandé pour des raisons de sécurité, pour " -#~ "s'assurer que votre navigateur n'est pas compromis par un tiers." - -#~ msgid "" -#~ "If you have configured your browser to disable cookies, please re-enable " -#~ "them, at least for this site, or for 'same-origin' requests." -#~ msgstr "" -#~ "Si vous avez configuré votre navigateur pour désactiver les cookies, " -#~ "veuillez les réactiver, au moins pour ce site, ou pour les requêtes de " -#~ "même origine." - -#~ msgid "More information is available with DEBUG=True." -#~ msgstr "Plus d'informations disponible avec DEBUG=True." - -#~ msgid "No year specified" -#~ msgstr "Pas d'année spécifiée" - -#~ msgid "Date out of range" -#~ msgstr "Date impossible" - -#~ msgid "No month specified" -#~ msgstr "Pas de mois spécifié" - -#~ msgid "No day specified" -#~ msgstr "Pas de jours spécifié" - -#~ msgid "No week specified" -#~ msgstr "Pas de semaine spécifiée" - -#~ msgid "No %(verbose_name_plural)s available" -#~ msgstr "%(verbose_name_plural)s non disponible" - -#~ msgid "" -#~ "Future %(verbose_name_plural)s not available because %(class_name)s." -#~ "allow_future is False." -#~ msgstr "" -#~ "Le futur %(verbose_name_plural)s n'est pas possible car %(class_name)s." -#~ "allow_future = False." - -#~ msgid "Invalid date string '%(datestr)s' given format '%(format)s'" -#~ msgstr "Date '%(datestr)s' au format non valide '%(format)s'." - -#~ msgid "No %(verbose_name)s found matching the query" -#~ msgstr "Aucun %(verbose_name)s trouvé pour cette requête" - -#~ msgid "Page is not 'last', nor can it be converted to an int." -#~ msgstr "" -#~ "La page spécifié n'est pas la dernière (last) et ne peux pas être " -#~ "convertie en nombre entier." - -#~ msgid "Invalid page (%(page_number)s): %(message)s" -#~ msgstr "Le numéro de page %(page_number)s est non valide: %(message)s" - -#~ msgid "Empty list and '%(class_name)s.allow_empty' is False." -#~ msgstr "Liste vide et '%(class_name)s.allow_empty = False." - -#~ msgid "Directory indexes are not allowed here." -#~ msgstr "L'index de Dossier n'est pas autorisé ici." - -#~ msgid "\"%(path)s\" does not exist" -#~ msgstr "\"%(path)s\" n'existe pas" - -#~ msgid "Index of %(directory)s" -#~ msgstr "Contenu de %(directory)s" - -#~ msgid "Django: the Web framework for perfectionists with deadlines." -#~ msgstr "" -#~ "Django: Le cadriciel Web pour les perfectionniste avec des dates limites." - -#~ msgid "" -#~ "View release notes for Django " -#~ "%(version)s" -#~ msgstr "" -#~ "Visiter les Notes de versions\" pour " -#~ "Django %(version)s" - -#~ msgid "The install worked successfully! Congratulations!" -#~ msgstr "L'installation a réussie! Félicitation!" - -#~ msgid "" -#~ "You are seeing this page because DEBUG=True is in your settings file and you have not configured " -#~ "any URLs." -#~ msgstr "" -#~ "Vous voyez cette page car DEBUG=True est renseigné dans vos paramètres et vous n'avez pas " -#~ "configuré d'URLs." - -#~ msgid "Django Documentation" -#~ msgstr "Documentation Django" - -#~ msgid "Topics, references, & how-to's" -#~ msgstr "Sujet, references & how-to" - -#~ msgid "Tutorial: A Polling App" -#~ msgstr "Tutoriel: Une application de sondage" - -#~ msgid "Get started with Django" -#~ msgstr "Débuter avec Django" - -#~ msgid "Django Community" -#~ msgstr "Communauté Django" - -#~ msgid "Connect, get help, or contribute" -#~ msgstr "Prendre contact, obtenir de l'aide ou contribuer" From cb545417acd8a7bd3583b692de569c6a96fee1b1 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 4 Sep 2020 07:47:52 +0200 Subject: [PATCH 32/41] Linting does not require deps --- .gitlab-ci.yml | 10 +--------- tox.ini | 1 - 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e8bd9b44..80d85791 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -40,15 +40,7 @@ linters: stage: quality-assurance image: debian:buster-backports before_script: - - > - apt-get update && - apt-get install --no-install-recommends -t buster-backports -y - python3-django python3-django-crispy-forms - python3-django-extensions python3-django-filters python3-django-polymorphic - python3-djangorestframework python3-django-cas-server python3-psycopg2 python3-pil - python3-babel python3-lockfile python3-pip python3-phonenumbers - python3-bs4 python3-setuptools tox - texlive-latex-extra texlive-lang-french lmodern texlive-fonts-recommended + - apt-get update && apt-get install -y tox script: tox -e linters # Be nice to new contributors, but please use `tox` diff --git a/tox.ini b/tox.ini index ad5cdc6f..b160324b 100644 --- a/tox.ini +++ b/tox.ini @@ -21,7 +21,6 @@ commands = [testenv:linters] deps = - -r{toxinidir}/requirements.txt flake8 flake8-colors flake8-import-order From b6847415b588601f74aeff7438575b02ba7f95f6 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 4 Sep 2020 07:53:31 +0200 Subject: [PATCH 33/41] Remove unused imports in tests --- apps/member/tests/test_login.py | 3 --- apps/member/tests/test_memberships.py | 2 -- 2 files changed, 5 deletions(-) diff --git a/apps/member/tests/test_login.py b/apps/member/tests/test_login.py index c4467f81..e022c4ea 100644 --- a/apps/member/tests/test_login.py +++ b/apps/member/tests/test_login.py @@ -5,8 +5,6 @@ from django.contrib.auth.models import User from django.test import TestCase from django.urls import reverse -from note.models import TransactionTemplate, TemplateCategory - """ Test that login page still works """ @@ -56,4 +54,3 @@ class TemplateLoggedInTests(TestCase): def test_accounts_password_reset(self): response = self.client.get('/accounts/password_reset/') self.assertEqual(response.status_code, 200) - diff --git a/apps/member/tests/test_memberships.py b/apps/member/tests/test_memberships.py index 4852de77..ef8b8209 100644 --- a/apps/member/tests/test_memberships.py +++ b/apps/member/tests/test_memberships.py @@ -5,14 +5,12 @@ import hashlib import os from datetime import date, timedelta -from django.conf import settings from django.contrib.auth.models import User from django.core.files.uploadedfile import SimpleUploadedFile from django.db.models import Q from django.test import TestCase from django.urls import reverse from django.utils import timezone - from member.models import Club, Membership, Profile from note.models import Alias, NoteSpecial from permission.models import Role From c03c18e93a9cca155a988148c5387becf964cd69 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 4 Sep 2020 15:53:00 +0200 Subject: [PATCH 34/41] Test and cover treasury app --- .../templates/activity/activity_entry.html | 2 +- apps/treasury/admin.py | 4 +- apps/treasury/api/views.py | 10 +- apps/treasury/forms.py | 10 +- apps/treasury/models.py | 4 +- .../treasury}/static/img/Finalist.png | Bin .../treasury}/static/img/Kataclist.png | Bin .../treasury}/static/img/Listorique.png | Bin .../treasury}/static/img/Monopolist.png | Bin .../treasury}/static/img/Saperlistpopette.png | Bin .../treasury}/static/img/Satellist.png | Bin apps/treasury/tables.py | 2 +- .../templates/treasury/invoice_list.html | 2 +- .../templates/treasury/invoice_sample.tex | 2 +- .../templates/treasury/remittance_list.html | 2 +- .../templates/treasury/sogecredit_list.html | 5 +- apps/treasury/tests/__init__.py | 0 apps/treasury/tests/test_treasury.py | 403 ++++++++++++++++++ apps/treasury/views.py | 17 +- note_kfet/static/js/transfer.js | 6 +- 20 files changed, 438 insertions(+), 31 deletions(-) rename {note_kfet => apps/treasury}/static/img/Finalist.png (100%) rename {note_kfet => apps/treasury}/static/img/Kataclist.png (100%) rename {note_kfet => apps/treasury}/static/img/Listorique.png (100%) rename {note_kfet => apps/treasury}/static/img/Monopolist.png (100%) rename {note_kfet => apps/treasury}/static/img/Saperlistpopette.png (100%) rename {note_kfet => apps/treasury}/static/img/Satellist.png (100%) create mode 100644 apps/treasury/tests/__init__.py create mode 100644 apps/treasury/tests/test_treasury.py diff --git a/apps/activity/templates/activity/activity_entry.html b/apps/activity/templates/activity/activity_entry.html index d59a4c48..d778490f 100644 --- a/apps/activity/templates/activity/activity_entry.html +++ b/apps/activity/templates/activity/activity_entry.html @@ -9,7 +9,7 @@ SPDX-License-Identifier: GPL-3.0-or-later

{{ title }}

-
+
{% trans "Transfer" %} diff --git a/apps/treasury/admin.py b/apps/treasury/admin.py index 1db820b2..25b4f4cb 100644 --- a/apps/treasury/admin.py +++ b/apps/treasury/admin.py @@ -24,9 +24,7 @@ class RemittanceAdmin(admin.ModelAdmin): list_display = ('remittance_type', 'date', 'comment', 'count', 'amount', 'closed', ) def has_change_permission(self, request, obj=None): - if not obj: - return True - return not obj.closed and super().has_change_permission(request, obj) + return not obj or (not obj.closed and super().has_change_permission(request, obj)) @admin.register(SogeCredit, site=admin_site) diff --git a/apps/treasury/api/views.py b/apps/treasury/api/views.py index ee97e6ac..82a0ed1e 100644 --- a/apps/treasury/api/views.py +++ b/apps/treasury/api/views.py @@ -16,7 +16,7 @@ class InvoiceViewSet(ReadProtectedModelViewSet): The djangorestframework plugin will get all `Invoice` objects, serialize it to JSON with the given serializer, then render it on /api/treasury/invoice/ """ - queryset = Invoice.objects.all() + queryset = Invoice.objects.order_by("id").all() serializer_class = InvoiceSerializer filter_backends = [DjangoFilterBackend] filterset_fields = ['bde', ] @@ -28,7 +28,7 @@ class ProductViewSet(ReadProtectedModelViewSet): The djangorestframework plugin will get all `Product` objects, serialize it to JSON with the given serializer, then render it on /api/treasury/product/ """ - queryset = Product.objects.all() + queryset = Product.objects.order_by("invoice_id", "id").all() serializer_class = ProductSerializer filter_backends = [SearchFilter] search_fields = ['$designation', ] @@ -40,7 +40,7 @@ class RemittanceTypeViewSet(ReadProtectedModelViewSet): The djangorestframework plugin will get all `RemittanceType` objects, serialize it to JSON with the given serializer then render it on /api/treasury/remittance_type/ """ - queryset = RemittanceType.objects + queryset = RemittanceType.objects.order_by("id") serializer_class = RemittanceTypeSerializer @@ -50,7 +50,7 @@ class RemittanceViewSet(ReadProtectedModelViewSet): The djangorestframework plugin will get all `Remittance` objects, serialize it to JSON with the given serializer, then render it on /api/treasury/remittance/ """ - queryset = Remittance.objects + queryset = Remittance.objects.order_by("id") serializer_class = RemittanceSerializer @@ -60,5 +60,5 @@ class SogeCreditViewSet(ReadProtectedModelViewSet): The djangorestframework plugin will get all `SogeCredit` objects, serialize it to JSON with the given serializer, then render it on /api/treasury/soge_credit/ """ - queryset = SogeCredit.objects + queryset = SogeCredit.objects.order_by("id") serializer_class = SogeCreditSerializer diff --git a/apps/treasury/forms.py b/apps/treasury/forms.py index 38da324d..c2461f76 100644 --- a/apps/treasury/forms.py +++ b/apps/treasury/forms.py @@ -16,21 +16,15 @@ class InvoiceForm(forms.ModelForm): """ def clean(self): + # If the invoice is locked, it can't be updated. if self.instance and self.instance.locked: for field_name in self.fields: self.cleaned_data[field_name] = getattr(self.instance, field_name) self.errors.clear() + self.add_error(None, _('This invoice is locked and can no longer be edited.')) return self.cleaned_data return super().clean() - def save(self, commit=True): - """ - If the invoice is locked, don't save it - """ - if not self.instance.locked: - super().save(commit) - return self.instance - class Meta: model = Invoice exclude = ('bde', 'date', 'tex', ) diff --git a/apps/treasury/models.py b/apps/treasury/models.py index 6d5b4021..762c7bb5 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -85,7 +85,7 @@ class Invoice(models.Model): old_invoice = Invoice.objects.filter(id=self.id) if old_invoice.exists(): - if old_invoice.get().locked: + if old_invoice.get().locked and not self._force_save: raise ValidationError(_("This invoice is locked and can no longer be edited.")) products = self.products.all() @@ -224,7 +224,7 @@ class Remittance(models.Model): def save(self, force_insert=False, force_update=False, using=None, update_fields=None): # Check if all transactions have the right type. - if self.transactions.filter(~Q(source=self.remittance_type.note)).exists(): + if self.transactions.exists() and self.transactions.filter(~Q(source=self.remittance_type.note)).exists(): raise ValidationError("All transactions in a remittance must have the same type") return super().save(force_insert, force_update, using, update_fields) diff --git a/note_kfet/static/img/Finalist.png b/apps/treasury/static/img/Finalist.png similarity index 100% rename from note_kfet/static/img/Finalist.png rename to apps/treasury/static/img/Finalist.png diff --git a/note_kfet/static/img/Kataclist.png b/apps/treasury/static/img/Kataclist.png similarity index 100% rename from note_kfet/static/img/Kataclist.png rename to apps/treasury/static/img/Kataclist.png diff --git a/note_kfet/static/img/Listorique.png b/apps/treasury/static/img/Listorique.png similarity index 100% rename from note_kfet/static/img/Listorique.png rename to apps/treasury/static/img/Listorique.png diff --git a/note_kfet/static/img/Monopolist.png b/apps/treasury/static/img/Monopolist.png similarity index 100% rename from note_kfet/static/img/Monopolist.png rename to apps/treasury/static/img/Monopolist.png diff --git a/note_kfet/static/img/Saperlistpopette.png b/apps/treasury/static/img/Saperlistpopette.png similarity index 100% rename from note_kfet/static/img/Saperlistpopette.png rename to apps/treasury/static/img/Saperlistpopette.png diff --git a/note_kfet/static/img/Satellist.png b/apps/treasury/static/img/Satellist.png similarity index 100% rename from note_kfet/static/img/Satellist.png rename to apps/treasury/static/img/Satellist.png diff --git a/apps/treasury/tables.py b/apps/treasury/tables.py index 14044f1c..9a72ecf3 100644 --- a/apps/treasury/tables.py +++ b/apps/treasury/tables.py @@ -34,7 +34,7 @@ class InvoiceTable(tables.Table): delete = tables.LinkColumn( 'treasury:invoice_delete', - args=[A('pk')], + args=[A('id')], verbose_name=_("delete"), text=_("Delete"), attrs={ diff --git a/apps/treasury/templates/treasury/invoice_list.html b/apps/treasury/templates/treasury/invoice_list.html index 32c1b1c1..d9cd8a3e 100644 --- a/apps/treasury/templates/treasury/invoice_list.html +++ b/apps/treasury/templates/treasury/invoice_list.html @@ -8,7 +8,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}
-
+
{% trans "Invoice" %}s diff --git a/apps/treasury/templates/treasury/invoice_sample.tex b/apps/treasury/templates/treasury/invoice_sample.tex index 4e6342b0..d7ec7391 100644 --- a/apps/treasury/templates/treasury/invoice_sample.tex +++ b/apps/treasury/templates/treasury/invoice_sample.tex @@ -58,7 +58,7 @@ \parbox[b][\paperheight]{\paperwidth}{% \vfill \centering - {\transparent{0.1}\includegraphics[width=\textwidth]{../../static/img/{{ obj.bde }}}}% + {\transparent{0.1}\includegraphics[width=\textwidth]{../../apps/treasury/static/img/{{ obj.bde }}}}% \vfill } } diff --git a/apps/treasury/templates/treasury/remittance_list.html b/apps/treasury/templates/treasury/remittance_list.html index c400f18f..8ced1ad0 100644 --- a/apps/treasury/templates/treasury/remittance_list.html +++ b/apps/treasury/templates/treasury/remittance_list.html @@ -8,7 +8,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}
-
+
{% trans "Invoice" %}s diff --git a/apps/treasury/templates/treasury/sogecredit_list.html b/apps/treasury/templates/treasury/sogecredit_list.html index c3862811..1eb1aba5 100644 --- a/apps/treasury/templates/treasury/sogecredit_list.html +++ b/apps/treasury/templates/treasury/sogecredit_list.html @@ -8,7 +8,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}
-
+
{% trans "Invoice" %}s @@ -59,9 +59,6 @@ SPDX-License-Identifier: GPL-3.0-or-later function reloadTable() { let pattern = searchbar_obj.val(); - if (pattern === old_pattern || pattern === "") - return; - $("#credits_table").load(location.pathname + "?search=" + pattern.replace(" ", "%20") + ( invalid_only_obj.is(':checked') ? "&valid=false" : "") + " #credits_table"); diff --git a/apps/treasury/tests/__init__.py b/apps/treasury/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/treasury/tests/test_treasury.py b/apps/treasury/tests/test_treasury.py new file mode 100644 index 00000000..15d35cb3 --- /dev/null +++ b/apps/treasury/tests/test_treasury.py @@ -0,0 +1,403 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError +from django.db.models import Q +from django.test import TestCase +from django.urls import reverse + +from member.models import Membership, Club +from note.models import SpecialTransaction, NoteSpecial, Transaction +from treasury.models import Invoice, Product, Remittance, RemittanceType, SogeCredit + + +class TestInvoices(TestCase): + """ + Check that invoices can be created and rendered properly. + """ + def setUp(self) -> None: + self.user = User.objects.create_superuser( + username="admintoto", + password="totototo", + email="admin@example.com", + ) + self.client.force_login(self.user) + sess = self.client.session + sess["permission_mask"] = 42 + sess.save() + + self.invoice = Invoice.objects.create( + id=1, + object="Object", + description="Description", + name="Me", + address="Earth", + acquitted=False, + ) + self.product = Product.objects.create( + invoice=self.invoice, + designation="Product", + quantity=3, + amount=3.14, + ) + + def test_admin_page(self): + """ + Display the invoice admin page. + """ + response = self.client.get(reverse("admin:index") + "treasury/invoice/") + self.assertEqual(response.status_code, 200) + + def test_invoices_list(self): + """ + Display the list of invoices. + """ + response = self.client.get(reverse("treasury:invoice_list")) + self.assertEqual(response.status_code, 200) + + def test_invoice_create(self): + """ + Try to create a new invoice. + """ + response = self.client.get(reverse("treasury:invoice_create")) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("treasury:invoice_create"), data={ + "id": 42, + "object": "Same object", + "description": "Longer description", + "name": "Me and others", + "address": "Alwways earth", + "acquitted": True, + "products-0-designation": "Designation", + "products-0-quantity": 1, + "products-0-amount": 42, + "products-TOTAL_FORMS": 1, + "products-INITIAL_FORMS": 0, + "products-MIN_NUM_FORMS": 0, + "products-MAX_NUM_FORMS": 1000, + }) + self.assertRedirects(response, reverse("treasury:invoice_list"), 302, 200) + self.assertTrue(Invoice.objects.filter(object="Same object", id=42).exists()) + self.assertTrue(Product.objects.filter(designation="Designation", invoice_id=42).exists()) + self.assertTrue(Invoice.objects.get(id=42).tex) + + def test_invoice_update(self): + """ + Try to update an invoice. + """ + response = self.client.get(reverse("treasury:invoice_update", args=(self.invoice.id,))) + self.assertEqual(response.status_code, 200) + + data = { + "object": "Same object", + "description": "Longer description", + "name": "Me and others", + "address": "Always earth", + "acquitted": True, + "locked": True, + "products-0-designation": "Designation", + "products-0-quantity": 1, + "products-0-amount": 4200, + "products-1-designation": "Second designation", + "products-1-quantity": 5, + "products-1-amount": -1800, + "products-TOTAL_FORMS": 2, + "products-INITIAL_FORMS": 0, + "products-MIN_NUM_FORMS": 0, + "products-MAX_NUM_FORMS": 1000, + } + + response = self.client.post(reverse("treasury:invoice_update", args=(self.invoice.id,)), data=data) + self.assertRedirects(response, reverse("treasury:invoice_list"), 302, 200) + self.invoice.refresh_from_db() + self.assertTrue(Invoice.objects.filter(pk=1, object="Same object", locked=True).exists()) + self.assertTrue(Product.objects.filter(designation="Second designation", invoice_id=1).exists()) + + # Resend the same data, but the invoice is locked. + response = self.client.get(reverse("treasury:invoice_update", args=(self.invoice.id,))) + self.assertTrue(response.status_code, 200) + response = self.client.post(reverse("treasury:invoice_update", args=(self.invoice.id,)), data=data) + self.assertTrue(response.status_code, 200) + + def test_delete_invoice(self): + """ + Try to delete an invoice. + """ + response = self.client.get(reverse("treasury:invoice_delete", args=(self.invoice.id,))) + self.assertEqual(response.status_code, 200) + + # Can't delete a locked invoice + self.invoice.locked = True + self.invoice.save() + response = self.client.delete(reverse("treasury:invoice_delete", args=(self.invoice.id,))) + self.assertEqual(response.status_code, 403) + self.assertTrue(Invoice.objects.filter(pk=self.invoice.id).exists()) + + # Unlock invoice and truly delete it. + self.invoice.locked = False + self.invoice._force_save = True + self.invoice.save() + response = self.client.delete(reverse("treasury:invoice_delete", args=(self.invoice.id,))) + self.assertRedirects(response, reverse("treasury:invoice_list"), 302, 200) + self.assertFalse(Invoice.objects.filter(pk=self.invoice.id).exists()) + + def test_invoice_render_pdf(self): + """ + Generate the PDF file of an invoice. + """ + response = self.client.get(reverse("treasury:invoice_render", args=(self.invoice.id,))) + self.assertEqual(response.status_code, 200) + + def test_invoice_api(self): + """ + Load some API pages + """ + response = self.client.get("/api/treasury/invoice/") + self.assertEqual(response.status_code, 200) + response = self.client.get("/api/treasury/product/") + self.assertEqual(response.status_code, 200) + + +class TestRemittances(TestCase): + """ + Create some credits and close remittances. + """ + + fixtures = ('initial',) + + def setUp(self) -> None: + self.user = User.objects.create_superuser( + username="admintoto", + password="totototo", + email="admin@example.com", + ) + self.client.force_login(self.user) + sess = self.client.session + sess["permission_mask"] = 42 + sess.save() + + self.credit = SpecialTransaction.objects.create( + source=NoteSpecial.objects.get(special_type="Chèque"), + destination=self.user.note, + amount=4200, + reason="Credit", + last_name="TOTO", + first_name="Toto", + bank="Société générale", + ) + + self.second_credit = SpecialTransaction.objects.create( + source=self.user.note, + destination=NoteSpecial.objects.get(special_type="Chèque"), + amount=424200, + reason="Second credit", + last_name="TOTO", + first_name="Toto", + bank="Société générale", + ) + + self.remittance = Remittance.objects.create( + remittance_type=RemittanceType.objects.get(), + comment="Test remittance", + closed=False, + ) + self.credit.specialtransactionproxy.remittance = self.remittance + self.credit.specialtransactionproxy.save() + + def test_admin_page(self): + """ + Load the admin page. + """ + response = self.client.get(reverse("admin:index") + "treasury/remittance/") + self.assertEqual(response.status_code, 200) + + def test_remittances_list(self): + """ + Display the remittance list. + :return: + """ + response = self.client.get(reverse("treasury:remittance_list")) + self.assertEqual(response.status_code, 200) + + def test_remittance_create(self): + """ + Create a new Remittance. + """ + response = self.client.get(reverse("treasury:remittance_create")) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("treasury:remittance_create"), data=dict( + remittance_type=RemittanceType.objects.get().pk, + comment="Created remittance", + )) + self.assertRedirects(response, reverse("treasury:remittance_list"), 302, 200) + self.assertTrue(Remittance.objects.filter(comment="Created remittance").exists()) + + def test_remittance_update(self): + """ + Update an existing remittance. + """ + response = self.client.get(reverse("treasury:remittance_update", args=(self.remittance.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("treasury:remittance_update", args=(self.remittance.pk,)), data=dict( + comment="Updated remittance", + )) + self.assertRedirects(response, reverse("treasury:remittance_list"), 302, 200) + self.assertTrue(Remittance.objects.filter(comment="Updated remittance").exists()) + + def test_remittance_close(self): + """ + Try to close an open remittance. + """ + response = self.client.get(reverse("treasury:remittance_update", args=(self.remittance.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("treasury:remittance_update", args=(self.remittance.pk,)), data=dict( + comment="Closed remittance", + close=True, + )) + self.assertRedirects(response, reverse("treasury:remittance_list"), 302, 200) + self.assertTrue(Remittance.objects.filter(comment="Closed remittance", closed=True).exists()) + + def test_remittance_link_transaction(self): + """ + Link a transaction to an open remittance. + """ + response = self.client.get(reverse("treasury:link_transaction", args=(self.credit.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("treasury:link_transaction", args=(self.credit.pk,)), data=dict( + remittance=self.remittance.pk, + last_name="Last Name", + first_name="First Name", + bank="Bank", + )) + self.assertRedirects(response, reverse("treasury:remittance_list"), 302, 200) + self.credit.refresh_from_db() + self.assertEqual(self.credit.last_name, "Last Name") + self.assertEqual(self.remittance.transactions.count(), 1) + + response = self.client.get(reverse("treasury:unlink_transaction", args=(self.credit.pk,))) + self.assertRedirects(response, reverse("treasury:remittance_list"), 302, 200) + + def test_invoice_api(self): + """ + Load some API pages + """ + response = self.client.get("/api/treasury/remittance_type/") + self.assertEqual(response.status_code, 200) + response = self.client.get("/api/treasury/remittance/") + self.assertEqual(response.status_code, 200) + + +class TestSogeCredits(TestCase): + """ + Check that credits from the Société générale are working correctly. + """ + + fixtures = ('initial',) + + def setUp(self) -> None: + self.user = User.objects.create_superuser( + username="admintoto", + password="totototo", + email="admin@example.com", + ) + self.client.force_login(self.user) + sess = self.client.session + sess["permission_mask"] = 42 + sess.save() + + self.kfet = Club.objects.get(name="Kfet") + self.bde = self.kfet.parent_club + + self.kfet_membership = Membership( + user=self.user, + club=self.kfet, + ) + self.kfet_membership._force_renew_parent = True + self.kfet_membership._soge = True + self.kfet_membership.save() + + def test_admin_page(self): + """ + Render the admin page. + """ + response = self.client.get(reverse("admin:index") + "treasury/sogecredit/") + self.assertEqual(response.status_code, 200) + + def test_sogecredit_list(self): + """ + Display the list of all credits. + """ + response = self.client.get(reverse("treasury:soge_credits")) + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("treasury:soge_credits") + "?search=toto&valid=") + self.assertEqual(response.status_code, 200) + + def test_validate_soge_credit(self): + """ + Try to validate a credit. + """ + soge_credit = SogeCredit.objects.get(user=self.user) + + response = self.client.get(reverse("treasury:manage_soge_credit", args=(soge_credit.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("treasury:manage_soge_credit", args=(soge_credit.pk,)), data=dict( + validate=True, + )) + self.assertRedirects(response, reverse("treasury:manage_soge_credit", args=(soge_credit.pk,)), 302, 200) + soge_credit.refresh_from_db() + self.assertTrue(soge_credit.valid) + self.user.note.refresh_from_db() + self.assertEqual(self.user.note.balance, 0) + self.assertEqual( + Transaction.objects.filter(Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3) + self.assertTrue(self.user.profile.soge) + + def test_delete_soge_credit(self): + """ + Try to invalidate a credit. + """ + soge_credit = SogeCredit.objects.get(user=self.user) + + response = self.client.get(reverse("treasury:manage_soge_credit", args=(soge_credit.pk,))) + self.assertEqual(response.status_code, 200) + + try: + self.client.post(reverse("treasury:manage_soge_credit", args=(soge_credit.pk,)), data=dict(delete=True)) + raise AssertionError("It is not possible to delete the soge credit until the note is not credited.") + except ValidationError: + pass + + SpecialTransaction.objects.create( + source=NoteSpecial.objects.get(special_type="Carte bancaire"), + destination=self.user.note, + amount=self.bde.membership_fee_paid + self.kfet.membership_fee_paid, + quantity=1, + reason="Registration is not complete, pliz pay", + last_name="TOTO", + first_name="Toto", + ) + + response = self.client.post(reverse("treasury:manage_soge_credit", args=(soge_credit.pk,)), + data=dict(delete=True)) + # 403 because no SogeCredit exists anymore, then a PermissionDenied is raised + self.assertRedirects(response, reverse("treasury:soge_credits"), 302, 403) + self.assertFalse(SogeCredit.objects.filter(pk=soge_credit.pk)) + self.user.note.refresh_from_db() + self.assertEqual(self.user.note.balance, 0) + self.assertEqual( + Transaction.objects.filter(Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3) + self.assertFalse(self.user.profile.soge) + + def test_invoice_api(self): + """ + Load some API pages + """ + response = self.client.get("/api/treasury/soge_credit/") + self.assertEqual(response.status_code, 200) diff --git a/apps/treasury/views.py b/apps/treasury/views.py index c2265289..5889f8b5 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -60,6 +60,11 @@ class InvoiceCreateView(ProtectQuerysetMixin, ProtectedCreateView): return context + def get_form(self, form_class=None): + form = super().get_form(form_class) + del form.fields["locked"] + return form + def form_valid(self, form): ret = super().form_valid(form) @@ -134,6 +139,11 @@ class InvoiceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): return context + def get_form(self, form_class=None): + form = super().get_form(form_class) + del form.fields["id"] + return form + def form_valid(self, form): ret = super().form_valid(form) @@ -165,6 +175,11 @@ class InvoiceDeleteView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView): model = Invoice extra_context = {"title": _("Delete invoice")} + def delete(self, request, *args, **kwargs): + if self.get_object().locked: + raise PermissionDenied(_("This invoice is locked and can't be deleted.")) + return super().delete(request, *args, **kwargs) + def get_success_url(self): return reverse_lazy('treasury:invoice_list') @@ -387,7 +402,7 @@ class SogeCreditListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableVi if not request.user.is_authenticated: return self.handle_no_permission() - if not self.get_queryset().exists(): + if not super().get_queryset().exists(): raise PermissionDenied(_("You are not able to see the treasury interface.")) return super().dispatch(request, *args, **kwargs) diff --git a/note_kfet/static/js/transfer.js b/note_kfet/static/js/transfer.js index cbae7456..e22d2b3f 100644 --- a/note_kfet/static/js/transfer.js +++ b/note_kfet/static/js/transfer.js @@ -96,7 +96,7 @@ $(document).ready(function() { let source = $("#source_note"); let dest = $("#dest_note"); - $("#type_transfer").click(function() { + $("#type_transfer").change(function() { if (LOCK) return; @@ -117,7 +117,7 @@ $(document).ready(function() { location.hash = "transfer"; }); - $("#type_credit").click(function() { + $("#type_credit").change(function() { if (LOCK) return; @@ -146,7 +146,7 @@ $(document).ready(function() { location.hash = "credit"; }); - $("#type_debit").click(function() { + $("#type_debit").change(function() { if (LOCK) return; From f71fb1fa81dd7a14d02b4512abe9dec532505df8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 4 Sep 2020 16:02:42 +0200 Subject: [PATCH 35/41] Use pre-defined queryset by default in API views --- apps/api/viewsets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/viewsets.py b/apps/api/viewsets.py index 333ae5e3..fa2fc941 100644 --- a/apps/api/viewsets.py +++ b/apps/api/viewsets.py @@ -26,7 +26,7 @@ class ReadProtectedModelViewSet(ModelViewSet): def get_queryset(self): user = self.request.user get_current_session().setdefault("permission_mask", 42) - return self.model.objects.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct() + return self.queryset.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct() class ReadOnlyProtectedModelViewSet(ReadOnlyModelViewSet): @@ -41,7 +41,7 @@ class ReadOnlyProtectedModelViewSet(ReadOnlyModelViewSet): def get_queryset(self): user = self.request.user get_current_session().setdefault("permission_mask", 42) - return self.model.objects.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct() + return self.queryset.filter(PermissionBackend.filter_queryset(user, self.model, "view")).distinct() class UserViewSet(ReadProtectedModelViewSet): From c93c81861d420dc423fc42a542ff1c1f93eb43f5 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 4 Sep 2020 16:28:50 +0200 Subject: [PATCH 36/41] Users can change their password, fix #59 --- apps/permission/fixtures/initial.json | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 120bb1ee..9012414d 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -2567,6 +2567,22 @@ "description": "(Dé)bloquer sa propre note et modifier la raison" } }, + { + "model": "permission.permission", + "pk": 165, + "fields": { + "model": [ + "auth", + "user" + ], + "query": "{}", + "type": "change", + "mask": 1, + "field": "password", + "permanent": true, + "description": "Changer son mot de passe" + } + }, { "model": "permission.role", "pk": 1, @@ -2591,7 +2607,8 @@ 52, 126, 161, - 162 + 162, + 165 ] } }, @@ -2932,7 +2949,8 @@ 161, 162, 163, - 164 + 164, + 165 ] } }, From 9b4923fc04e08ba8b00df01e4802088ddccb1aca Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 4 Sep 2020 16:37:17 +0200 Subject: [PATCH 37/41] Fix some permissions, grant temporary all treasurers to make transactions from anyone to anyone while a better system is not implemented --- apps/permission/fixtures/initial.json | 63 ++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 9012414d..cb9b8d04 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -2583,6 +2583,54 @@ "description": "Changer son mot de passe" } }, + { + "model": "permission.permission", + "pk": 166, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}}, {\"valid\": false}]", + "type": "add", + "mask": 2, + "field": "", + "permanent": false, + "description": "Créer une transaction quelconque tant que la source reste au-dessus de -50 €" + } + }, + { + "model": "permission.permission", + "pk": 167, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]", + "type": "change", + "mask": 2, + "field": "valid", + "permanent": false, + "description": "Modifier le statut de validation d'une transaction si c'est possible" + } + }, + { + "model": "permission.permission", + "pk": 168, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]", + "type": "change", + "mask": 2, + "field": "invalidity_reason", + "permanent": false, + "description": "Modifier la raison d'invalidité d'une transaction si c'est possible" + } + }, { "model": "permission.role", "pk": 1, @@ -2714,7 +2762,11 @@ 127, 133, 141, - 142 + 142, + 150, + 166, + 167, + 168 ] } }, @@ -2728,8 +2780,7 @@ 24, 25, 26, - 27, - 33 + 27 ] } }, @@ -2962,7 +3013,6 @@ "name": "GC Kfet", "permissions": [ 32, - 33, 56, 58, 55, @@ -2977,7 +3027,10 @@ 29, 30, 31, - 143 + 143, + 166, + 167, + 168 ] } }, From 5c7fe716adf71623fb5e43914ac618234761063f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 4 Sep 2020 16:43:57 +0200 Subject: [PATCH 38/41] Fix JSON --- apps/logs/signals.py | 5 +-- apps/permission/fixtures/initial.json | 2 +- .../tests/test_permission_queries.py | 35 ++++++++++--------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 2d443d13..89e7d05d 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -50,10 +50,7 @@ def save_object(sender, instance, **kwargs): in order to store each modification made """ # noinspection PyProtectedMember - if instance._meta.label_lower in EXCLUDED: - return - - if hasattr(instance, "_no_log"): + if instance._meta.label_lower in EXCLUDED or hasattr(instance, "_no_log"): return # noinspection PyProtectedMember diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index cb9b8d04..5f266788 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -2607,7 +2607,7 @@ "note", "transaction" ], - "query": "[\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]", + "query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]", "type": "change", "mask": 2, "field": "valid", diff --git a/apps/permission/tests/test_permission_queries.py b/apps/permission/tests/test_permission_queries.py index e0af9cf0..4d73ae11 100644 --- a/apps/permission/tests/test_permission_queries.py +++ b/apps/permission/tests/test_permission_queries.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later from datetime import date +from json.decoder import JSONDecodeError from django.contrib.auth.models import User from django.core.exceptions import FieldError @@ -56,29 +57,29 @@ class PermissionQueryTestCase(TestCase): We use a random user with a random WEIClub (to use permissions for the WEI) in a random team in a random bus. """ for perm in Permission.objects.all(): - instanced = perm.about( - user=User.objects.get(), - club=WEIClub.objects.get(), - membership=Membership.objects.get(), - User=User, - Club=Club, - Membership=Membership, - Note=Note, - NoteUser=NoteUser, - NoteClub=NoteClub, - NoteSpecial=NoteSpecial, - F=F, - Q=Q, - now=timezone.now(), - today=date.today(), - ) try: + instanced = perm.about( + user=User.objects.get(), + club=WEIClub.objects.get(), + membership=Membership.objects.get(), + User=User, + Club=Club, + Membership=Membership, + Note=Note, + NoteUser=NoteUser, + NoteClub=NoteClub, + NoteSpecial=NoteSpecial, + F=F, + Q=Q, + now=timezone.now(), + today=date.today(), + ) instanced.update_query() query = instanced.query model = perm.model.model_class() model.objects.filter(query).all() # print("Good query for permission", perm) - except (FieldError, AttributeError, ValueError, TypeError): + except (FieldError, AttributeError, ValueError, TypeError, JSONDecodeError): print("Query error for permission", perm) print("Query:", perm.query) if instanced.query: From 70e1a611dd1445758eaac14473bb17a7518ba128 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 4 Sep 2020 18:36:20 +0200 Subject: [PATCH 39/41] Export activites as an ICS Calendar --- apps/activity/urls.py | 1 + apps/activity/views.py | 627 ++++++++++++++++++++++------------------- apps/logs/signals.py | 5 +- 3 files changed, 346 insertions(+), 287 deletions(-) diff --git a/apps/activity/urls.py b/apps/activity/urls.py index f074e8f7..155229d4 100644 --- a/apps/activity/urls.py +++ b/apps/activity/urls.py @@ -14,4 +14,5 @@ urlpatterns = [ path('/entry/', views.ActivityEntryView.as_view(), name='activity_entry'), path('/update/', views.ActivityUpdateView.as_view(), name='activity_update'), path('new/', views.ActivityCreateView.as_view(), name='activity_create'), + path('calendar.ics', views.CalendarView.as_view(), name='calendar_ics'), ] diff --git a/apps/activity/views.py b/apps/activity/views.py index fd218db5..79934245 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -1,283 +1,344 @@ -# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay -# SPDX-License-Identifier: GPL-3.0-or-later - -from django.conf import settings -from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import PermissionDenied -from django.db.models import F, Q -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, TemplateView, UpdateView -from django_tables2.views import SingleTableView -from note.models import Alias, NoteSpecial, NoteUser -from permission.backends import PermissionBackend -from permission.views import ProtectQuerysetMixin, ProtectedCreateView - -from .forms import ActivityForm, GuestForm -from .models import Activity, Entry, Guest -from .tables import ActivityTable, EntryTable, GuestTable - - -class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView): - """ - View to create a new Activity - """ - model = Activity - form_class = ActivityForm - extra_context = {"title": _("Create new activity")} - - def get_sample_object(self): - return Activity( - name="", - description="", - creater=self.request.user, - activity_type_id=1, - organizer_id=1, - attendees_club_id=1, - date_start=timezone.now(), - date_end=timezone.now(), - ) - - def form_valid(self, form): - form.instance.creater = self.request.user - return super().form_valid(form) - - def get_success_url(self, **kwargs): - self.object.refresh_from_db() - return reverse_lazy('activity:activity_detail', kwargs={"pk": self.object.pk}) - - -class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): - """ - Displays all Activities, and classify if they are on-going or upcoming ones. - """ - model = Activity - table_class = ActivityTable - ordering = ('-date_start',) - extra_context = {"title": _("Activities")} - - def get_queryset(self): - return super().get_queryset().distinct() - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - upcoming_activities = Activity.objects.filter(date_end__gt=timezone.now()) - context['upcoming'] = ActivityTable( - data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")), - prefix='upcoming-', - ) - - started_activities = Activity.objects\ - .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ - .filter(open=True, valid=True).all() - context["started_activities"] = started_activities - - return context - - -class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): - """ - Shows details about one activity. Add guest to context - """ - model = Activity - context_object_name = "activity" - extra_context = {"title": _("Activity detail")} - - def get_context_data(self, **kwargs): - context = super().get_context_data() - - table = GuestTable(data=Guest.objects.filter(activity=self.object) - .filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))) - context["guests"] = table - - context["activity_started"] = timezone.now() > timezone.localtime(self.object.date_start) - - return context - - -class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): - """ - Updates one Activity - """ - model = Activity - form_class = ActivityForm - extra_context = {"title": _("Update activity")} - - def get_success_url(self, **kwargs): - return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) - - -class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView): - """ - Invite a Guest, The rules to invites someone are defined in `forms:activity.GuestForm` - """ - model = Guest - form_class = GuestForm - template_name = "activity/activity_form.html" - - def get_sample_object(self): - """ Creates a standart Guest binds to the Activity""" - activity = Activity.objects.get(pk=self.kwargs["pk"]) - return Guest( - activity=activity, - first_name="", - last_name="", - inviter=self.request.user.note, - ) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - activity = context["form"].activity - context["title"] = _('Invite guest to the activity "{}"').format(activity.name) - return context - - def get_form(self, form_class=None): - form = super().get_form(form_class) - form.activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ - .get(pk=self.kwargs["pk"]) - form.fields["inviter"].initial = self.request.user.note - return form - - def form_valid(self, form): - form.instance.activity = Activity.objects\ - .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).get(pk=self.kwargs["pk"]) - return super().form_valid(form) - - def get_success_url(self, **kwargs): - return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) - - -class ActivityEntryView(LoginRequiredMixin, TemplateView): - """ - Manages entry to an activity - """ - template_name = "activity/activity_entry.html" - - def dispatch(self, request, *args, **kwargs): - """ - Don't display the entry interface if the user has no right to see it (no right to add an entry for itself), - it is closed or doesn't manage entries. - """ - activity = Activity.objects.get(pk=self.kwargs["pk"]) - - sample_entry = Entry(activity=activity, note=self.request.user.note) - if not PermissionBackend.check_perm(self.request.user, "activity.add_entry", sample_entry): - raise PermissionDenied(_("You are not allowed to display the entry interface for this activity.")) - - if not activity.activity_type.manage_entries: - raise PermissionDenied(_("This activity does not support activity entries.")) - - if not activity.open: - raise PermissionDenied(_("This activity is closed.")) - return super().dispatch(request, *args, **kwargs) - - def get_invited_guest(self, activity): - """ - Retrieves all Guests to the activity - """ - - guest_qs = Guest.objects\ - .annotate(balance=F("inviter__balance"), note_name=F("inviter__user__username"))\ - .filter(activity=activity)\ - .filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))\ - .order_by('last_name', 'first_name').distinct() - - if "search" in self.request.GET and self.request.GET["search"]: - pattern = self.request.GET["search"] - if pattern[0] != "^": - pattern = "^" + pattern - guest_qs = guest_qs.filter( - Q(first_name__regex=pattern) - | Q(last_name__regex=pattern) - | Q(inviter__alias__name__regex=pattern) - | Q(inviter__alias__normalized_name__regex=Alias.normalize(pattern)) - ) - else: - guest_qs = guest_qs.none() - return guest_qs - - def get_invited_note(self, activity): - """ - Retrieves all Note that can attend the activity, - they need to have an up-to-date membership in the attendees_club. - """ - note_qs = Alias.objects.annotate(last_name=F("note__noteuser__user__last_name"), - first_name=F("note__noteuser__user__first_name"), - username=F("note__noteuser__user__username"), - note_name=F("name"), - balance=F("note__balance")) - - # Keep only users that have a note - note_qs = note_qs.filter(note__noteuser__isnull=False) - - # Keep only members - note_qs = note_qs.filter( - note__noteuser__user__memberships__club=activity.attendees_club, - note__noteuser__user__memberships__date_start__lte=timezone.now(), - note__noteuser__user__memberships__date_end__gte=timezone.now(), - ) - - # Filter with permission backend - note_qs = note_qs.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")) - - if "search" in self.request.GET and self.request.GET["search"]: - pattern = self.request.GET["search"] - note_qs = note_qs.filter( - Q(note__noteuser__user__first_name__regex=pattern) - | Q(note__noteuser__user__last_name__regex=pattern) - | Q(name__regex=pattern) - | Q(normalized_name__regex=Alias.normalize(pattern)) - ) - else: - note_qs = note_qs.none() - - if settings.DATABASES[note_qs.db]["ENGINE"] == 'django.db.backends.postgresql': - note_qs = note_qs.distinct('note__pk')[:20] - else: - # SQLite doesn't support distinct fields. For compatibility reason (in dev mode), the note list will only - # have distinct aliases rather than distinct notes with a SQLite DB, but it can fill the result page. - # In production mode, please use PostgreSQL. - note_qs = note_qs.distinct()[:20] - return note_qs - - def get_context_data(self, **kwargs): - """ - Query the list of Guest and Note to the activity and add information to makes entry with JS. - """ - context = super().get_context_data(**kwargs) - - activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ - .distinct().get(pk=self.kwargs["pk"]) - context["activity"] = activity - - matched = [] - - for guest in self.get_invited_guest(activity): - guest.type = "Invité" - matched.append(guest) - - for note in self.get_invited_note(activity): - note.type = "Adhérent" - note.activity = activity - matched.append(note) - - table = EntryTable(data=matched) - context["table"] = table - - context["entries"] = Entry.objects.filter(activity=activity) - - context["title"] = _('Entry for activity "{}"').format(activity.name) - context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk - context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk - - activities_open = Activity.objects.filter(open=True).filter( - PermissionBackend.filter_queryset(self.request.user, Activity, "view")).distinct().all() - context["activities_open"] = [a for a in activities_open - if PermissionBackend.check_perm(self.request.user, - "activity.add_entry", - Entry(activity=a, note=self.request.user.note,))] - - return context +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later +from hashlib import md5 + +from django.conf import settings +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import PermissionDenied +from django.db.models import F, Q +from django.http import HttpResponse +from django.urls import reverse_lazy +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from django.views import View +from django.views.generic import DetailView, TemplateView, UpdateView +from django_tables2.views import SingleTableView +from note.models import Alias, NoteSpecial, NoteUser +from permission.backends import PermissionBackend +from permission.views import ProtectQuerysetMixin, ProtectedCreateView + +from .forms import ActivityForm, GuestForm +from .models import Activity, Entry, Guest +from .tables import ActivityTable, EntryTable, GuestTable + + +class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView): + """ + View to create a new Activity + """ + model = Activity + form_class = ActivityForm + extra_context = {"title": _("Create new activity")} + + def get_sample_object(self): + return Activity( + name="", + description="", + creater=self.request.user, + activity_type_id=1, + organizer_id=1, + attendees_club_id=1, + date_start=timezone.now(), + date_end=timezone.now(), + ) + + def form_valid(self, form): + form.instance.creater = self.request.user + return super().form_valid(form) + + def get_success_url(self, **kwargs): + self.object.refresh_from_db() + return reverse_lazy('activity:activity_detail', kwargs={"pk": self.object.pk}) + + +class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): + """ + Displays all Activities, and classify if they are on-going or upcoming ones. + """ + model = Activity + table_class = ActivityTable + ordering = ('-date_start',) + extra_context = {"title": _("Activities")} + + def get_queryset(self): + return super().get_queryset().distinct() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + upcoming_activities = Activity.objects.filter(date_end__gt=timezone.now()) + context['upcoming'] = ActivityTable( + data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")), + prefix='upcoming-', + ) + + started_activities = Activity.objects\ + .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ + .filter(open=True, valid=True).all() + context["started_activities"] = started_activities + + return context + + +class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): + """ + Shows details about one activity. Add guest to context + """ + model = Activity + context_object_name = "activity" + extra_context = {"title": _("Activity detail")} + + def get_context_data(self, **kwargs): + context = super().get_context_data() + + table = GuestTable(data=Guest.objects.filter(activity=self.object) + .filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))) + context["guests"] = table + + context["activity_started"] = timezone.now() > timezone.localtime(self.object.date_start) + + return context + + +class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): + """ + Updates one Activity + """ + model = Activity + form_class = ActivityForm + extra_context = {"title": _("Update activity")} + + def get_success_url(self, **kwargs): + return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) + + +class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView): + """ + Invite a Guest, The rules to invites someone are defined in `forms:activity.GuestForm` + """ + model = Guest + form_class = GuestForm + template_name = "activity/activity_form.html" + + def get_sample_object(self): + """ Creates a standart Guest binds to the Activity""" + activity = Activity.objects.get(pk=self.kwargs["pk"]) + return Guest( + activity=activity, + first_name="", + last_name="", + inviter=self.request.user.note, + ) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + activity = context["form"].activity + context["title"] = _('Invite guest to the activity "{}"').format(activity.name) + return context + + def get_form(self, form_class=None): + form = super().get_form(form_class) + form.activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ + .get(pk=self.kwargs["pk"]) + form.fields["inviter"].initial = self.request.user.note + return form + + def form_valid(self, form): + form.instance.activity = Activity.objects\ + .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).get(pk=self.kwargs["pk"]) + return super().form_valid(form) + + def get_success_url(self, **kwargs): + return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) + + +class ActivityEntryView(LoginRequiredMixin, TemplateView): + """ + Manages entry to an activity + """ + template_name = "activity/activity_entry.html" + + def dispatch(self, request, *args, **kwargs): + """ + Don't display the entry interface if the user has no right to see it (no right to add an entry for itself), + it is closed or doesn't manage entries. + """ + activity = Activity.objects.get(pk=self.kwargs["pk"]) + + sample_entry = Entry(activity=activity, note=self.request.user.note) + if not PermissionBackend.check_perm(self.request.user, "activity.add_entry", sample_entry): + raise PermissionDenied(_("You are not allowed to display the entry interface for this activity.")) + + if not activity.activity_type.manage_entries: + raise PermissionDenied(_("This activity does not support activity entries.")) + + if not activity.open: + raise PermissionDenied(_("This activity is closed.")) + return super().dispatch(request, *args, **kwargs) + + def get_invited_guest(self, activity): + """ + Retrieves all Guests to the activity + """ + + guest_qs = Guest.objects\ + .annotate(balance=F("inviter__balance"), note_name=F("inviter__user__username"))\ + .filter(activity=activity)\ + .filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))\ + .order_by('last_name', 'first_name').distinct() + + if "search" in self.request.GET and self.request.GET["search"]: + pattern = self.request.GET["search"] + if pattern[0] != "^": + pattern = "^" + pattern + guest_qs = guest_qs.filter( + Q(first_name__regex=pattern) + | Q(last_name__regex=pattern) + | Q(inviter__alias__name__regex=pattern) + | Q(inviter__alias__normalized_name__regex=Alias.normalize(pattern)) + ) + else: + guest_qs = guest_qs.none() + return guest_qs + + def get_invited_note(self, activity): + """ + Retrieves all Note that can attend the activity, + they need to have an up-to-date membership in the attendees_club. + """ + note_qs = Alias.objects.annotate(last_name=F("note__noteuser__user__last_name"), + first_name=F("note__noteuser__user__first_name"), + username=F("note__noteuser__user__username"), + note_name=F("name"), + balance=F("note__balance")) + + # Keep only users that have a note + note_qs = note_qs.filter(note__noteuser__isnull=False) + + # Keep only members + note_qs = note_qs.filter( + note__noteuser__user__memberships__club=activity.attendees_club, + note__noteuser__user__memberships__date_start__lte=timezone.now(), + note__noteuser__user__memberships__date_end__gte=timezone.now(), + ) + + # Filter with permission backend + note_qs = note_qs.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")) + + if "search" in self.request.GET and self.request.GET["search"]: + pattern = self.request.GET["search"] + note_qs = note_qs.filter( + Q(note__noteuser__user__first_name__regex=pattern) + | Q(note__noteuser__user__last_name__regex=pattern) + | Q(name__regex=pattern) + | Q(normalized_name__regex=Alias.normalize(pattern)) + ) + else: + note_qs = note_qs.none() + + if settings.DATABASES[note_qs.db]["ENGINE"] == 'django.db.backends.postgresql': + note_qs = note_qs.distinct('note__pk')[:20] + else: + # SQLite doesn't support distinct fields. For compatibility reason (in dev mode), the note list will only + # have distinct aliases rather than distinct notes with a SQLite DB, but it can fill the result page. + # In production mode, please use PostgreSQL. + note_qs = note_qs.distinct()[:20] + return note_qs + + def get_context_data(self, **kwargs): + """ + Query the list of Guest and Note to the activity and add information to makes entry with JS. + """ + context = super().get_context_data(**kwargs) + + activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ + .distinct().get(pk=self.kwargs["pk"]) + context["activity"] = activity + + matched = [] + + for guest in self.get_invited_guest(activity): + guest.type = "Invité" + matched.append(guest) + + for note in self.get_invited_note(activity): + note.type = "Adhérent" + note.activity = activity + matched.append(note) + + table = EntryTable(data=matched) + context["table"] = table + + context["entries"] = Entry.objects.filter(activity=activity) + + context["title"] = _('Entry for activity "{}"').format(activity.name) + context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk + context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk + + activities_open = Activity.objects.filter(open=True).filter( + PermissionBackend.filter_queryset(self.request.user, Activity, "view")).distinct().all() + context["activities_open"] = [a for a in activities_open + if PermissionBackend.check_perm(self.request.user, + "activity.add_entry", + Entry(activity=a, note=self.request.user.note,))] + + return context + + +class CalendarView(View): + """ + Render an ICS calendar with all valid activities. + """ + + def multilines(self, string, maxlength, offset=0): + newstring = string[:maxlength - offset] + string = string[maxlength - offset:] + while string: + newstring += "\r\n " + newstring += string[:maxlength - 1] + string = string[maxlength - 1:] + return newstring + + def get(self, request, *args, **kwargs): + ics = """BEGIN:VCALENDAR +VERSION: 2.0 +PRODID:Note Kfet 2020 +X-WR-CALNAME:Kfet Calendar +NAME:Kfet Calendar +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Europe/Berlin +TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin +X-LIC-LOCATION:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:CEST +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:CET +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +""" + for activity in Activity.objects.filter(valid=True).order_by("-date_start").all(): + ics += f"""BEGIN:VEVENT +DTSTAMP:{"{:%Y%m%dT%H%M%S}".format(activity.date_start)}Z +UID:{activity.id} +SUMMARY;CHARSET=UTF-8:{self.multilines(activity.name, 75, 22)} +DTSTART;TZID=Europe/Berlin:{"{:%Y%m%dT%H%M%S}".format(activity.date_start)} +DTEND;TZID=Europe/Berlin:{"{:%Y%m%dT%H%M%S}".format(activity.date_end)} +LOCATION:{self.multilines(activity.location, 75, 9) if activity.location else "Kfet"} +DESCRIPTION;CHARSET=UTF-8:{self.multilines(activity.description, 75, 26)} + -- {activity.organizer.name} +END:VEVENT +""" + ics += "END:VCALENDAR" + ics = ics.replace("\r", "").replace("\n", "\r\n") + return HttpResponse(ics, content_type="text/calendar; charset=UTF-8") diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 89e7d05d..e58ba7c1 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -117,10 +117,7 @@ def delete_object(sender, instance, **kwargs): Each time a model is deleted, an entry in the table `Changelog` is added in the database """ # noinspection PyProtectedMember - if instance._meta.label_lower in EXCLUDED: - return - - if hasattr(instance, "_no_log"): + if instance._meta.label_lower in EXCLUDED or hasattr(instance, "_no_log"): return # Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP From 6d1b75b9b67d21a0fa2c37be36e9e94808aeca27 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 4 Sep 2020 19:24:48 +0200 Subject: [PATCH 40/41] Fix linebreaks in ICS file --- apps/activity/views.py | 689 +++++++++++++++++++++-------------------- 1 file changed, 345 insertions(+), 344 deletions(-) diff --git a/apps/activity/views.py b/apps/activity/views.py index 79934245..f6dae500 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -1,344 +1,345 @@ -# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay -# SPDX-License-Identifier: GPL-3.0-or-later -from hashlib import md5 - -from django.conf import settings -from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import PermissionDenied -from django.db.models import F, Q -from django.http import HttpResponse -from django.urls import reverse_lazy -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ -from django.views import View -from django.views.generic import DetailView, TemplateView, UpdateView -from django_tables2.views import SingleTableView -from note.models import Alias, NoteSpecial, NoteUser -from permission.backends import PermissionBackend -from permission.views import ProtectQuerysetMixin, ProtectedCreateView - -from .forms import ActivityForm, GuestForm -from .models import Activity, Entry, Guest -from .tables import ActivityTable, EntryTable, GuestTable - - -class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView): - """ - View to create a new Activity - """ - model = Activity - form_class = ActivityForm - extra_context = {"title": _("Create new activity")} - - def get_sample_object(self): - return Activity( - name="", - description="", - creater=self.request.user, - activity_type_id=1, - organizer_id=1, - attendees_club_id=1, - date_start=timezone.now(), - date_end=timezone.now(), - ) - - def form_valid(self, form): - form.instance.creater = self.request.user - return super().form_valid(form) - - def get_success_url(self, **kwargs): - self.object.refresh_from_db() - return reverse_lazy('activity:activity_detail', kwargs={"pk": self.object.pk}) - - -class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): - """ - Displays all Activities, and classify if they are on-going or upcoming ones. - """ - model = Activity - table_class = ActivityTable - ordering = ('-date_start',) - extra_context = {"title": _("Activities")} - - def get_queryset(self): - return super().get_queryset().distinct() - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - upcoming_activities = Activity.objects.filter(date_end__gt=timezone.now()) - context['upcoming'] = ActivityTable( - data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")), - prefix='upcoming-', - ) - - started_activities = Activity.objects\ - .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ - .filter(open=True, valid=True).all() - context["started_activities"] = started_activities - - return context - - -class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): - """ - Shows details about one activity. Add guest to context - """ - model = Activity - context_object_name = "activity" - extra_context = {"title": _("Activity detail")} - - def get_context_data(self, **kwargs): - context = super().get_context_data() - - table = GuestTable(data=Guest.objects.filter(activity=self.object) - .filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))) - context["guests"] = table - - context["activity_started"] = timezone.now() > timezone.localtime(self.object.date_start) - - return context - - -class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): - """ - Updates one Activity - """ - model = Activity - form_class = ActivityForm - extra_context = {"title": _("Update activity")} - - def get_success_url(self, **kwargs): - return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) - - -class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView): - """ - Invite a Guest, The rules to invites someone are defined in `forms:activity.GuestForm` - """ - model = Guest - form_class = GuestForm - template_name = "activity/activity_form.html" - - def get_sample_object(self): - """ Creates a standart Guest binds to the Activity""" - activity = Activity.objects.get(pk=self.kwargs["pk"]) - return Guest( - activity=activity, - first_name="", - last_name="", - inviter=self.request.user.note, - ) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - activity = context["form"].activity - context["title"] = _('Invite guest to the activity "{}"').format(activity.name) - return context - - def get_form(self, form_class=None): - form = super().get_form(form_class) - form.activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ - .get(pk=self.kwargs["pk"]) - form.fields["inviter"].initial = self.request.user.note - return form - - def form_valid(self, form): - form.instance.activity = Activity.objects\ - .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).get(pk=self.kwargs["pk"]) - return super().form_valid(form) - - def get_success_url(self, **kwargs): - return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) - - -class ActivityEntryView(LoginRequiredMixin, TemplateView): - """ - Manages entry to an activity - """ - template_name = "activity/activity_entry.html" - - def dispatch(self, request, *args, **kwargs): - """ - Don't display the entry interface if the user has no right to see it (no right to add an entry for itself), - it is closed or doesn't manage entries. - """ - activity = Activity.objects.get(pk=self.kwargs["pk"]) - - sample_entry = Entry(activity=activity, note=self.request.user.note) - if not PermissionBackend.check_perm(self.request.user, "activity.add_entry", sample_entry): - raise PermissionDenied(_("You are not allowed to display the entry interface for this activity.")) - - if not activity.activity_type.manage_entries: - raise PermissionDenied(_("This activity does not support activity entries.")) - - if not activity.open: - raise PermissionDenied(_("This activity is closed.")) - return super().dispatch(request, *args, **kwargs) - - def get_invited_guest(self, activity): - """ - Retrieves all Guests to the activity - """ - - guest_qs = Guest.objects\ - .annotate(balance=F("inviter__balance"), note_name=F("inviter__user__username"))\ - .filter(activity=activity)\ - .filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))\ - .order_by('last_name', 'first_name').distinct() - - if "search" in self.request.GET and self.request.GET["search"]: - pattern = self.request.GET["search"] - if pattern[0] != "^": - pattern = "^" + pattern - guest_qs = guest_qs.filter( - Q(first_name__regex=pattern) - | Q(last_name__regex=pattern) - | Q(inviter__alias__name__regex=pattern) - | Q(inviter__alias__normalized_name__regex=Alias.normalize(pattern)) - ) - else: - guest_qs = guest_qs.none() - return guest_qs - - def get_invited_note(self, activity): - """ - Retrieves all Note that can attend the activity, - they need to have an up-to-date membership in the attendees_club. - """ - note_qs = Alias.objects.annotate(last_name=F("note__noteuser__user__last_name"), - first_name=F("note__noteuser__user__first_name"), - username=F("note__noteuser__user__username"), - note_name=F("name"), - balance=F("note__balance")) - - # Keep only users that have a note - note_qs = note_qs.filter(note__noteuser__isnull=False) - - # Keep only members - note_qs = note_qs.filter( - note__noteuser__user__memberships__club=activity.attendees_club, - note__noteuser__user__memberships__date_start__lte=timezone.now(), - note__noteuser__user__memberships__date_end__gte=timezone.now(), - ) - - # Filter with permission backend - note_qs = note_qs.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")) - - if "search" in self.request.GET and self.request.GET["search"]: - pattern = self.request.GET["search"] - note_qs = note_qs.filter( - Q(note__noteuser__user__first_name__regex=pattern) - | Q(note__noteuser__user__last_name__regex=pattern) - | Q(name__regex=pattern) - | Q(normalized_name__regex=Alias.normalize(pattern)) - ) - else: - note_qs = note_qs.none() - - if settings.DATABASES[note_qs.db]["ENGINE"] == 'django.db.backends.postgresql': - note_qs = note_qs.distinct('note__pk')[:20] - else: - # SQLite doesn't support distinct fields. For compatibility reason (in dev mode), the note list will only - # have distinct aliases rather than distinct notes with a SQLite DB, but it can fill the result page. - # In production mode, please use PostgreSQL. - note_qs = note_qs.distinct()[:20] - return note_qs - - def get_context_data(self, **kwargs): - """ - Query the list of Guest and Note to the activity and add information to makes entry with JS. - """ - context = super().get_context_data(**kwargs) - - activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ - .distinct().get(pk=self.kwargs["pk"]) - context["activity"] = activity - - matched = [] - - for guest in self.get_invited_guest(activity): - guest.type = "Invité" - matched.append(guest) - - for note in self.get_invited_note(activity): - note.type = "Adhérent" - note.activity = activity - matched.append(note) - - table = EntryTable(data=matched) - context["table"] = table - - context["entries"] = Entry.objects.filter(activity=activity) - - context["title"] = _('Entry for activity "{}"').format(activity.name) - context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk - context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk - - activities_open = Activity.objects.filter(open=True).filter( - PermissionBackend.filter_queryset(self.request.user, Activity, "view")).distinct().all() - context["activities_open"] = [a for a in activities_open - if PermissionBackend.check_perm(self.request.user, - "activity.add_entry", - Entry(activity=a, note=self.request.user.note,))] - - return context - - -class CalendarView(View): - """ - Render an ICS calendar with all valid activities. - """ - - def multilines(self, string, maxlength, offset=0): - newstring = string[:maxlength - offset] - string = string[maxlength - offset:] - while string: - newstring += "\r\n " - newstring += string[:maxlength - 1] - string = string[maxlength - 1:] - return newstring - - def get(self, request, *args, **kwargs): - ics = """BEGIN:VCALENDAR -VERSION: 2.0 -PRODID:Note Kfet 2020 -X-WR-CALNAME:Kfet Calendar -NAME:Kfet Calendar -CALSCALE:GREGORIAN -BEGIN:VTIMEZONE -TZID:Europe/Berlin -TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin -X-LIC-LOCATION:Europe/Berlin -BEGIN:DAYLIGHT -TZOFFSETFROM:+0100 -TZOFFSETTO:+0200 -TZNAME:CEST -DTSTART:19700329T020000 -RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU -END:DAYLIGHT -BEGIN:STANDARD -TZOFFSETFROM:+0200 -TZOFFSETTO:+0100 -TZNAME:CET -DTSTART:19701025T030000 -RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU -END:STANDARD -END:VTIMEZONE -""" - for activity in Activity.objects.filter(valid=True).order_by("-date_start").all(): - ics += f"""BEGIN:VEVENT -DTSTAMP:{"{:%Y%m%dT%H%M%S}".format(activity.date_start)}Z -UID:{activity.id} -SUMMARY;CHARSET=UTF-8:{self.multilines(activity.name, 75, 22)} -DTSTART;TZID=Europe/Berlin:{"{:%Y%m%dT%H%M%S}".format(activity.date_start)} -DTEND;TZID=Europe/Berlin:{"{:%Y%m%dT%H%M%S}".format(activity.date_end)} -LOCATION:{self.multilines(activity.location, 75, 9) if activity.location else "Kfet"} -DESCRIPTION;CHARSET=UTF-8:{self.multilines(activity.description, 75, 26)} - -- {activity.organizer.name} -END:VEVENT -""" - ics += "END:VCALENDAR" - ics = ics.replace("\r", "").replace("\n", "\r\n") - return HttpResponse(ics, content_type="text/calendar; charset=UTF-8") +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later +from hashlib import md5 +from random import randint + +from django.conf import settings +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import PermissionDenied +from django.db.models import F, Q +from django.http import HttpResponse +from django.urls import reverse_lazy +from django.utils import timezone +from django.utils.crypto import get_random_string +from django.utils.translation import gettext_lazy as _ +from django.views import View +from django.views.generic import DetailView, TemplateView, UpdateView +from django_tables2.views import SingleTableView +from note.models import Alias, NoteSpecial, NoteUser +from permission.backends import PermissionBackend +from permission.views import ProtectQuerysetMixin, ProtectedCreateView + +from .forms import ActivityForm, GuestForm +from .models import Activity, Entry, Guest +from .tables import ActivityTable, EntryTable, GuestTable + + +class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView): + """ + View to create a new Activity + """ + model = Activity + form_class = ActivityForm + extra_context = {"title": _("Create new activity")} + + def get_sample_object(self): + return Activity( + name="", + description="", + creater=self.request.user, + activity_type_id=1, + organizer_id=1, + attendees_club_id=1, + date_start=timezone.now(), + date_end=timezone.now(), + ) + + def form_valid(self, form): + form.instance.creater = self.request.user + return super().form_valid(form) + + def get_success_url(self, **kwargs): + self.object.refresh_from_db() + return reverse_lazy('activity:activity_detail', kwargs={"pk": self.object.pk}) + + +class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): + """ + Displays all Activities, and classify if they are on-going or upcoming ones. + """ + model = Activity + table_class = ActivityTable + ordering = ('-date_start',) + extra_context = {"title": _("Activities")} + + def get_queryset(self): + return super().get_queryset().distinct() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + upcoming_activities = Activity.objects.filter(date_end__gt=timezone.now()) + context['upcoming'] = ActivityTable( + data=upcoming_activities.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")), + prefix='upcoming-', + ) + + started_activities = Activity.objects\ + .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ + .filter(open=True, valid=True).all() + context["started_activities"] = started_activities + + return context + + +class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): + """ + Shows details about one activity. Add guest to context + """ + model = Activity + context_object_name = "activity" + extra_context = {"title": _("Activity detail")} + + def get_context_data(self, **kwargs): + context = super().get_context_data() + + table = GuestTable(data=Guest.objects.filter(activity=self.object) + .filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))) + context["guests"] = table + + context["activity_started"] = timezone.now() > timezone.localtime(self.object.date_start) + + return context + + +class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): + """ + Updates one Activity + """ + model = Activity + form_class = ActivityForm + extra_context = {"title": _("Update activity")} + + def get_success_url(self, **kwargs): + return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) + + +class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView): + """ + Invite a Guest, The rules to invites someone are defined in `forms:activity.GuestForm` + """ + model = Guest + form_class = GuestForm + template_name = "activity/activity_form.html" + + def get_sample_object(self): + """ Creates a standart Guest binds to the Activity""" + activity = Activity.objects.get(pk=self.kwargs["pk"]) + return Guest( + activity=activity, + first_name="", + last_name="", + inviter=self.request.user.note, + ) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + activity = context["form"].activity + context["title"] = _('Invite guest to the activity "{}"').format(activity.name) + return context + + def get_form(self, form_class=None): + form = super().get_form(form_class) + form.activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ + .get(pk=self.kwargs["pk"]) + form.fields["inviter"].initial = self.request.user.note + return form + + def form_valid(self, form): + form.instance.activity = Activity.objects\ + .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).get(pk=self.kwargs["pk"]) + return super().form_valid(form) + + def get_success_url(self, **kwargs): + return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) + + +class ActivityEntryView(LoginRequiredMixin, TemplateView): + """ + Manages entry to an activity + """ + template_name = "activity/activity_entry.html" + + def dispatch(self, request, *args, **kwargs): + """ + Don't display the entry interface if the user has no right to see it (no right to add an entry for itself), + it is closed or doesn't manage entries. + """ + activity = Activity.objects.get(pk=self.kwargs["pk"]) + + sample_entry = Entry(activity=activity, note=self.request.user.note) + if not PermissionBackend.check_perm(self.request.user, "activity.add_entry", sample_entry): + raise PermissionDenied(_("You are not allowed to display the entry interface for this activity.")) + + if not activity.activity_type.manage_entries: + raise PermissionDenied(_("This activity does not support activity entries.")) + + if not activity.open: + raise PermissionDenied(_("This activity is closed.")) + return super().dispatch(request, *args, **kwargs) + + def get_invited_guest(self, activity): + """ + Retrieves all Guests to the activity + """ + + guest_qs = Guest.objects\ + .annotate(balance=F("inviter__balance"), note_name=F("inviter__user__username"))\ + .filter(activity=activity)\ + .filter(PermissionBackend.filter_queryset(self.request.user, Guest, "view"))\ + .order_by('last_name', 'first_name').distinct() + + if "search" in self.request.GET and self.request.GET["search"]: + pattern = self.request.GET["search"] + if pattern[0] != "^": + pattern = "^" + pattern + guest_qs = guest_qs.filter( + Q(first_name__regex=pattern) + | Q(last_name__regex=pattern) + | Q(inviter__alias__name__regex=pattern) + | Q(inviter__alias__normalized_name__regex=Alias.normalize(pattern)) + ) + else: + guest_qs = guest_qs.none() + return guest_qs + + def get_invited_note(self, activity): + """ + Retrieves all Note that can attend the activity, + they need to have an up-to-date membership in the attendees_club. + """ + note_qs = Alias.objects.annotate(last_name=F("note__noteuser__user__last_name"), + first_name=F("note__noteuser__user__first_name"), + username=F("note__noteuser__user__username"), + note_name=F("name"), + balance=F("note__balance")) + + # Keep only users that have a note + note_qs = note_qs.filter(note__noteuser__isnull=False) + + # Keep only members + note_qs = note_qs.filter( + note__noteuser__user__memberships__club=activity.attendees_club, + note__noteuser__user__memberships__date_start__lte=timezone.now(), + note__noteuser__user__memberships__date_end__gte=timezone.now(), + ) + + # Filter with permission backend + note_qs = note_qs.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")) + + if "search" in self.request.GET and self.request.GET["search"]: + pattern = self.request.GET["search"] + note_qs = note_qs.filter( + Q(note__noteuser__user__first_name__regex=pattern) + | Q(note__noteuser__user__last_name__regex=pattern) + | Q(name__regex=pattern) + | Q(normalized_name__regex=Alias.normalize(pattern)) + ) + else: + note_qs = note_qs.none() + + if settings.DATABASES[note_qs.db]["ENGINE"] == 'django.db.backends.postgresql': + note_qs = note_qs.distinct('note__pk')[:20] + else: + # SQLite doesn't support distinct fields. For compatibility reason (in dev mode), the note list will only + # have distinct aliases rather than distinct notes with a SQLite DB, but it can fill the result page. + # In production mode, please use PostgreSQL. + note_qs = note_qs.distinct()[:20] + return note_qs + + def get_context_data(self, **kwargs): + """ + Query the list of Guest and Note to the activity and add information to makes entry with JS. + """ + context = super().get_context_data(**kwargs) + + activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ + .distinct().get(pk=self.kwargs["pk"]) + context["activity"] = activity + + matched = [] + + for guest in self.get_invited_guest(activity): + guest.type = "Invité" + matched.append(guest) + + for note in self.get_invited_note(activity): + note.type = "Adhérent" + note.activity = activity + matched.append(note) + + table = EntryTable(data=matched) + context["table"] = table + + context["entries"] = Entry.objects.filter(activity=activity) + + context["title"] = _('Entry for activity "{}"').format(activity.name) + context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk + context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk + + activities_open = Activity.objects.filter(open=True).filter( + PermissionBackend.filter_queryset(self.request.user, Activity, "view")).distinct().all() + context["activities_open"] = [a for a in activities_open + if PermissionBackend.check_perm(self.request.user, + "activity.add_entry", + Entry(activity=a, note=self.request.user.note,))] + + return context + + +class CalendarView(View): + """ + Render an ICS calendar with all valid activities. + """ + + def multilines(self, string, maxlength, offset=0): + newstring = string[:maxlength - offset] + string = string[maxlength - offset:] + while string: + newstring += "\r\n " + newstring += string[:maxlength - 1] + string = string[maxlength - 1:] + return newstring + + def get(self, request, *args, **kwargs): + ics = """BEGIN:VCALENDAR +VERSION: 2.0 +PRODID:Note Kfet 2020 +X-WR-CALNAME:Kfet Calendar +NAME:Kfet Calendar +CALSCALE:GREGORIAN +BEGIN:VTIMEZONE +TZID:Europe/Berlin +X-LIC-LOCATION:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:CEST +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:CET +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +""" + for activity in Activity.objects.filter(valid=True).order_by("-date_start").all(): + ics += f"""BEGIN:VEVENT +DTSTAMP:{"{:%Y%m%dT%H%M%S}".format(activity.date_start)}Z +UID:{md5((activity.name + "$" + str(activity.id) + str(activity.date_start)).encode("UTF-8")).hexdigest()} +SUMMARY;CHARSET=UTF-8:{self.multilines(activity.name, 75, 22)} +DTSTART;TZID=Europe/Berlin:{"{:%Y%m%dT%H%M%S}".format(activity.date_start)} +DTEND;TZID=Europe/Berlin:{"{:%Y%m%dT%H%M%S}".format(activity.date_end)} +LOCATION:{self.multilines(activity.location, 75, 9) if activity.location else "Kfet"} +DESCRIPTION;CHARSET=UTF-8:""" + self.multilines(activity.description.replace("\n", "\\n"), 75, 26) + """ + -- {activity.organizer.name} +END:VEVENT +""" + ics += "END:VCALENDAR" + ics = ics.replace("\r", "").replace("\n", "\r\n") + return HttpResponse(ics, content_type="text/calendar; charset=UTF-8") From 4ddd763886bd426a7c06a4ca7a34b882131044c2 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 4 Sep 2020 21:46:40 +0200 Subject: [PATCH 41/41] Test activity app --- apps/activity/models.py | 2 +- apps/activity/tests/test_activities.py | 176 +++++++++++++++++++++++++ apps/activity/views.py | 31 ++--- 3 files changed, 191 insertions(+), 18 deletions(-) create mode 100644 apps/activity/tests/test_activities.py diff --git a/apps/activity/models.py b/apps/activity/models.py index 131cd725..0aefaf59 100644 --- a/apps/activity/models.py +++ b/apps/activity/models.py @@ -130,7 +130,7 @@ class Activity(models.Model): raise ValidationError(_("The end date must be after the start date.")) ret = super().save(*args, **kwargs) - if settings.DEBUG and self.pk and "scripts" in settings.INSTALLED_APPS: + if not settings.DEBUG and self.pk and "scripts" in settings.INSTALLED_APPS: def refresh_activities(): from scripts.management.commands.refresh_activities import Command as RefreshActivitiesCommand RefreshActivitiesCommand.refresh_human_readable_wiki_page("Modification de l'activité " + self.name) diff --git a/apps/activity/tests/test_activities.py b/apps/activity/tests/test_activities.py new file mode 100644 index 00000000..db83fb0e --- /dev/null +++ b/apps/activity/tests/test_activities.py @@ -0,0 +1,176 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from datetime import timedelta + +from django.contrib.auth.models import User +from django.test import TestCase +from django.urls import reverse +from django.utils import timezone + +from activity.models import Activity, ActivityType, Guest, Entry +from member.models import Club + + +class TestActivities(TestCase): + """ + Test activities + """ + fixtures = ('initial',) + + def setUp(self): + self.user = User.objects.create_superuser( + username="admintoto", + password="tototototo", + email="toto@example.com" + ) + self.client.force_login(self.user) + + sess = self.client.session + sess["permission_mask"] = 42 + sess.save() + + self.activity = Activity.objects.create( + name="Activity", + description="This is a test activity\non two very very long lines\nbecause this is very important.", + location="Earth", + activity_type=ActivityType.objects.get(name="Pot"), + creater=self.user, + organizer=Club.objects.get(name="Kfet"), + attendees_club=Club.objects.get(name="Kfet"), + date_start=timezone.now(), + date_end=timezone.now() + timedelta(days=2), + valid=True, + ) + + self.guest = Guest.objects.create( + activity=self.activity, + inviter=self.user.note, + last_name="GUEST", + first_name="Guest", + ) + + def test_activity_list(self): + """ + Display the list of all activities + """ + response = self.client.get(reverse("activity:activity_list")) + self.assertEqual(response.status_code, 200) + + def test_activity_create(self): + """ + Create a new activity + """ + response = self.client.get(reverse("activity:activity_create")) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("activity:activity_create"), data=dict( + name="Activity created", + description="This activity was successfully created.", + location="Earth", + activity_type=ActivityType.objects.get(name="Soirée de club").id, + creater=self.user.id, + organizer=Club.objects.get(name="Kfet").id, + attendees_club=Club.objects.get(name="Kfet").id, + date_start="{:%Y-%m-%d %H:%M}".format(timezone.now()), + date_end="{:%Y-%m-%d %H:%M}".format(timezone.now() + timedelta(days=2)), + valid=True, + )) + self.assertTrue(Activity.objects.filter(name="Activity created").exists()) + activity = Activity.objects.get(name="Activity created") + self.assertRedirects(response, reverse("activity:activity_detail", args=(activity.pk,)), 302, 200) + + def test_activity_detail(self): + """ + Display the detail of an activity + """ + response = self.client.get(reverse("activity:activity_detail", args=(self.activity.pk,))) + self.assertEqual(response.status_code, 200) + + def test_activity_update(self): + """ + Update an activity + """ + response = self.client.get(reverse("activity:activity_update", args=(self.activity.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.post(reverse("activity:activity_update", args=(self.activity.pk,)), data=dict( + name=str(self.activity) + " updated", + description="This activity was successfully updated.", + location="Earth", + activity_type=ActivityType.objects.get(name="Autre").id, + creater=self.user.id, + organizer=Club.objects.get(name="Kfet").id, + attendees_club=Club.objects.get(name="Kfet").id, + date_start="{:%Y-%m-%d %H:%M}".format(timezone.now()), + date_end="{:%Y-%m-%d %H:%M}".format(timezone.now() + timedelta(days=2)), + valid=True, + )) + self.assertTrue(Activity.objects.filter(name="Activity updated").exists()) + self.assertRedirects(response, reverse("activity:activity_detail", args=(self.activity.pk,)), 302, 200) + + def test_activity_entry(self): + """ + Create some entries + """ + self.activity.open = True + self.activity.save() + + response = self.client.get(reverse("activity:activity_entry", args=(self.activity.pk,))) + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("activity:activity_entry", args=(self.activity.pk,)) + "?search=guest") + self.assertEqual(response.status_code, 200) + response = self.client.get(reverse("activity:activity_entry", args=(self.activity.pk,)) + "?search=admin") + self.assertEqual(response.status_code, 200) + + # User entry + response = self.client.post("/api/activity/entry/", data=dict( + activity=self.activity.id, + note=self.user.note.id, + guest="", + )) + self.assertEqual(response.status_code, 201) # 201 = Created + self.assertTrue(Entry.objects.filter(note=self.user.note, guest=None, activity=self.activity).exists()) + + # Guest entry + response = self.client.post("/api/activity/entry/", data=dict( + activity=self.activity.id, + note=self.user.note.id, + guest=self.guest.id, + )) + self.assertEqual(response.status_code, 201) # 201 = Created + self.assertTrue(Entry.objects.filter(note=self.user.note, guest=self.guest.id, activity=self.activity).exists()) + + def test_activity_invite(self): + """ + Try to invite people to an activity + """ + response = self.client.get(reverse("activity:activity_invite", args=(self.activity.pk,))) + self.assertEqual(response.status_code, 200) + + # The activity is started, can't invite + response = self.client.post(reverse("activity:activity_invite", args=(self.activity.pk,)), data=dict( + activity=self.activity.id, + inviter=self.user.note.id, + last_name="GUEST2", + first_name="Guest", + )) + self.assertEqual(response.status_code, 200) + + self.activity.date_start += timedelta(days=1) + self.activity.save() + + response = self.client.post(reverse("activity:activity_invite", args=(self.activity.pk,)), data=dict( + activity=self.activity.id, + inviter=self.user.note.id, + last_name="GUEST2", + first_name="Guest", + )) + self.assertRedirects(response, reverse("activity:activity_detail", args=(self.activity.pk,)), 302, 200) + + def test_activity_ics(self): + """ + Render the ICS calendar + """ + response = self.client.get(reverse("activity:calendar_ics")) + self.assertEqual(response.status_code, 200) diff --git a/apps/activity/views.py b/apps/activity/views.py index f6dae500..7de31b0c 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -1,7 +1,7 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later + from hashlib import md5 -from random import randint from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin @@ -11,7 +11,6 @@ from django.db.models import F, Q from django.http import HttpResponse from django.urls import reverse_lazy from django.utils import timezone -from django.utils.crypto import get_random_string from django.utils.translation import gettext_lazy as _ from django.views import View from django.views.generic import DetailView, TemplateView, UpdateView @@ -195,10 +194,10 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): if pattern[0] != "^": pattern = "^" + pattern guest_qs = guest_qs.filter( - Q(first_name__regex=pattern) - | Q(last_name__regex=pattern) - | Q(inviter__alias__name__regex=pattern) - | Q(inviter__alias__normalized_name__regex=Alias.normalize(pattern)) + Q(first_name__iregex=pattern) + | Q(last_name__iregex=pattern) + | Q(inviter__alias__name__iregex=pattern) + | Q(inviter__alias__normalized_name__iregex=Alias.normalize(pattern)) ) else: guest_qs = guest_qs.none() @@ -231,21 +230,19 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): if "search" in self.request.GET and self.request.GET["search"]: pattern = self.request.GET["search"] note_qs = note_qs.filter( - Q(note__noteuser__user__first_name__regex=pattern) - | Q(note__noteuser__user__last_name__regex=pattern) - | Q(name__regex=pattern) - | Q(normalized_name__regex=Alias.normalize(pattern)) + Q(note__noteuser__user__first_name__iregex=pattern) + | Q(note__noteuser__user__last_name__iregex=pattern) + | Q(name__iregex=pattern) + | Q(normalized_name__iregex=Alias.normalize(pattern)) ) else: note_qs = note_qs.none() - if settings.DATABASES[note_qs.db]["ENGINE"] == 'django.db.backends.postgresql': - note_qs = note_qs.distinct('note__pk')[:20] - else: - # SQLite doesn't support distinct fields. For compatibility reason (in dev mode), the note list will only - # have distinct aliases rather than distinct notes with a SQLite DB, but it can fill the result page. - # In production mode, please use PostgreSQL. - note_qs = note_qs.distinct()[:20] + # SQLite doesn't support distinct fields. For compatibility reason (in dev mode), the note list will only + # have distinct aliases rather than distinct notes with a SQLite DB, but it can fill the result page. + # In production mode, please use PostgreSQL. + note_qs = note_qs.distinct('note__pk')[:20]\ + if settings.DATABASES[note_qs.db]["ENGINE"] == 'django.db.backends.postgresql' else note_qs.distinct()[:20] return note_qs def get_context_data(self, **kwargs):