diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4cc57807..8fcf9966 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -52,9 +52,9 @@ linters: - pip install --upgrade black isort flake8 script: - black --check . - - isort --recursive --check-only --diff bda cof events gestioncof kfet petitscours provisioning shared utils + - isort --recursive --check-only --diff bda bds cof events gestioncof kfet petitscours provisioning shared utils # Print errors only - - flake8 --exit-zero bda cof events gestioncof kfet petitscours provisioning shared utils + - flake8 --exit-zero bda bds cof events gestioncof kfet petitscours provisioning shared utils cache: key: linters paths: diff --git a/CHANGELOG b/CHANGELOG index 7b91303f..e0e8a9c6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ -* Le FUTUR ! +* Le FUTUR ! (pas prêt pour la prod) -- Nouveau module de gestion des événements (début d'implem, désactivé en prod) +- Nouveau module de gestion des événements +- Nouveau module BDS * Version 0.3 - ??? diff --git a/bds/__init__.py b/bds/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bds/admin.py b/bds/admin.py new file mode 100644 index 00000000..e7181670 --- /dev/null +++ b/bds/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin + +from bds.models import BDSProfile + +admin.site.register(BDSProfile) diff --git a/bds/apps.py b/bds/apps.py new file mode 100644 index 00000000..db0cafcb --- /dev/null +++ b/bds/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class BdsConfig(AppConfig): + name = "bds" diff --git a/bds/migrations/0001_initial.py b/bds/migrations/0001_initial.py new file mode 100644 index 00000000..e6fe5377 --- /dev/null +++ b/bds/migrations/0001_initial.py @@ -0,0 +1,142 @@ +# Generated by Django 2.2 on 2019-07-17 12:48 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + +import bds.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] + + operations = [ + migrations.CreateModel( + name="BDSProfile", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "phone", + models.CharField( + blank=True, max_length=20, verbose_name="téléphone" + ), + ), + ( + "occupation", + models.CharField( + choices=[ + ("EXT", "Extérieur"), + ("1A", "1A"), + ("2A", "2A"), + ("3A", "3A"), + ("4A", "4A"), + ("MAG", "Magistérien"), + ("ARC", "Archicube"), + ("DOC", "Doctorant"), + ("CST", "CST"), + ("PER", "Personnel ENS"), + ], + default="1A", + max_length=3, + verbose_name="occupation", + ), + ), + ( + "departement", + models.CharField( + blank=True, max_length=50, verbose_name="département" + ), + ), + ( + "birthdate", + models.DateField( + blank=True, null=True, verbose_name="date de naissance" + ), + ), + ( + "mails_bds", + models.BooleanField( + default=False, verbose_name="recevoir les mails du BDS" + ), + ), + ( + "is_buro", + models.BooleanField( + default=False, verbose_name="membre du Burô du BDS" + ), + ), + ( + "has_certificate", + models.BooleanField( + default=False, verbose_name="certificat médical" + ), + ), + ( + "certificate_file", + models.FileField( + blank=True, + upload_to=bds.models.BDSProfile.get_certificate_filename, + verbose_name="fichier de certificat médical", + ), + ), + ( + "ASPSL_number", + models.CharField( + blank=True, + max_length=50, + null=True, + verbose_name="numéro AS PSL", + ), + ), + ( + "FFSU_number", + models.CharField( + blank=True, max_length=50, null=True, verbose_name="numéro FFSU" + ), + ), + ( + "cotisation_period", + models.CharField( + choices=[ + ("ANN", "Année"), + ("SE1", "Premier semestre"), + ("SE2", "Deuxième semestre"), + ("NO", "Aucune"), + ], + default="NO", + max_length=3, + verbose_name="inscription", + ), + ), + ( + "registration_date", + models.DateField( + auto_now_add=True, verbose_name="date d'inscription" + ), + ), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="bds", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name": "Profil BDS", + "verbose_name_plural": "Profils BDS", + }, + ) + ] diff --git a/bds/migrations/0002_bds_group.py b/bds/migrations/0002_bds_group.py new file mode 100644 index 00000000..9b54d35c --- /dev/null +++ b/bds/migrations/0002_bds_group.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2 on 2019-07-17 14:56 + +from django.contrib.auth.management import create_permissions +from django.db import migrations +from django.db.models import Q + + +def create_bds_buro_group(apps, schema_editor): + for app_config in apps.get_app_configs(): + create_permissions(app_config, apps=apps, verbosity=0) + + Group = apps.get_model("auth", "Group") + Permission = apps.get_model("auth", "Permission") + group, created = Group.objects.get_or_create(name="Burô du BDS") + if created: + perms = Permission.objects.filter( + Q(content_type__app_label="bds") + | Q(content_type__app_label="auth") & Q(content_type__model="user") + ) + group.permissions.set(perms) + group.save() + + +class Migration(migrations.Migration): + + dependencies = [("bds", "0001_initial")] + + operations = [ + migrations.RunPython(create_bds_buro_group, migrations.RunPython.noop) + ] diff --git a/bds/migrations/__init__.py b/bds/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bds/models.py b/bds/models.py new file mode 100644 index 00000000..f262ee26 --- /dev/null +++ b/bds/models.py @@ -0,0 +1,95 @@ +from datetime import date +from os.path import splitext + +from django.contrib.auth import get_user_model +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from shared.utils import choices_length + +User = get_user_model() + + +class BDSProfile(models.Model): + OCCUPATION_CHOICES = ( + ("EXT", "Extérieur"), + ("1A", "1A"), + ("2A", "2A"), + ("3A", "3A"), + ("4A", "4A"), + ("MAG", "Magistérien"), + ("ARC", "Archicube"), + ("DOC", "Doctorant"), + ("CST", "CST"), + ("PER", "Personnel ENS"), + ) + + TYPE_COTIZ_CHOICES = ( + ("ETU", "Étudiant"), + ("NOR", "Normalien"), + ("EXT", "Extérieur"), + ("ARC", "Archicube"), + ) + + COTIZ_DURATION_CHOICES = ( + ("ANN", "Année"), + ("SE1", "Premier semestre"), + ("SE2", "Deuxième semestre"), + ("NO", "Aucune"), + ) + + def get_certificate_filename(instance, filename): + _, ext = splitext(filename) # récupère l'extension du fichier + year = str(date.now().year) + return "certifs/{username}-{year}.{ext}".format( + username=instance.user.username, year=year, ext=ext + ) + + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="bds") + phone = models.CharField(_("téléphone"), max_length=20, blank=True) + occupation = models.CharField( + _("occupation"), + default="1A", + choices=OCCUPATION_CHOICES, + max_length=choices_length(OCCUPATION_CHOICES), + ) + departement = models.CharField(_("département"), max_length=50, blank=True) + birthdate = models.DateField( + auto_now_add=False, + auto_now=False, + verbose_name=_("date de naissance"), + blank=True, + null=True, + ) + + mails_bds = models.BooleanField(_("recevoir les mails du BDS"), default=False) + is_buro = models.BooleanField(_("membre du Burô du BDS"), default=False) + + has_certificate = models.BooleanField(_("certificat médical"), default=False) + certificate_file = models.FileField( + _("fichier de certificat médical"), + upload_to=get_certificate_filename, + blank=True, + ) + + ASPSL_number = models.CharField( + _("numéro AS PSL"), max_length=50, blank=True, null=True + ) + FFSU_number = models.CharField( + _("numéro FFSU"), max_length=50, blank=True, null=True + ) + + cotisation_period = models.CharField( + _("inscription"), default="NO", choices=COTIZ_DURATION_CHOICES, max_length=3 + ) + + registration_date = models.DateField( + auto_now_add=True, verbose_name=_("date d'inscription") + ) + + class Meta: + verbose_name = _("Profil BDS") + verbose_name_plural = _("Profils BDS") + + def __str__(self): + return self.user.username diff --git a/bds/views.py b/bds/views.py new file mode 100644 index 00000000..60f00ef0 --- /dev/null +++ b/bds/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/cof/settings/dev.py b/cof/settings/dev.py index 0920e538..db7a52cb 100644 --- a/cof/settings/dev.py +++ b/cof/settings/dev.py @@ -15,9 +15,9 @@ DEBUG = True if TESTING: PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"] -# Only in development mode as long as the events app is not -# ready for production -INSTALLED_APPS += ["events"] +# As long as these apps are not ready for production, they are only available +# in development mode +INSTALLED_APPS += ["events", "bds"] # --- diff --git a/gestioncof/models.py b/gestioncof/models.py index e4975fc4..c2c71660 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -5,7 +5,7 @@ from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _ from bda.models import Spectacle -from petitscours.models import choices_length +from shared.utils import choices_length TYPE_COMMENT_FIELD = (("text", _("Texte long")), ("char", _("Texte court"))) diff --git a/kfet/models.py b/kfet/models.py index 5d8ad3cb..a2d776b9 100644 --- a/kfet/models.py +++ b/kfet/models.py @@ -1,6 +1,5 @@ import re from datetime import date -from functools import reduce from django.contrib.auth.models import User from django.core.validators import RegexValidator @@ -11,6 +10,7 @@ from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from gestioncof.models import CofProfile +from shared.utils import choices_length from . import KFET_DELETED_TRIGRAMME from .auth import KFET_GENERIC_TRIGRAMME @@ -19,10 +19,6 @@ from .config import kfet_config from .utils import to_ukf -def choices_length(choices): - return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0) - - def default_promo(): now = date.today() return now.month <= 8 and now.year - 1 or now.year diff --git a/petitscours/models.py b/petitscours/models.py index cc518675..27b5e931 100644 --- a/petitscours/models.py +++ b/petitscours/models.py @@ -1,15 +1,10 @@ -from functools import reduce - from django.contrib.auth.models import User from django.db import models from django.db.models import Min from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ - -def choices_length(choices): - return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0) - +from shared.utils import choices_length LEVELS_CHOICES = ( ("college", _("Collège")), diff --git a/setup.cfg b/setup.cfg index 350ceb20..7695596d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,7 @@ [coverage:run] source = bda + bds cof events gestioncof @@ -35,7 +36,7 @@ default_section = THIRDPARTY force_grid_wrap = 0 include_trailing_comma = true known_django = django -known_first_party = bda,cof,events,gestioncof,kfet,petitscours,shared,utils +known_first_party = bda,bds,cof,events,gestioncof,kfet,petitscours,shared,utils line_length = 88 multi_line_output = 3 not_skip = __init__.py diff --git a/shared/utils.py b/shared/utils.py new file mode 100644 index 00000000..ea4aa0dd --- /dev/null +++ b/shared/utils.py @@ -0,0 +1,5 @@ +from functools import reduce + + +def choices_length(choices): + return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0)