diff --git a/.pylintrc b/.pylintrc index 8b45ce1..1c8bf0a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -232,7 +232,7 @@ single-line-if-stmt=no [VARIABLES] -django-settings-module=khana.settings +django-settings-module=config.settings # List of additional names supposed to be defined in builtins. Remember that # you should avoid defining new builtins when possible. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1e88b0d --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +# Makefile for khana + +ifeq ($(shell which coverage >/dev/null 2>&1; echo $$?), 1) + $(error The 'coverage' command was not found. Make sure you have coverage installed) +endif + +.PHONY: help coverage + +help: + @echo " coverage to run coverage check of the source files." + +coverage: + coverage run --source='.' manage.py test; coverage report; coverage html; + @echo "Testing of coverage in the sources finished." diff --git a/src/base/__init__.py b/config/__init__.py similarity index 100% rename from src/base/__init__.py rename to config/__init__.py diff --git a/src/khana/settings.py b/config/settings.py similarity index 84% rename from src/khana/settings.py rename to config/settings.py index 32f882a..1beedde 100644 --- a/src/khana/settings.py +++ b/config/settings.py @@ -16,19 +16,17 @@ import environ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -env = environ.Env( - DEBUG=(bool, False) -) +env = environ.Env(DEBUG=(bool, False)) environ.Env.read_env() -DEBUG = env('DEBUG', default=True) +DEBUG = env("DEBUG", default=True) -SECRET_KEY = env('SECRET_KEY', default="6@9p0g-5ebcttbt$^*s4rda5!piezt6b7wj35g(+$mgz52k#d=") +SECRET_KEY = env( + "SECRET_KEY", default="6@9p0g-5ebcttbt$^*s4rda5!piezt6b7wj35g(+$mgz52k#d=" +) -DATABASES = { - 'default': env.db('DATABASE_URL', default='sqlite:///db.sqlite3') -} +DATABASES = {"default": env.db("DATABASE_URL", default="sqlite:///db.sqlite3")} # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ @@ -45,14 +43,14 @@ INSTALLED_APPS = ( "django.contrib.messages", "django.contrib.staticfiles", "django_extensions", - "people", - "location", - "planning", - "objective", - "competition", - "profile", - "tools", - "communication", + "khana.people", + "khana.location", + "khana.planning", + "khana.objective", + "khana.competition", + "khana.profile", + "khana.tools", + "khana.communication", "rest_framework", ) @@ -66,7 +64,7 @@ MIDDLEWARE = [ #'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -ROOT_URLCONF = "khana.urls" +ROOT_URLCONF = "config.urls" TEMPLATES = [ { @@ -122,4 +120,4 @@ DEBUG_TOOLBAR_CONFIG = { "JQUERY_URL": STATIC_URL + "js/jquery-2.1.4.min.js", } -DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" diff --git a/src/khana/urls.py b/config/urls.py similarity index 100% rename from src/khana/urls.py rename to config/urls.py diff --git a/src/khana/views.py b/config/views.py similarity index 98% rename from src/khana/views.py rename to config/views.py index 4c1e062..2bc423b 100644 --- a/src/khana/views.py +++ b/config/views.py @@ -246,7 +246,8 @@ def search(request): name__icontains=pattern ) # ou gymnaste qui y participe ! gymnast_list = Gymnast.objects.filter( - Q(user__last_name__icontains=pattern) | Q(user__first_name__icontains=pattern) + Q(user__last_name__icontains=pattern) + | Q(user__first_name__icontains=pattern) ) accident_list = Accident.objects.filter( Q(gymnast__user__last_name__icontains=pattern) diff --git a/src/khana/wsgi.py b/config/wsgi.py similarity index 100% rename from src/khana/wsgi.py rename to config/wsgi.py diff --git a/src/fixtures/initial_data.json b/fixtures/initial_data.json similarity index 100% rename from src/fixtures/initial_data.json rename to fixtures/initial_data.json diff --git a/src/communication/__init__.py b/khana/__init__.py similarity index 100% rename from src/communication/__init__.py rename to khana/__init__.py diff --git a/src/communication/migrations/__init__.py b/khana/base/__init__.py similarity index 100% rename from src/communication/migrations/__init__.py rename to khana/base/__init__.py diff --git a/src/base/models.py b/khana/base/models.py similarity index 100% rename from src/base/models.py rename to khana/base/models.py diff --git a/src/base/tests.py b/khana/base/tests.py similarity index 100% rename from src/base/tests.py rename to khana/base/tests.py diff --git a/src/competition/__init__.py b/khana/communication/__init__.py similarity index 100% rename from src/competition/__init__.py rename to khana/communication/__init__.py diff --git a/src/communication/admin.py b/khana/communication/admin.py similarity index 99% rename from src/communication/admin.py rename to khana/communication/admin.py index 888a2eb..17d2191 100644 --- a/src/communication/admin.py +++ b/khana/communication/admin.py @@ -9,6 +9,7 @@ from .models import Message class MessageAdmin(admin.ModelAdmin): """La classe `MessageAdmin` contrôle la gestion des messages """ + list_display = ("sender", "recipient", "written_at", "is_read", "read_at") ordering = ("written_at", "sender") search_fields = ("sender", "recipient", "message_title") diff --git a/src/communication/apps.py b/khana/communication/apps.py similarity index 69% rename from src/communication/apps.py rename to khana/communication/apps.py index bcfabf0..9575409 100644 --- a/src/communication/apps.py +++ b/khana/communication/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class CommunicationConfig(AppConfig): - name = "communication" + name = "khana.communication" diff --git a/src/communication/forms.py b/khana/communication/forms.py similarity index 99% rename from src/communication/forms.py rename to khana/communication/forms.py index 4465aef..a183e6a 100644 --- a/src/communication/forms.py +++ b/khana/communication/forms.py @@ -8,6 +8,7 @@ from .models import Message class MessageForm(forms.ModelForm): """Formulaire de base pour la création et la modification de messages """ + class Meta: model = Message fields = ( diff --git a/src/communication/migrations/0001_initial.py b/khana/communication/migrations/0001_initial.py similarity index 100% rename from src/communication/migrations/0001_initial.py rename to khana/communication/migrations/0001_initial.py diff --git a/src/communication/migrations/0002_auto_20190413_1028.py b/khana/communication/migrations/0002_auto_20190413_1028.py similarity index 100% rename from src/communication/migrations/0002_auto_20190413_1028.py rename to khana/communication/migrations/0002_auto_20190413_1028.py diff --git a/khana/communication/migrations/0003_auto_20210513_1058.py b/khana/communication/migrations/0003_auto_20210513_1058.py new file mode 100644 index 0000000..93db0a3 --- /dev/null +++ b/khana/communication/migrations/0003_auto_20210513_1058.py @@ -0,0 +1,63 @@ +# Generated by Django 3.2.2 on 2021-05-13 10:58 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("communication", "0002_auto_20190413_1028"), + ] + + operations = [ + migrations.RenameField( + model_name="message", old_name="message_body", new_name="body", + ), + migrations.RenameField( + model_name="message", old_name="date_of_reading", new_name="read_at", + ), + migrations.RenameField( + model_name="message", old_name="message_title", new_name="title", + ), + migrations.RenameField( + model_name="message", old_name="date_of_writing", new_name="written_at", + ), + migrations.RemoveField(model_name="message", name="is_read",), + migrations.RemoveField(model_name="message", name="reader",), + migrations.RemoveField(model_name="message", name="writer",), + migrations.AddField( + model_name="message", + name="content", + field=models.TextField( + blank=True, + help_text="Seul le MarkDown simple est accepté", + null=True, + verbose_name="Comments", + ), + ), + migrations.AddField( + model_name="message", + name="recipient", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + related_name="received_messages", + to="auth.user", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="message", + name="sender", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + related_name="sent_messages", + to="auth.user", + ), + preserve_default=False, + ), + ] diff --git a/src/competition/migrations/__init__.py b/khana/communication/migrations/__init__.py similarity index 100% rename from src/competition/migrations/__init__.py rename to khana/communication/migrations/__init__.py diff --git a/src/communication/models.py b/khana/communication/models.py similarity index 79% rename from src/communication/models.py rename to khana/communication/models.py index b449b12..3c25def 100644 --- a/src/communication/models.py +++ b/khana/communication/models.py @@ -12,7 +12,7 @@ from datetime import datetime from django.db import models from django.contrib.auth import get_user_model -from base.models import Markdownizable +from khana.base.models import Markdownizable User = get_user_model() @@ -33,13 +33,11 @@ class Message(Markdownizable): sender = models.ForeignKey( User, on_delete=models.CASCADE, related_name="sent_messages" ) - written_at = models.DateTimeField( - auto_now_add=True, verbose_name="Date of writing" - ) - recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name="received_messages") - read_at = models.DateTimeField( - auto_now=True, verbose_name="Date of reading" + written_at = models.DateTimeField(auto_now_add=True, verbose_name="Date of writing") + recipient = models.ForeignKey( + User, on_delete=models.CASCADE, related_name="received_messages" ) + read_at = models.DateTimeField(auto_now=True, verbose_name="Date of reading") title = models.CharField(max_length=255, verbose_name="Title") body = models.TextField(null=True, blank=True, verbose_name="Message") diff --git a/src/communication/tests_models.py b/khana/communication/tests_models.py similarity index 88% rename from src/communication/tests_models.py rename to khana/communication/tests_models.py index 254da67..1d44243 100644 --- a/src/communication/tests_models.py +++ b/khana/communication/tests_models.py @@ -14,6 +14,6 @@ def test_message_to_string(): """Vérifie la représentation textuelle d'un message """ timing = datetime.now() - user = User(username='fred', password='fredpassword') + user = User(username="fred", password="fredpassword") message = Message(sender=user, written_at=timing, title="test") assert str(message) == "fred - " + str(timing) + " : test" diff --git a/src/communication/urls.py b/khana/communication/urls.py similarity index 100% rename from src/communication/urls.py rename to khana/communication/urls.py diff --git a/khana/communication/views.py b/khana/communication/views.py new file mode 100644 index 0000000..5189119 --- /dev/null +++ b/khana/communication/views.py @@ -0,0 +1,92 @@ +"""Vues et fonctions pour tout ce qui touche à la communication entre plusieurs utilisateurs.""" + +from datetime import datetime + +from django.contrib.auth.decorators import login_required +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import render, get_object_or_404 +from django.views.decorators.http import require_http_methods +from django.urls import reverse + +from .forms import MessageForm +from .models import Message + + +@login_required +def get_number_of_unread_message(request): + """Récupère le nombre de messages non lus associés à l'utilisateur en session. + """ + return ( + Message.objects.filter(recipient=request.user) + .filter(read_at__isnull=True) + .count() + ) + + +@login_required +@require_http_methods(["GET"]) +def get_received_messages(request): + """Récupère des messages recus pour l'utilisateur connecté. + """ + return request.user.received_messages.all() + + +@login_required +@require_http_methods(["GET"]) +def get_sent_messages(request): + """Récupère des messages envoyés par l'utilisateur connecté. + """ + return request.user.sent_messages.all() + + +@login_required +@require_http_methods(["GET"]) +def get_message_details(request, messageid): + """Récupère les détails (l'affichage ?) d'un message. + """ + message = get_object_or_404(Message, pk=messageid) + if not message.read_at and message.recipient == request.user.id: + message.read_at = datetime.now() + message.save() + + context = {"message": message, "type": None} + return render(request, "message_details.html", context) + + +@login_required +@require_http_methods(["POST"]) +def delete_message(request, messageid): + """Supprime le message dont la clé est passée en paramètre. + """ + try: + message = Message.objects.get(pk=messageid) + + if message.sender == request.user or message.recipient == request.user: + message.delete() + else: + return HttpResponse(401) + except: + return HttpResponse(400) + + return HttpResponse(200) + + +@login_required +@require_http_methods(["GET", "POST"]) +def compose_message(request): + """Permet à l'utilisateur connecté de rédiger un nouveau message. + """ + + if request.method == "POST": + form = MessageForm(request.POST) + + if form.is_valid(): + form.save() + return HttpResponseRedirect(reverse("sent_messages")) + + print("Invalid form") + else: + form = MessageForm() + + context = {"form": form, "writer": request.user.id} + return render(request, "message_create.html", context) diff --git a/src/khana/__init__.py b/khana/competition/__init__.py similarity index 100% rename from src/khana/__init__.py rename to khana/competition/__init__.py diff --git a/src/competition/admin.py b/khana/competition/admin.py similarity index 100% rename from src/competition/admin.py rename to khana/competition/admin.py diff --git a/khana/competition/apps.py b/khana/competition/apps.py new file mode 100644 index 0000000..3021000 --- /dev/null +++ b/khana/competition/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CompetitionConfig(AppConfig): + name = "khana.competition" diff --git a/src/competition/forms.py b/khana/competition/forms.py similarity index 100% rename from src/competition/forms.py rename to khana/competition/forms.py diff --git a/src/competition/migrations/0001_initial.py b/khana/competition/migrations/0001_initial.py similarity index 100% rename from src/competition/migrations/0001_initial.py rename to khana/competition/migrations/0001_initial.py diff --git a/src/competition/migrations/0002_auto_20190316_1658.py b/khana/competition/migrations/0002_auto_20190316_1658.py similarity index 100% rename from src/competition/migrations/0002_auto_20190316_1658.py rename to khana/competition/migrations/0002_auto_20190316_1658.py diff --git a/src/competition/migrations/0003_auto_20190402_2026.py b/khana/competition/migrations/0003_auto_20190402_2026.py similarity index 100% rename from src/competition/migrations/0003_auto_20190402_2026.py rename to khana/competition/migrations/0003_auto_20190402_2026.py diff --git a/src/competition/migrations/0004_auto_20190402_2032.py b/khana/competition/migrations/0004_auto_20190402_2032.py similarity index 100% rename from src/competition/migrations/0004_auto_20190402_2032.py rename to khana/competition/migrations/0004_auto_20190402_2032.py diff --git a/src/competition/migrations/0005_auto_20190402_2035.py b/khana/competition/migrations/0005_auto_20190402_2035.py similarity index 100% rename from src/competition/migrations/0005_auto_20190402_2035.py rename to khana/competition/migrations/0005_auto_20190402_2035.py diff --git a/src/competition/migrations/0006_auto_20190429_1125.py b/khana/competition/migrations/0006_auto_20190429_1125.py similarity index 100% rename from src/competition/migrations/0006_auto_20190429_1125.py rename to khana/competition/migrations/0006_auto_20190429_1125.py diff --git a/src/competition/migrations/0007_auto_20190524_1211.py b/khana/competition/migrations/0007_auto_20190524_1211.py similarity index 100% rename from src/competition/migrations/0007_auto_20190524_1211.py rename to khana/competition/migrations/0007_auto_20190524_1211.py diff --git a/src/location/__init__.py b/khana/competition/migrations/__init__.py similarity index 100% rename from src/location/__init__.py rename to khana/competition/migrations/__init__.py diff --git a/src/competition/models.py b/khana/competition/models.py similarity index 100% rename from src/competition/models.py rename to khana/competition/models.py diff --git a/src/competition/tests_models.py b/khana/competition/tests_models.py similarity index 94% rename from src/competition/tests_models.py rename to khana/competition/tests_models.py index 2156169..89652ff 100644 --- a/src/competition/tests_models.py +++ b/khana/competition/tests_models.py @@ -1,12 +1,7 @@ # coding=UTF-8 import pytest -from .models import ( - Point, - Competition, - Division, - Level -) +from .models import Point, Competition, Division, Level # class TestModelCompetition(TestCase): @@ -14,6 +9,7 @@ from .models import ( # Tests relatifs à la classe `Compétition`. # """ + def test_competition_str_(): """ Vérifie la représentation textuelle de la classe. """ competition = Competition(name="Belgian Open Trampoline", acronym="BOT") @@ -25,6 +21,7 @@ def test_competition_str_(): # Tests relatifs à la classe `Division`. # """ + def test_division_str_(): """ Vérifie la représentation textuelle de la classe. """ competition = Competition(name="Belgian Open Trampoline", acronym="BOT") @@ -37,6 +34,7 @@ def test_division_str_(): # Tests relatifs à la classe `Level`. # """ + def test_level_str_(): """ Vérifie la représentation textuelle de la classe. """ competition = Competition(name="Belgian Open Trampoline", acronym="BOT") diff --git a/src/competition/urls.py b/khana/competition/urls.py similarity index 100% rename from src/competition/urls.py rename to khana/competition/urls.py diff --git a/src/competition/views.py b/khana/competition/views.py similarity index 100% rename from src/competition/views.py rename to khana/competition/views.py diff --git a/src/location/migrations/__init__.py b/khana/location/__init__.py similarity index 100% rename from src/location/migrations/__init__.py rename to khana/location/__init__.py diff --git a/src/location/admin.py b/khana/location/admin.py similarity index 100% rename from src/location/admin.py rename to khana/location/admin.py diff --git a/khana/location/apps.py b/khana/location/apps.py new file mode 100644 index 0000000..4cb417c --- /dev/null +++ b/khana/location/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class LocationConfig(AppConfig): + name = "khana.location" diff --git a/src/location/forms.py b/khana/location/forms.py similarity index 100% rename from src/location/forms.py rename to khana/location/forms.py diff --git a/src/location/migrations/0001_initial.py b/khana/location/migrations/0001_initial.py similarity index 100% rename from src/location/migrations/0001_initial.py rename to khana/location/migrations/0001_initial.py diff --git a/src/location/migrations/0002_auto_20190402_2032.py b/khana/location/migrations/0002_auto_20190402_2032.py similarity index 100% rename from src/location/migrations/0002_auto_20190402_2032.py rename to khana/location/migrations/0002_auto_20190402_2032.py diff --git a/src/location/migrations/0003_auto_20190402_2035.py b/khana/location/migrations/0003_auto_20190402_2035.py similarity index 100% rename from src/location/migrations/0003_auto_20190402_2035.py rename to khana/location/migrations/0003_auto_20190402_2035.py diff --git a/src/location/tests/__init__.py b/khana/location/migrations/__init__.py similarity index 100% rename from src/location/tests/__init__.py rename to khana/location/migrations/__init__.py diff --git a/src/location/models.py b/khana/location/models.py similarity index 100% rename from src/location/models.py rename to khana/location/models.py diff --git a/src/objective/__init__.py b/khana/location/tests/__init__.py similarity index 100% rename from src/objective/__init__.py rename to khana/location/tests/__init__.py diff --git a/src/location/tests/test_models.py b/khana/location/tests/test_models.py similarity index 66% rename from src/location/tests/test_models.py rename to khana/location/tests/test_models.py index d404631..f6f8c4e 100644 --- a/src/location/tests/test_models.py +++ b/khana/location/tests/test_models.py @@ -8,10 +8,7 @@ from ..models import Club, Country, Place class TestCountry(TestCase): def test_str_should_contain_name_and_iso2(self): country = Country.objects.create( - nameus="Belgium", - namefr="Belgique", - isonum=56, - iso2="BE" + nameus="Belgium", namefr="Belgique", isonum=56, iso2="BE" ) self.assertEqual(str(country), "Belgique (BE)") @@ -23,11 +20,8 @@ class TestPlace(TestCase): name="Heaven", postal=1080, country=Country.objects.create( - nameus="Belgium", - namefr="Belgique", - isonum=56, - iso2="BE" - ) + nameus="Belgium", namefr="Belgique", isonum=56, iso2="BE" + ), ) self.assertEqual(str(place), "Heaven (?)") @@ -41,12 +35,9 @@ class TestClub(TestCase): name="Heaven", postal=1080, country=Country.objects.create( - nameus="Belgium", - namefr="Belgique", - isonum=56, - iso2="BE" - ) - ) + nameus="Belgium", namefr="Belgique", isonum=56, iso2="BE" + ), + ), ) - self.assertEqual(str(club), "RSCA (à ?)") \ No newline at end of file + self.assertEqual(str(club), "RSCA (à ?)") diff --git a/src/location/tests_models.py b/khana/location/tests_models.py similarity index 79% rename from src/location/tests_models.py rename to khana/location/tests_models.py index 1c93626..f5f002f 100644 --- a/src/location/tests_models.py +++ b/khana/location/tests_models.py @@ -1,10 +1,6 @@ # coding=UTF-8 -from .models import ( - Club, - Place, - Country -) +from .models import Club, Place, Country import pytest # class GymnastTestCase(): @@ -12,11 +8,13 @@ def test_country_tostring(): c = Country(namefr="Belgique", iso2="56") assert str(c) == "Belgique (56)" + def test_place_tostring(): p = Place(name="FATC", city="Lillois") assert str(p) == "FATC (Lillois)" + def test_club_tostring(): p = Place(name="FATC", city="Lillois") club = Club(place=p, name="FATC2") - assert str(club) == "FATC2 (à Lillois)" \ No newline at end of file + assert str(club) == "FATC2 (à Lillois)" diff --git a/src/location/urls.py b/khana/location/urls.py similarity index 100% rename from src/location/urls.py rename to khana/location/urls.py diff --git a/src/location/views.py b/khana/location/views.py similarity index 100% rename from src/location/views.py rename to khana/location/views.py diff --git a/src/main.py b/khana/main.py similarity index 100% rename from src/main.py rename to khana/main.py diff --git a/src/objective/migrations/__init__.py b/khana/objective/__init__.py similarity index 100% rename from src/objective/migrations/__init__.py rename to khana/objective/__init__.py diff --git a/src/objective/admin.py b/khana/objective/admin.py similarity index 100% rename from src/objective/admin.py rename to khana/objective/admin.py diff --git a/khana/objective/apps.py b/khana/objective/apps.py new file mode 100644 index 0000000..d7d8166 --- /dev/null +++ b/khana/objective/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ObjectiveConfig(AppConfig): + name = "khana.objective" diff --git a/src/objective/forms.py b/khana/objective/forms.py similarity index 100% rename from src/objective/forms.py rename to khana/objective/forms.py diff --git a/src/objective/migrations/0001_initial.py b/khana/objective/migrations/0001_initial.py similarity index 100% rename from src/objective/migrations/0001_initial.py rename to khana/objective/migrations/0001_initial.py diff --git a/src/objective/migrations/0002_auto_20190316_1658.py b/khana/objective/migrations/0002_auto_20190316_1658.py similarity index 100% rename from src/objective/migrations/0002_auto_20190316_1658.py rename to khana/objective/migrations/0002_auto_20190316_1658.py diff --git a/src/objective/migrations/0003_auto_20190318_0855.py b/khana/objective/migrations/0003_auto_20190318_0855.py similarity index 100% rename from src/objective/migrations/0003_auto_20190318_0855.py rename to khana/objective/migrations/0003_auto_20190318_0855.py diff --git a/src/objective/migrations/0004_auto_20190330_0920.py b/khana/objective/migrations/0004_auto_20190330_0920.py similarity index 100% rename from src/objective/migrations/0004_auto_20190330_0920.py rename to khana/objective/migrations/0004_auto_20190330_0920.py diff --git a/src/objective/migrations/0005_auto_20190330_1652.py b/khana/objective/migrations/0005_auto_20190330_1652.py similarity index 100% rename from src/objective/migrations/0005_auto_20190330_1652.py rename to khana/objective/migrations/0005_auto_20190330_1652.py diff --git a/src/objective/migrations/0006_auto_20190401_1755.py b/khana/objective/migrations/0006_auto_20190401_1755.py similarity index 100% rename from src/objective/migrations/0006_auto_20190401_1755.py rename to khana/objective/migrations/0006_auto_20190401_1755.py diff --git a/src/objective/migrations/0007_auto_20190402_0820.py b/khana/objective/migrations/0007_auto_20190402_0820.py similarity index 100% rename from src/objective/migrations/0007_auto_20190402_0820.py rename to khana/objective/migrations/0007_auto_20190402_0820.py diff --git a/src/objective/migrations/0008_auto_20190402_0830.py b/khana/objective/migrations/0008_auto_20190402_0830.py similarity index 100% rename from src/objective/migrations/0008_auto_20190402_0830.py rename to khana/objective/migrations/0008_auto_20190402_0830.py diff --git a/src/objective/migrations/0009_auto_20190402_2026.py b/khana/objective/migrations/0009_auto_20190402_2026.py similarity index 100% rename from src/objective/migrations/0009_auto_20190402_2026.py rename to khana/objective/migrations/0009_auto_20190402_2026.py diff --git a/src/objective/migrations/0010_auto_20190402_2032.py b/khana/objective/migrations/0010_auto_20190402_2032.py similarity index 100% rename from src/objective/migrations/0010_auto_20190402_2032.py rename to khana/objective/migrations/0010_auto_20190402_2032.py diff --git a/src/objective/migrations/0011_auto_20190402_2035.py b/khana/objective/migrations/0011_auto_20190402_2035.py similarity index 100% rename from src/objective/migrations/0011_auto_20190402_2035.py rename to khana/objective/migrations/0011_auto_20190402_2035.py diff --git a/src/objective/migrations/0012_auto_20190407_1837.py b/khana/objective/migrations/0012_auto_20190407_1837.py similarity index 100% rename from src/objective/migrations/0012_auto_20190407_1837.py rename to khana/objective/migrations/0012_auto_20190407_1837.py diff --git a/src/objective/migrations/0013_auto_20190429_0708.py b/khana/objective/migrations/0013_auto_20190429_0708.py similarity index 100% rename from src/objective/migrations/0013_auto_20190429_0708.py rename to khana/objective/migrations/0013_auto_20190429_0708.py diff --git a/src/objective/migrations/0014_evaluation.py b/khana/objective/migrations/0014_evaluation.py similarity index 100% rename from src/objective/migrations/0014_evaluation.py rename to khana/objective/migrations/0014_evaluation.py diff --git a/src/objective/migrations/0015_auto_20190524_1211.py b/khana/objective/migrations/0015_auto_20190524_1211.py similarity index 100% rename from src/objective/migrations/0015_auto_20190524_1211.py rename to khana/objective/migrations/0015_auto_20190524_1211.py diff --git a/src/objective/migrations/0016_rename_information_educative_content.py b/khana/objective/migrations/0016_rename_information_educative_content.py similarity index 58% rename from src/objective/migrations/0016_rename_information_educative_content.py rename to khana/objective/migrations/0016_rename_information_educative_content.py index f6fb3f0..e5c66b8 100644 --- a/src/objective/migrations/0016_rename_information_educative_content.py +++ b/khana/objective/migrations/0016_rename_information_educative_content.py @@ -6,13 +6,11 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('objective', '0015_auto_20190524_1211'), + ("objective", "0015_auto_20190524_1211"), ] operations = [ migrations.RenameField( - model_name='educative', - old_name='information', - new_name='content', + model_name="educative", old_name="information", new_name="content", ), ] diff --git a/khana/objective/migrations/0017_auto_20210620_1618.py b/khana/objective/migrations/0017_auto_20210620_1618.py new file mode 100644 index 0000000..fbc3486 --- /dev/null +++ b/khana/objective/migrations/0017_auto_20210620_1618.py @@ -0,0 +1,75 @@ +# Generated by Django 3.2.2 on 2021-06-20 16:18 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("objective", "0016_rename_information_educative_content"), + ] + + operations = [ + migrations.RenameModel(old_name="Routine_Skill", new_name="RoutineSkill",), + migrations.AlterModelOptions( + name="educative", + options={ + "ordering": ["label", "short_label"], + "verbose_name": "Educatif", + "verbose_name_plural": "Educatifs", + }, + ), + migrations.AlterModelOptions( + name="touchposition", + options={ + "ordering": [ + "label", + "short_label", + "is_default", + "allowed_in_competition", + ], + "verbose_name": "Landing", + "verbose_name_plural": "Landings", + }, + ), + migrations.RenameField( + model_name="educative", old_name="ageBoy", new_name="age_boy", + ), + migrations.RenameField( + model_name="educative", old_name="ageGirl", new_name="age_girl", + ), + migrations.RenameField( + model_name="educative", old_name="educative", new_name="educatives", + ), + migrations.RenameField( + model_name="educative", old_name="longLabel", new_name="label", + ), + migrations.RenameField( + model_name="educative", old_name="prerequisite", new_name="prerequisites", + ), + migrations.RenameField( + model_name="educative", old_name="shortLabel", new_name="short_label", + ), + migrations.RenameField( + model_name="skill", old_name="rotationType", new_name="rotation_type", + ), + migrations.RenameField( + model_name="skill", + old_name="simplyNotation", + new_name="simplified_notation", + ), + migrations.RenameField( + model_name="touchposition", + old_name="competition", + new_name="allowed_in_competition", + ), + migrations.RenameField( + model_name="touchposition", old_name="default", new_name="is_default", + ), + migrations.RenameField( + model_name="touchposition", old_name="longLabel", new_name="label", + ), + migrations.RenameField( + model_name="touchposition", old_name="shortLabel", new_name="short_label", + ), + ] diff --git a/src/people/__init__.py b/khana/objective/migrations/__init__.py similarity index 100% rename from src/people/__init__.py rename to khana/objective/migrations/__init__.py diff --git a/src/objective/models.py b/khana/objective/models.py similarity index 69% rename from src/objective/models.py rename to khana/objective/models.py index 6d26ecc..8b617a2 100644 --- a/src/objective/models.py +++ b/khana/objective/models.py @@ -3,7 +3,7 @@ from datetime import date from django.db import models from django.db.models import Q -from base.models import Markdownizable +from khana.base.models import Markdownizable from django.contrib.auth.models import User @@ -17,24 +17,24 @@ class Educative(Markdownizable): verbose_name_plural = "Educatifs" ordering = ["label", "short_label"] # 'level', - label = models.CharField(max_length = 255, verbose_name = "Long Name") - short_label = models.CharField(max_length = 255, verbose_name = "Short Name") + label = models.CharField(max_length=255, verbose_name="Long Name") + short_label = models.CharField(max_length=255, verbose_name="Short Name") difficulty = models.DecimalField( - max_digits = 3, decimal_places = 1, verbose_name = "Difficulty" + max_digits=3, decimal_places=1, verbose_name="Difficulty" ) - level = models.PositiveSmallIntegerField(verbose_name = "Level", default = 0) - rank = models.PositiveSmallIntegerField(verbose_name = "Rank", default = 0) + level = models.PositiveSmallIntegerField(verbose_name="Level", default=0) + rank = models.PositiveSmallIntegerField(verbose_name="Rank", default=0) educatives = models.ManyToManyField( - "self", related_name = "educativeOf", blank = True, symmetrical = False + "self", related_name="educativeOf", blank=True, symmetrical=False ) prerequisites = models.ManyToManyField( - "self", related_name = "prerequisiteOf", blank = True, symmetrical = False + "self", related_name="prerequisiteOf", blank=True, symmetrical=False ) age_boy = models.PositiveSmallIntegerField( - blank = True, null = True, verbose_name = "Boy's age" + blank=True, null=True, verbose_name="Boy's age" ) age_girl = models.PositiveSmallIntegerField( - blank = True, null = True, verbose_name = "Girl's age" + blank=True, null=True, verbose_name="Girl's age" ) def __str__(self): @@ -56,10 +56,10 @@ class TouchPosition(models.Model): verbose_name_plural = "Landings" ordering = ["label", "short_label", "is_default", "allowed_in_competition"] - label = models.CharField(max_length = 30, verbose_name = "Nom long") - short_label = models.CharField(max_length = 15, verbose_name = "Nom court") - allowed_in_competition = models.BooleanField(verbose_name = "Compétition") - is_default = models.BooleanField(verbose_name = "Défaut") + label = models.CharField(max_length=30, verbose_name="Nom long") + short_label = models.CharField(max_length=15, verbose_name="Nom court") + allowed_in_competition = models.BooleanField(verbose_name="Compétition") + is_default = models.BooleanField(verbose_name="Défaut") def __str__(self): return "%s" % (self.longLabel) @@ -104,29 +104,31 @@ class Skill(Educative): (2, "Backward"), ) - position = models.CharField(max_length = 2, choices = POSITION_CHOICES) + position = models.CharField(max_length=2, choices=POSITION_CHOICES) departure = models.ForeignKey( TouchPosition, - related_name = "depart_of", - default = get_default_position, - verbose_name = "Take-off position", - on_delete = models.CASCADE, + related_name="depart_of", + default=get_default_position, + verbose_name="Take-off position", + on_delete=models.CASCADE, ) landing = models.ForeignKey( TouchPosition, - related_name = "landing_of", - default = get_default_position, - verbose_name = "Landing position", - on_delete = models.CASCADE, + related_name="landing_of", + default=get_default_position, + verbose_name="Landing position", + on_delete=models.CASCADE, ) rotation_type = models.PositiveSmallIntegerField( - choices = ROTATION_CHOICES, verbose_name = "Type de rotation" + choices=ROTATION_CHOICES, verbose_name="Type de rotation" ) - rotation = models.PositiveSmallIntegerField(verbose_name = "1/4 de rotation") - twist = models.PositiveSmallIntegerField(verbose_name = "1/2 Vrille") - notation = models.CharField(max_length = 25) - simplified_notation = models.CharField(max_length = 25, verbose_name = "Notation simplifiée") - is_competitive = models.BooleanField(default = False) + rotation = models.PositiveSmallIntegerField(verbose_name="1/4 de rotation") + twist = models.PositiveSmallIntegerField(verbose_name="1/2 Vrille") + notation = models.CharField(max_length=25) + simplified_notation = models.CharField( + max_length=25, verbose_name="Notation simplifiée" + ) + is_competitive = models.BooleanField(default=False) # importance = models.PositiveSmallIntegerField(default = 1) def __str__(self): @@ -144,9 +146,9 @@ class Routine(Educative): active = models.BooleanField() jumps = models.ManyToManyField( - Skill, through = "RoutineSkill", verbose_name = "routine" + Skill, through="RoutineSkill", verbose_name="routine" ) # ceci n'est pas un vrai champ - is_competitive = models.BooleanField(default = False) + is_competitive = models.BooleanField(default=False) def __str__(self): return "%s (%s)" % (self.label, self.short_label) @@ -216,10 +218,10 @@ class RoutineSkill(models.Model): ordering = ("rank",) routine = models.ForeignKey( - Routine, on_delete = models.CASCADE, default = None, related_name = "skill_links" + Routine, on_delete=models.CASCADE, default=None, related_name="skill_links" ) skill = models.ForeignKey( - Skill, on_delete = models.CASCADE, default = None, related_name = "routine_links" + Skill, on_delete=models.CASCADE, default=None, related_name="routine_links" ) rank = models.PositiveSmallIntegerField() @@ -247,13 +249,13 @@ class Chrono(models.Model): (99, "Other"), ) - routine = models.ForeignKey(Routine, on_delete = models.CASCADE, default = None) - routine_type = models.PositiveSmallIntegerField(choices = ROUTINETYPE_CHOICE) + routine = models.ForeignKey(Routine, on_delete=models.CASCADE, default=None) + routine_type = models.PositiveSmallIntegerField(choices=ROUTINETYPE_CHOICE) gymnast = models.ForeignKey( - "people.gymnast", on_delete = models.CASCADE, default = None + "people.gymnast", on_delete=models.CASCADE, default=None ) - date = models.DateField(default = date.today, verbose_name = "Date") - score = models.DecimalField(max_digits = 5, decimal_places = 2) + date = models.DateField(default=date.today, verbose_name="Date") + score = models.DecimalField(max_digits=5, decimal_places=2) def __str__(self): return "%s le %s : %s pour %s" % ( @@ -278,13 +280,13 @@ class Evaluation(models.Model): Classe permettant l'évaluation des éducatifs. """ - date = models.DateField(default = date.today, verbose_name = "Date") - value = models.PositiveSmallIntegerField(default = 0) + date = models.DateField(default=date.today, verbose_name="Date") + value = models.PositiveSmallIntegerField(default=0) educative = models.ForeignKey( Educative, - related_name = "depart_of", - verbose_name = "Take-off position", - on_delete = models.CASCADE, + related_name="depart_of", + verbose_name="Take-off position", + on_delete=models.CASCADE, ) - type_of_evaluator = models.BooleanField(default = 0) - evaluator = models.ForeignKey(User, null = True, on_delete = models.SET_NULL) + type_of_evaluator = models.BooleanField(default=0) + evaluator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) diff --git a/src/objective/tests.py b/khana/objective/tests.py similarity index 100% rename from src/objective/tests.py rename to khana/objective/tests.py diff --git a/src/objective/urls.py b/khana/objective/urls.py similarity index 95% rename from src/objective/urls.py rename to khana/objective/urls.py index a8ff9a8..57e71cf 100644 --- a/src/objective/urls.py +++ b/khana/objective/urls.py @@ -42,11 +42,7 @@ routine_urlpatterns = [ views.delete_skill_from_routine, name="delete_skill_from_routine", ), - path( - r"suggest/", - views.suggest_routine, - name="suggest_routine", - ) + path(r"suggest/", views.suggest_routine, name="suggest_routine",), ] # Chrono diff --git a/src/objective/views.py b/khana/objective/views.py similarity index 90% rename from src/objective/views.py rename to khana/objective/views.py index fa171cb..dcc9a74 100644 --- a/src/objective/views.py +++ b/khana/objective/views.py @@ -338,7 +338,9 @@ def random_skill(request, skill_quantity=20, is_competitive=True): number_of_skill = skillid_list.count() selected_skillid_list = [ skillid_list[x]["id"] - for x in [random.randrange(0, number_of_skill, 1) for i in range(skill_quantity)] + for x in [ + random.randrange(0, number_of_skill, 1) for i in range(skill_quantity) + ] ] skill_list = Skill.objects.filter(id__in=selected_skillid_list) @@ -387,12 +389,14 @@ def __construct_routine( if current_routine: skill_list = Skill.objects.filter( - departure = current_routine[-1].landing - ) # , difficulty__lte = total_difficulty_score + departure=current_routine[-1].landing + ) # , difficulty__lte = total_difficulty_score if len(current_routine) == (max_routine_length - 1): skill_list = skill_list.filter(landing__longLabel="Debout") if logic and current_routine[-1].landing.longLabel == "Debout": - skill_list = skill_list.exclude(rotationType = current_routine[-1].rotationType) + skill_list = skill_list.exclude( + rotationType=current_routine[-1].rotationType + ) else: skill_list = Skill.objects.filter(departure__longLabel="Debout") @@ -404,23 +408,31 @@ def __construct_routine( min_diff_skill = total_difficulty_score max_diff_skill = total_difficulty_score + 3 else: - if math.ceil(total_difficulty_score / max_routine_length) <= max_skill_difficulty: - min_diff_skill = math.ceil(max((total_difficulty_score / max_routine_length) - 5, 0)) + if ( + math.ceil(total_difficulty_score / max_routine_length) + <= max_skill_difficulty + ): + min_diff_skill = math.ceil( + max((total_difficulty_score / max_routine_length) - 5, 0) + ) else: return - if (math.ceil(total_difficulty_score / max_routine_length) + 2) <= max_skill_difficulty: - max_diff_skill = math.ceil(total_difficulty_score / max_routine_length) + 2 + if ( + math.ceil(total_difficulty_score / max_routine_length) + 2 + ) <= max_skill_difficulty: + max_diff_skill = ( + math.ceil(total_difficulty_score / max_routine_length) + 2 + ) else: return skill_list = skill_list.filter( - difficulty__gte = (min_diff_skill / 10), - difficulty__lte = (max_diff_skill / 10) + difficulty__gte=(min_diff_skill / 10), difficulty__lte=(max_diff_skill / 10) ) if gymnast: - skill_list = skill_list.filter(cando__gymnast = gymnast) + skill_list = skill_list.filter(cando__gymnast=gymnast) for skill in skill_list: current_routine.append(skill) @@ -429,7 +441,9 @@ def __construct_routine( current_routine, max_routine_length, max_skill_difficulty, - total_difficulty_score - (skill.difficulty * 10) if total_difficulty_score is not None else None, + total_difficulty_score - (skill.difficulty * 10) + if total_difficulty_score is not None + else None, competition, logic, gymnast, @@ -442,7 +456,7 @@ def __construct_routine( def suggest_routine( request, - max_routine_length = 2, + max_routine_length=2, total_difficulty_score=None, competition=True, logic=True, @@ -471,24 +485,32 @@ def suggest_routine( total_difficulty_score = 26 if gymnast: - max_skill_difficulty = Educative.objects.values('difficulty').filter(cando__gymnast=gymnast).order_by( - "-difficulty" - )[:1][0]["difficulty"] * 10 + max_skill_difficulty = ( + Educative.objects.values("difficulty") + .filter(cando__gymnast=gymnast) + .order_by("-difficulty")[:1][0]["difficulty"] + * 10 + ) else: - max_skill_difficulty = Skill.objects.values('difficulty').order_by("-difficulty")[:1][0]["difficulty"] * 10 + max_skill_difficulty = ( + Skill.objects.values("difficulty").order_by("-difficulty")[:1][0][ + "difficulty" + ] + * 10 + ) # difficulty_scores = range(5, 45, 5) # for total_difficulty_score in difficulty_scores: # print("===============================================================================================") __construct_routine( - routine, - max_routine_length, - max_skill_difficulty, - total_difficulty_score, - competition, - logic, - gymnast, - ) + routine, + max_routine_length, + max_skill_difficulty, + total_difficulty_score, + competition, + logic, + gymnast, + ) # print(routines) diff --git a/src/people/management/__init__.py b/khana/people/__init__.py similarity index 100% rename from src/people/management/__init__.py rename to khana/people/__init__.py diff --git a/khana/people/admin.py b/khana/people/admin.py new file mode 100644 index 0000000..c4cc275 --- /dev/null +++ b/khana/people/admin.py @@ -0,0 +1,127 @@ +"""Administration des gymnastes et des personnes. + +Remarks: + * Je ne pense pas qu'il faille encore passer par `ForeignKeyAutocompleteAdmin`. + https://docs.djangoproject.com/fr/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.autocomplete_fields +""" + +from django.contrib import admin + +from django_extensions.admin import ForeignKeyAutocompleteAdmin + +from .models import ( + Gymnast, + Accident, + CanDoRelation, + ToDoRelation, + GymnastHasRoutine, +) + + +class CanDoRelationAdmin(ForeignKeyAutocompleteAdmin): + model = CanDoRelation + + list_display = ("date", "gymnast", "educative") + list_filter = ("gymnast",) + search_fields = ("gymnast", "educative") + autocomplete_fields = ("gymnast",) + + # related_search_fields = { + # 'gymnast': ('lastname', 'firstname'), + # } + + +class InlineCanDoRelation(admin.TabularInline): + model = CanDoRelation + + +class ToDoRelationAdmin(ForeignKeyAutocompleteAdmin): + model = ToDoRelation + + list_display = ("date", "gymnast", "educative") + list_filter = ("gymnast",) + search_fields = ("gymnast", "educative") + autocomplete_fields = ("gymnast",) + + # related_search_fields = { + # 'gymnast': ('lastname', 'firstname'), + # # 'educative': ('longLabel', 'shortLabel'), # TO_FRED : Pq ca marche pas ca ? + # } + + +class GymnastHasRoutineAdmin(ForeignKeyAutocompleteAdmin): + model = GymnastHasRoutine + + list_display = ("gymnast", "routine", "routine_type", "datebegin", "dateend") + list_filter = ("gymnast", "routine_type") + search_fields = ("gymnast", "routine") + autocomplete_fields = ("gymnast", "routine") + + +class InlineToDoRelation(admin.TabularInline): + model = ToDoRelation + + +class GymnastAdmin(admin.ModelAdmin): + model = Gymnast + + def lastname(self, obj): + return obj.user.last_name + + def firstname(self, obj): + return obj.user.first_name + + def email(self, obj): + return obj.user.email + + def is_active(self, obj): + return obj.user.is_active + + fields = ( + # "lastname", + # "firstname", + "birthdate", + "gender", + "club", + "niss", + "address", + "postal", + "city", + "phone", + "gsm", + # "user__email", + "fedid", + "year_of_practice", + "gsm_mother", + "email_mother", + "gsm_father", + "email_father", + # "user.is_active", + "orientation", + "trainer", + "picture", + "content", + ) + + list_display = ("lastname", "firstname", "birthdate", "age", "is_active") + list_filter = ("gender", "user__is_active") + search_fields = ("lastname", "firstname", "email") + inlines = [InlineToDoRelation, InlineCanDoRelation] + autocomplete_fields = ("club",) + + +class AccidentAdmin(admin.ModelAdmin): + model = Accident + + fields = ("date", "gymnast", "educative", "information") + list_display = ("date", "gymnast", "educative") + list_filter = ("date",) + search_fields = ("date", "gymnast", "educative") + autocomplete_fields = ["gymnast", "educative"] + + +admin.site.register(Gymnast, GymnastAdmin) +admin.site.register(Accident, AccidentAdmin) +admin.site.register(CanDoRelation, CanDoRelationAdmin) +admin.site.register(ToDoRelation, ToDoRelationAdmin) +admin.site.register(GymnastHasRoutine, GymnastHasRoutineAdmin) diff --git a/khana/people/apps.py b/khana/people/apps.py new file mode 100644 index 0000000..c22a7cc --- /dev/null +++ b/khana/people/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PeopleConfig(AppConfig): + name = "khana.people" diff --git a/khana/people/forms.py b/khana/people/forms.py new file mode 100644 index 0000000..ab913be --- /dev/null +++ b/khana/people/forms.py @@ -0,0 +1,174 @@ +"""Formulaires de gestion des données entrantes pour les gymnastes et accidents.""" + +from django import forms + +from .models import ( + Accident, + Gymnast, + GymnastHasRoutine, +) + +from django.contrib.auth.models import User + + +class AccidentForm(forms.ModelForm): + class Meta: + model = Accident + fields = ("gymnast", "educative", "date", "content") + widgets = { + "date": forms.DateInput( + attrs={ + "class": "form-control datepicker", + # "value": date.today().strftime("%Y-%m-%d"), + } + ), + "gymnast": forms.HiddenInput(), + "educative": forms.HiddenInput(), + "content": forms.Textarea( + attrs={ + "class": "form-control", + "placeholder": "Informations about accident: context (why, where, …), consequencies, …", + } + ), + } + + gymnast_related = forms.CharField( + widget=forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Searching gymnast…", + "data-ref": "#id_gymnast", + } + ) + ) + educative_related = forms.CharField( + widget=forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Searching skill…", + "data-ref": "#id_educative", + } + ) + ) + + +class UserForm(forms.ModelForm): + class Meta: + model = User + fields = ( + "last_name", + "first_name", + "email", + "is_active", + "username", + ) + + +class GymnastForm(forms.ModelForm): + + lastname = forms.CharField( + widget=forms.TextInput( + attrs={"class": "form-control", "placeholder": "Lastname"} + ) + ) + firstname = forms.CharField( + widget=forms.TextInput( + attrs={"class": "form-control", "placeholder": "Firstname"} + ) + ) + email = forms.EmailField() + # is_active = forms.CheckboxInput() + + class Meta: + model = Gymnast + fields = ( + "id", + "birthdate", + "gender", + "address", + "postal", + "city", + "phone", + "gsm", + "gsm_main_responsible", + "email_main_responsible", + "gsm_second_responsible", + "email_second_responsible", + "orientation", + "picture", + "content", + ) + + widgets = { + "id": forms.HiddenInput(), + "lastname": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Lastname"} + ), + "firstname": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Firstname"} + ), + "birthdate": forms.DateInput(attrs={"class": "form-control datepicker"}), + "gender": forms.Select(attrs={"class": "form-control"}), + "address": forms.TextInput(attrs={"class": "form-control"}), + "postal": forms.TextInput(attrs={"class": "form-control"}), + "city": forms.TextInput(attrs={"class": "form-control"}), + "phone": forms.TextInput(attrs={"class": "form-control"}), + "gsm": forms.TextInput(attrs={"class": "form-control"}), + "email": forms.EmailInput(attrs={"class": "form-control"}), + "gsm_main_responsible": forms.TextInput(attrs={"class": "form-control"}), + "email_main_responsible": forms.EmailInput(attrs={"class": "form-control"}), + "gsm_second_responsible": forms.TextInput(attrs={"class": "form-control"}), + "email_second_responsible": forms.EmailInput( + attrs={"class": "form-control"} + ), + # "is_active": forms.CheckboxInput( + # attrs={ + # "class": "bootstrap-switch mt-0", + # "data-on-label": "", + # "data-off-label": "", + # } + # ), + "orientation": forms.Select(attrs={"class": "form-control"}), + "picture": forms.Select(attrs={"class": "form-control"}), + "content": forms.Textarea( + attrs={ + "class": "form-control", + "placeholder": "Informations about the gymnast.", + } + ), + } + + +class GymnastHasRoutineForm(forms.ModelForm): + """ + """ + + class Meta: + model = GymnastHasRoutine + fields = ("gymnast", "routine", "routine_type", "datebegin", "dateend") + widgets = { + "gymnast": forms.HiddenInput(), + "routine": forms.HiddenInput(), + "routine_type": forms.Select(attrs={"class": "form-control"}), + "datebegin": forms.DateInput(attrs={"class": "form-control datepicker",}), + "dateend": forms.DateInput(attrs={"class": "form-control datepicker",}), + } + + gymnast_related = forms.CharField( + widget=forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Searching gymnast…", + "data-ref": "#id_gymnast", + } + ) + ) + routine_related = forms.CharField( + widget=forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Searching routine…", + "data-ref": "#id_routine", + } + ) + ) diff --git a/src/people/management/commands/__init__.py b/khana/people/management/__init__.py similarity index 100% rename from src/people/management/commands/__init__.py rename to khana/people/management/__init__.py diff --git a/src/people/migrations/__init__.py b/khana/people/management/commands/__init__.py similarity index 100% rename from src/people/migrations/__init__.py rename to khana/people/management/commands/__init__.py diff --git a/src/people/management/commands/superpowers.py b/khana/people/management/commands/superpowers.py similarity index 100% rename from src/people/management/commands/superpowers.py rename to khana/people/management/commands/superpowers.py diff --git a/src/people/migrations/0001_initial.py b/khana/people/migrations/0001_initial.py similarity index 100% rename from src/people/migrations/0001_initial.py rename to khana/people/migrations/0001_initial.py diff --git a/src/people/migrations/0002_gymnast_club.py b/khana/people/migrations/0002_gymnast_club.py similarity index 100% rename from src/people/migrations/0002_gymnast_club.py rename to khana/people/migrations/0002_gymnast_club.py diff --git a/src/people/migrations/0003_auto_20190330_0920.py b/khana/people/migrations/0003_auto_20190330_0920.py similarity index 100% rename from src/people/migrations/0003_auto_20190330_0920.py rename to khana/people/migrations/0003_auto_20190330_0920.py diff --git a/src/people/migrations/0004_auto_20190330_0932.py b/khana/people/migrations/0004_auto_20190330_0932.py similarity index 100% rename from src/people/migrations/0004_auto_20190330_0932.py rename to khana/people/migrations/0004_auto_20190330_0932.py diff --git a/src/people/migrations/0005_auto_20190330_1652.py b/khana/people/migrations/0005_auto_20190330_1652.py similarity index 100% rename from src/people/migrations/0005_auto_20190330_1652.py rename to khana/people/migrations/0005_auto_20190330_1652.py diff --git a/src/people/migrations/0006_auto_20190331_0735.py b/khana/people/migrations/0006_auto_20190331_0735.py similarity index 100% rename from src/people/migrations/0006_auto_20190331_0735.py rename to khana/people/migrations/0006_auto_20190331_0735.py diff --git a/src/people/migrations/0007_auto_20190401_1755.py b/khana/people/migrations/0007_auto_20190401_1755.py similarity index 100% rename from src/people/migrations/0007_auto_20190401_1755.py rename to khana/people/migrations/0007_auto_20190401_1755.py diff --git a/src/people/migrations/0008_auto_20190402_0820.py b/khana/people/migrations/0008_auto_20190402_0820.py similarity index 100% rename from src/people/migrations/0008_auto_20190402_0820.py rename to khana/people/migrations/0008_auto_20190402_0820.py diff --git a/src/people/migrations/0009_auto_20190402_1215.py b/khana/people/migrations/0009_auto_20190402_1215.py similarity index 100% rename from src/people/migrations/0009_auto_20190402_1215.py rename to khana/people/migrations/0009_auto_20190402_1215.py diff --git a/src/people/migrations/0010_auto_20190402_1356.py b/khana/people/migrations/0010_auto_20190402_1356.py similarity index 100% rename from src/people/migrations/0010_auto_20190402_1356.py rename to khana/people/migrations/0010_auto_20190402_1356.py diff --git a/src/people/migrations/0011_auto_20190402_2032.py b/khana/people/migrations/0011_auto_20190402_2032.py similarity index 100% rename from src/people/migrations/0011_auto_20190402_2032.py rename to khana/people/migrations/0011_auto_20190402_2032.py diff --git a/src/people/migrations/0012_auto_20190402_2035.py b/khana/people/migrations/0012_auto_20190402_2035.py similarity index 100% rename from src/people/migrations/0012_auto_20190402_2035.py rename to khana/people/migrations/0012_auto_20190402_2035.py diff --git a/src/people/migrations/0013_auto_20190509_0805.py b/khana/people/migrations/0013_auto_20190509_0805.py similarity index 100% rename from src/people/migrations/0013_auto_20190509_0805.py rename to khana/people/migrations/0013_auto_20190509_0805.py diff --git a/src/people/migrations/0014_auto_20210513_1058.py b/khana/people/migrations/0014_auto_20210513_1058.py similarity index 51% rename from src/people/migrations/0014_auto_20210513_1058.py rename to khana/people/migrations/0014_auto_20210513_1058.py index 1b42506..ce1b2b6 100644 --- a/src/people/migrations/0014_auto_20210513_1058.py +++ b/khana/people/migrations/0014_auto_20210513_1058.py @@ -6,18 +6,14 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('people', '0013_auto_20190509_0805'), + ("people", "0013_auto_20190509_0805"), ] operations = [ migrations.RenameField( - model_name='accident', - old_name='information', - new_name='content', + model_name="accident", old_name="information", new_name="content", ), migrations.RenameField( - model_name='gymnast', - old_name='information', - new_name='content', + model_name="gymnast", old_name="information", new_name="content", ), ] diff --git a/khana/people/migrations/0015_auto_20210514_1619.py b/khana/people/migrations/0015_auto_20210514_1619.py new file mode 100644 index 0000000..d502364 --- /dev/null +++ b/khana/people/migrations/0015_auto_20210514_1619.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.2 on 2021-05-14 16:19 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0014_auto_20210513_1058"), + ] + + operations = [ + migrations.AlterModelOptions( + name="gymnast", + options={ + "ordering": ["user__last_name", "user__first_name"], + "verbose_name": "Gymnast", + "verbose_name_plural": "Gymnasts", + }, + ), + migrations.RemoveField(model_name="gymnast", name="email",), + migrations.RemoveField(model_name="gymnast", name="firstname",), + migrations.RemoveField(model_name="gymnast", name="lastname",), + ] diff --git a/khana/people/migrations/0016_auto_20210515_0800.py b/khana/people/migrations/0016_auto_20210515_0800.py new file mode 100644 index 0000000..027393f --- /dev/null +++ b/khana/people/migrations/0016_auto_20210515_0800.py @@ -0,0 +1,38 @@ +# Generated by Django 3.2.2 on 2021-05-15 08:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0015_auto_20210514_1619"), + ] + + operations = [ + migrations.RemoveField(model_name="gymnast", name="active",), + migrations.AlterField( + model_name="gymnast", + name="fedid", + field=models.CharField( + blank=True, max_length=10, null=True, verbose_name="Federation ID" + ), + ), + migrations.AlterField( + model_name="gymnast", + name="orientation", + field=models.PositiveSmallIntegerField( + blank=True, + choices=[(None, "Unknown"), (0, "Left"), (1, "Right")], + null=True, + verbose_name="Twist side", + ), + ), + migrations.AlterField( + model_name="gymnast", + name="phone", + field=models.CharField( + blank=True, max_length=9, null=True, verbose_name="Phond" + ), + ), + ] diff --git a/src/people/migrations/0017_alter_gymnast_user.py b/khana/people/migrations/0017_alter_gymnast_user.py similarity index 55% rename from src/people/migrations/0017_alter_gymnast_user.py rename to khana/people/migrations/0017_alter_gymnast_user.py index 7773987..6a6a82d 100644 --- a/src/people/migrations/0017_alter_gymnast_user.py +++ b/khana/people/migrations/0017_alter_gymnast_user.py @@ -9,14 +9,19 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('people', '0016_auto_20210515_0800'), + ("people", "0016_auto_20210515_0800"), ] operations = [ migrations.AlterField( - model_name='gymnast', - name='user', - field=models.OneToOneField(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='gymnast', to='auth.user'), + model_name="gymnast", + name="user", + field=models.OneToOneField( + default=1, + on_delete=django.db.models.deletion.CASCADE, + related_name="gymnast", + to="auth.user", + ), preserve_default=False, ), ] diff --git a/src/people/migrations/0018_auto_20210621_1804.py b/khana/people/migrations/0018_auto_20210621_1804.py similarity index 51% rename from src/people/migrations/0018_auto_20210621_1804.py rename to khana/people/migrations/0018_auto_20210621_1804.py index a3ae865..ca77a82 100644 --- a/src/people/migrations/0018_auto_20210621_1804.py +++ b/khana/people/migrations/0018_auto_20210621_1804.py @@ -6,18 +6,14 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('people', '0017_alter_gymnast_user'), + ("people", "0017_alter_gymnast_user"), ] operations = [ migrations.RenameField( - model_name='gymnast', - old_name='fedid', - new_name='federation_id', + model_name="gymnast", old_name="fedid", new_name="federation_id", ), migrations.RenameField( - model_name='gymnast', - old_name='have_routine', - new_name='routine', + model_name="gymnast", old_name="have_routine", new_name="routine", ), ] diff --git a/khana/people/migrations/0019_auto_20210621_1809.py b/khana/people/migrations/0019_auto_20210621_1809.py new file mode 100644 index 0000000..74008b4 --- /dev/null +++ b/khana/people/migrations/0019_auto_20210621_1809.py @@ -0,0 +1,39 @@ +# Generated by Django 3.2.2 on 2021-06-21 18:09 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0018_auto_20210621_1804"), + ] + + operations = [ + migrations.RenameField( + model_name="gymnast", old_name="cando", new_name="can_do", + ), + migrations.RenameField( + model_name="gymnast", + old_name="email_father", + new_name="email_main_responsible", + ), + migrations.RenameField( + model_name="gymnast", + old_name="email_mother", + new_name="email_second_responsible", + ), + migrations.RenameField( + model_name="gymnast", + old_name="gsm_mother", + new_name="gsm_main_responsible", + ), + migrations.RenameField( + model_name="gymnast", + old_name="gsm_father", + new_name="gsm_second_responsible", + ), + migrations.RenameField( + model_name="gymnast", old_name="haveToDo", new_name="have_to_do", + ), + ] diff --git a/src/people/templatetags/__init__.py b/khana/people/migrations/__init__.py similarity index 100% rename from src/people/templatetags/__init__.py rename to khana/people/migrations/__init__.py diff --git a/khana/people/models.py b/khana/people/models.py new file mode 100644 index 0000000..c74e3d4 --- /dev/null +++ b/khana/people/models.py @@ -0,0 +1,346 @@ +"""Modélisation des gymnastes, accidents et relations à faire/faites. + +Notes: + Est-ce qu'il ne faudrait pas refactoriser les GSM père/mère ? + Avec une table d'association, et un champ qui indique la qualité du contact ? + Du coup, cela permettrait se débarasser des champs phone, gsm, gsm_father et gsm_mother. + + Comment sont gérées les évolutions ? Changement de clubs, de fédération, + + A quoi correspond le champ `year_of_practice` ? + Comment se comportera-t-il dans un an ? Dans deux ans ? + Est-ce qu'il ne faudrait pas une date de début, plutôt ? + + Idem pour la méthode `actual_year_of_pratice`. + Comme elle se base sur le champ `created_at`, il suffit que l'enregistrement ne soit pas + réalisé correctement pour que la méthode retourne une mauvaise information. + + Que signifie qu'un gymnaste soit actif ou inactif ? Est-ce que cela ne devrait pas plutôt + être géré au niveau des utilisateurs ? + + Au niveau des utilisateurs, comme un User Django a déjà les champs lastname/firstname + pourquoi ne pas les réutiliser ? On garde la clé OneToOne, mais on déplace dans l'autre + classe les champs qui peuvent l'être. `Email` s'y retrouve également. + + Les relations `cando`, `haveToDo` et `have_routine` ne sont pas correctement nommées. + Si tu as une instance de `Gymnast`, tu devrais faire ceci : + + >>> gregg = Gymnast.objects.first() + >>> gregg.have_routine <-- pas bien + >>> gregg.routines <-- ok + + Idéalement, cela pourrait même être une indirection. + + >>> gregg.routines <-- retourne la relation de type `have_routine` + >>> gregg.educatives.can_do <-- retourne les éducatifs qu'il **peut** faire + >>> gregg.educatives.must_do <-- retourne les éducatifs qu'il **doit** faire + (j'avoue ne pas tout à fait comprendre la nuance entre les deux) + + Tu as des fonctions qui ne sont pas du tout utilisées et qui pourrissent un peu le fichier. + >>> next_age() ? N'est appelé nulle part ailleurs. + >>> known_skills() <-- peut être récupéré directement via l'attribut `cando` + (du coup, tu as sans doute une piste de renommage ;-)) +""" + +from datetime import date + +from django.contrib.auth.models import User +from django.db import models + +import pendulum + +from khana.base.models import Markdownizable + + +class Gymnast(Markdownizable): + """Représente un gymnaste. + + En plus de sa signalétique (nom, prénom, date de naissance, ...), + un gymnaste aura une photo et une orientation (de vrille). + + Un gymnaste peut être actif ou inactif. + """ + + class Meta: + verbose_name = "Gymnast" + verbose_name_plural = "Gymnasts" + ordering = ["user__last_name", "user__first_name"] + + GENDER_CHOICES = ((0, "Male"), (1, "Female")) + ORIENTATION_CHOICES = ((None, "Unknown"), (0, "Left"), (1, "Right")) + + birthdate = models.DateField(verbose_name="Date de naissance") + gender = models.PositiveSmallIntegerField( + choices=GENDER_CHOICES, verbose_name="Sexe" + ) + niss = models.CharField(max_length=11, null=True, blank=True) + address = models.CharField(max_length=255, null=True, blank=True) + postal = models.CharField(max_length=6, null=True, blank=True) + city = models.CharField(max_length=150, null=True, blank=True) + phone = models.CharField(max_length=9, null=True, blank=True, verbose_name="Phond") + gsm = models.CharField(max_length=10, null=True, blank=True) + federation_id = models.CharField( + max_length=10, null=True, blank=True, verbose_name="Federation ID" + ) + gsm_main_responsible = models.CharField( + max_length=10, null=True, blank=True, verbose_name="GSM mère" + ) + email_main_responsible = models.EmailField(max_length=255, null=True, blank=True) + gsm_second_responsible = models.CharField( + max_length=10, null=True, blank=True, verbose_name="GSM père" + ) + email_second_responsible = models.EmailField(max_length=255, null=True, blank=True) + orientation = models.PositiveSmallIntegerField( + choices=ORIENTATION_CHOICES, null=True, blank=True, verbose_name="Twist side", + ) + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="gymnast") + trainer = models.ForeignKey( + User, null=True, on_delete=models.SET_NULL, related_name="gymnasts" + ) + club = models.ForeignKey( + "location.Club", null=True, on_delete=models.SET_NULL, related_name="gymnasts" + ) + can_do = models.ManyToManyField( + "objective.Educative", + through="CanDoRelation", + related_name="isdoneby", # related_name to change + ) + have_to_do = models.ManyToManyField( + "objective.Educative", + through="ToDoRelation", + related_name="mustBeDoneBy", # related_name to change + ) + routine = models.ManyToManyField( + "objective.Routine", + through="GymnastHasRoutine", + related_name="doneBy", # related_name to change + ) + picture = models.ImageField(upload_to="gymnasts", null=True, blank=True) + year_of_practice = models.PositiveSmallIntegerField(default=0) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return u"%s, %s" % (self.user.last_name, self.user.first_name) + + @property + def next_birthday(self): + """Définit la prochaine date (de fête) d'anniversaire pour cette personne. + + Returns: + Soit le jour/mois pour cette année + Soit le jour/mois pour l'année prochaine. + + Examples: en supposant qu'on soit le 23/05/2019 + >>> from datetime import date + >>> gregg = People(name='Tru', firstname='Gregg', birthdate=date(1982, 2, 5) + >>> gregg.next_birthday() + Date(2020, 2, 5) + """ + + now = pendulum.now() + + this_year_birthday = pendulum.date( + now.year, self.birthdate.month, self.birthdate.day + ) + + if this_year_birthday.is_past(): + return pendulum.date(now.year + 1, self.birthdate.month, self.birthdate.day) + return this_year_birthday + + @property + def next_birthday_in_days(self): + now = pendulum.now() + return self.next_birthday.diff(now).in_days() + + @property + def age(self): + """ Renvoie l'âge d'un gymnaste. """ + today = date.today() + return ( + today.year + - self.birthdate.year + - ((today.month, today.day) < (self.birthdate.month, self.birthdate.day)) + ) + + @property + def next_age(self): + """ Renvoie l'âge prochain du gymnaste. """ + return (self.age) + 1 + + @property + def known_skills(self): + """ Renvoie la liste des objectifs qu'un gymnaste sait faire. """ + return CanDoRelation.objects.filter(gymnast=self.id) + + @property + def actual_year_of_pratice(self): + """ + Renvoie le nombre d'année de pratique du gymnaste en se basant sur le + nombre d'année qu'il avait déjà lors de son introduction dans le + système + le nombre d'année qu'il est dans le système. + """ + period = pendulum.now() - pendulum.instance(self.created_at, "Europe/Brussels") + return int(self.year_of_practice + period.in_years()) + + +class Accident(Markdownizable): + """La classe `Accident` permet d'indiquer qu'un gymnaste est tombé durant un saut. + """ + + class Meta: + verbose_name = "Accident" + verbose_name_plural = "Accidents" + # ordering = ["date", "gymnast"] + + gymnast = models.ForeignKey( + Gymnast, + verbose_name="Gymnast", + related_name="accident", + on_delete=models.CASCADE, + ) + educative = models.ForeignKey( + "objective.Skill", + verbose_name="Skill", + related_name="accident", + on_delete=models.CASCADE, + ) + date = models.DateField(verbose_name="Date") + + def __str__(self): + return "%s, %s (%s)" % ( + self.gymnast.lastname, + self.gymnast.firstname, + self.date, + ) + + +class CanDoRelation(models.Model): + """ + Représente les objectifs qu'un gymnaste sait faire (à partir d'une date donnée). + """ + + class Meta: + verbose_name = "CanDo" + verbose_name_plural = "CanDos" + ordering = ["date", "educative", "gymnast"] + unique_together = ("gymnast", "educative") + + # TO_FRED : puis-je avoir ton avis là-dessus ? + # + # JE VAIS INDIQUER QUE DES ELEVES SAVENT FAIRE DES FIGURES (SKILL) + # + # + # QUESTIONS + # --------- + # + # 1) Cela ne dervait pas être deux relations ManyToMany dans chacune des tables GYMNAST et EDUCATIVE (ou SKILL) ? + # Un élève saura faire plusieurs figures. + # Et une figure pourra savoir être faite par plusieurs élèves... Donc, ? + # + # 2) Dans un premier temps, même si je l'appelle EDUCATIVE mon champs, pour être pérenne, + # je n'associerai QUE des figures à un élève. Je voudrais donc, dans l'admin, que mon champs + # ne soit populer qu'avec les éléments de la table SKILL et non pas tous les éléments de la + # table EDUCATIVE. + gymnast = models.ForeignKey( + Gymnast, + verbose_name="Gymnast", + related_name="toObjective", + on_delete=models.CASCADE, + ) + educative = models.ForeignKey( + "objective.Educative", + verbose_name="Skill", + related_name="cando", + on_delete=models.CASCADE, + ) + date = models.DateField(default=date.today, verbose_name="Date") + + def __str__(self): + return "%s, %s - %s" % ( + self.gymnast.user.last_name, + self.gymnast.user.first_name, + self.educative.shortLabel, + ) + + @staticmethod + def create(gymnast, skill, linkdate): + c = CanDoRelation() + c.gymnast = gymnast + c.educative = skill + c.date = linkdate + c.save() + return c + + +class ToDoRelation(models.Model): + """ + Classe représentant les objectifs qu'une gymnaste devra savoir faire pour une date donnée. + """ + + class Meta: + verbose_name = "ToDo" + verbose_name_plural = "ToDos" + ordering = ["date", "educative", "gymnast"] + unique_together = ("gymnast", "educative") + + gymnast = models.ForeignKey( + Gymnast, verbose_name="Gymnast", related_name="todo", on_delete=models.CASCADE + ) + educative = models.ForeignKey( + "objective.Educative", + verbose_name="Skill", + related_name="isInToDo", + on_delete=models.CASCADE, + ) + date = models.DateField(default=date.today, verbose_name="Date") + + def __str__(self): + return "%s, %s - %s" % ( + self.gymnast.lastname, + self.gymnast.firstname, + self.educative.shortLabel, + ) + + +class GymnastHasRoutine(models.Model): + """ + Classe représentant le lien entre les gymnastes et leurs séries. + """ + + class Meta: + verbose_name = "Gymnast Has Routine" + verbose_name_plural = "Gymnast Has Routines" + + ROUTINETYPE_CHOICE = ( + (1, "L1"), + (2, "L2"), + (3, "L3"), + (4, "L4"), + (5, "L1S"), + (6, "L2S"), + (7, "L3S"), + (8, "L4S"), + ) + + gymnast = models.ForeignKey( + Gymnast, + verbose_name="Gymnast", + related_name="has_routine", + on_delete=models.CASCADE, + ) + routine = models.ForeignKey( + "objective.Routine", + verbose_name="Routine", + related_name="used_by_gymnast", + on_delete=models.CASCADE, + ) + routine_type = models.PositiveSmallIntegerField( + choices=ROUTINETYPE_CHOICE, verbose_name="Type", default="1" + ) + datebegin = models.DateField(default=date.today, verbose_name="Date begin") + dateend = models.DateField( + default=date.today, verbose_name="Date end", null=True, blank=True + ) + + def __str__(self): + return "%s - %s : %s" % (self.gymnast, self.routine_type, self.routine) diff --git a/src/planning/__init__.py b/khana/people/templatetags/__init__.py similarity index 100% rename from src/planning/__init__.py rename to khana/people/templatetags/__init__.py diff --git a/src/people/templatetags/accident.py b/khana/people/templatetags/accident.py similarity index 100% rename from src/people/templatetags/accident.py rename to khana/people/templatetags/accident.py diff --git a/src/people/templatetags/chrono.py b/khana/people/templatetags/chrono.py similarity index 100% rename from src/people/templatetags/chrono.py rename to khana/people/templatetags/chrono.py diff --git a/src/people/templatetags/event.py b/khana/people/templatetags/event.py similarity index 100% rename from src/people/templatetags/event.py rename to khana/people/templatetags/event.py diff --git a/src/people/templatetags/format.py b/khana/people/templatetags/format.py similarity index 100% rename from src/people/templatetags/format.py rename to khana/people/templatetags/format.py diff --git a/src/people/templatetags/plannification.py b/khana/people/templatetags/plannification.py similarity index 100% rename from src/people/templatetags/plannification.py rename to khana/people/templatetags/plannification.py diff --git a/src/people/templatetags/planning.py b/khana/people/templatetags/planning.py similarity index 100% rename from src/people/templatetags/planning.py rename to khana/people/templatetags/planning.py diff --git a/src/people/templatetags/routines.py b/khana/people/templatetags/routines.py similarity index 100% rename from src/people/templatetags/routines.py rename to khana/people/templatetags/routines.py diff --git a/src/people/templatetags/scores.py b/khana/people/templatetags/scores.py similarity index 100% rename from src/people/templatetags/scores.py rename to khana/people/templatetags/scores.py diff --git a/src/people/templatetags/statistics.py b/khana/people/templatetags/statistics.py similarity index 100% rename from src/people/templatetags/statistics.py rename to khana/people/templatetags/statistics.py diff --git a/src/people/templatetags/training.py b/khana/people/templatetags/training.py similarity index 100% rename from src/people/templatetags/training.py rename to khana/people/templatetags/training.py diff --git a/src/people/tests_models.py b/khana/people/tests_models.py similarity index 62% rename from src/people/tests_models.py rename to khana/people/tests_models.py index 3bb7587..f082b8d 100644 --- a/src/people/tests_models.py +++ b/khana/people/tests_models.py @@ -10,28 +10,47 @@ def test_gymnast_tostring(): g = Gymnast(lastname="Pauchou", firstname="Fred") assert str(g) == "Pauchou, Fred" + def test_gymnaste_get_age(): - g = Gymnast(lastname="Pauchou", firstname="Fred", birthdate=datetime.strptime('03/07/1985', '%d/%m/%Y')); + g = Gymnast( + lastname="Pauchou", + firstname="Fred", + birthdate=datetime.strptime("03/07/1985", "%d/%m/%Y"), + ) assert g.age == 35 + def test_gymnaste_get_next_age(): - g = Gymnast(lastname="Pauchou", firstname="Fred", birthdate=datetime.strptime('03/07/1985', '%d/%m/%Y')); + g = Gymnast( + lastname="Pauchou", + firstname="Fred", + birthdate=datetime.strptime("03/07/1985", "%d/%m/%Y"), + ) assert g.next_age == 36 + def test_gymnaste_next_birthday(): - g = Gymnast(lastname="Pauchou", firstname="Fred", birthdate=datetime.strptime('03/07/1985', '%d/%m/%Y')); - assert g.next_birthday == datetime.strptime('03/07/2021', '%d/%m/%Y') + g = Gymnast( + lastname="Pauchou", + firstname="Fred", + birthdate=datetime.strptime("03/07/1985", "%d/%m/%Y"), + ) + assert g.next_birthday == datetime.strptime("03/07/2021", "%d/%m/%Y") + def test_gymnast_known_skills(): # @Fred : Comment tester cela ? pass + def test_gymnast_actual_year_of_pratice(): # @Fred : Comment tester cela ? pass + # class AccidentTestCase(): + def test_accident_tostring(): timing = date.today() g = Gymnast(lastname="Pauchou", firstname="Fred") diff --git a/src/people/urls.py b/khana/people/urls.py similarity index 100% rename from src/people/urls.py rename to khana/people/urls.py diff --git a/src/people/views.py b/khana/people/views.py similarity index 99% rename from src/people/views.py rename to khana/people/views.py index c66d145..414f660 100644 --- a/src/people/views.py +++ b/khana/people/views.py @@ -29,7 +29,8 @@ def gymnast_listing(request): if pattern: gymnasts_list = Gymnast.objects.filter( - Q(user__last_name__icontains=pattern) | Q(user__first_name__icontains=pattern) + Q(user__last_name__icontains=pattern) + | Q(user__first_name__icontains=pattern) ) else: gymnasts_list = Gymnast.objects.filter(user__is_active=True) diff --git a/src/planning/ExpertSystem.md b/khana/planning/ExpertSystem.md similarity index 100% rename from src/planning/ExpertSystem.md rename to khana/planning/ExpertSystem.md diff --git a/src/planning/README.md b/khana/planning/README.md similarity index 100% rename from src/planning/README.md rename to khana/planning/README.md diff --git a/src/planning/migrations/__init__.py b/khana/planning/__init__.py similarity index 100% rename from src/planning/migrations/__init__.py rename to khana/planning/__init__.py diff --git a/src/planning/admin.py b/khana/planning/admin.py similarity index 100% rename from src/planning/admin.py rename to khana/planning/admin.py diff --git a/khana/planning/apps.py b/khana/planning/apps.py new file mode 100644 index 0000000..37b04e1 --- /dev/null +++ b/khana/planning/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PlanningConfig(AppConfig): + name = "khana.planning" diff --git a/src/planning/forms.py b/khana/planning/forms.py similarity index 100% rename from src/planning/forms.py rename to khana/planning/forms.py index 9d2efb4..89a80ae 100644 --- a/src/planning/forms.py +++ b/khana/planning/forms.py @@ -1,4 +1,3 @@ - from datetime import date from django import forms @@ -7,6 +6,7 @@ from django.contrib.admin.widgets import FilteredSelectMultiple from people.models import Gymnast from .models import Unavailability, Event, PlanningLine + class UnavailabilityForm(forms.ModelForm): class Meta: model = Unavailability diff --git a/src/planning/migrations/0001_initial.py b/khana/planning/migrations/0001_initial.py similarity index 100% rename from src/planning/migrations/0001_initial.py rename to khana/planning/migrations/0001_initial.py diff --git a/src/planning/migrations/0002_course_season.py b/khana/planning/migrations/0002_course_season.py similarity index 100% rename from src/planning/migrations/0002_course_season.py rename to khana/planning/migrations/0002_course_season.py diff --git a/src/planning/migrations/0003_auto_20190316_1857.py b/khana/planning/migrations/0003_auto_20190316_1857.py similarity index 100% rename from src/planning/migrations/0003_auto_20190316_1857.py rename to khana/planning/migrations/0003_auto_20190316_1857.py diff --git a/src/planning/migrations/0004_auto_20190317_0722.py b/khana/planning/migrations/0004_auto_20190317_0722.py similarity index 100% rename from src/planning/migrations/0004_auto_20190317_0722.py rename to khana/planning/migrations/0004_auto_20190317_0722.py diff --git a/src/planning/migrations/0005_auto_20190318_0855.py b/khana/planning/migrations/0005_auto_20190318_0855.py similarity index 100% rename from src/planning/migrations/0005_auto_20190318_0855.py rename to khana/planning/migrations/0005_auto_20190318_0855.py diff --git a/src/planning/migrations/0006_auto_20190321_1739.py b/khana/planning/migrations/0006_auto_20190321_1739.py similarity index 100% rename from src/planning/migrations/0006_auto_20190321_1739.py rename to khana/planning/migrations/0006_auto_20190321_1739.py diff --git a/src/planning/migrations/0007_round_is_important.py b/khana/planning/migrations/0007_round_is_important.py similarity index 100% rename from src/planning/migrations/0007_round_is_important.py rename to khana/planning/migrations/0007_round_is_important.py diff --git a/src/planning/migrations/0008_auto_20190402_2032.py b/khana/planning/migrations/0008_auto_20190402_2032.py similarity index 100% rename from src/planning/migrations/0008_auto_20190402_2032.py rename to khana/planning/migrations/0008_auto_20190402_2032.py diff --git a/src/planning/migrations/0009_auto_20190402_2035.py b/khana/planning/migrations/0009_auto_20190402_2035.py similarity index 100% rename from src/planning/migrations/0009_auto_20190402_2035.py rename to khana/planning/migrations/0009_auto_20190402_2035.py diff --git a/src/planning/migrations/0010_auto_20190512_1401.py b/khana/planning/migrations/0010_auto_20190512_1401.py similarity index 100% rename from src/planning/migrations/0010_auto_20190512_1401.py rename to khana/planning/migrations/0010_auto_20190512_1401.py diff --git a/src/planning/migrations/0011_auto_20190512_1428.py b/khana/planning/migrations/0011_auto_20190512_1428.py similarity index 100% rename from src/planning/migrations/0011_auto_20190512_1428.py rename to khana/planning/migrations/0011_auto_20190512_1428.py diff --git a/src/planning/migrations/0012_round_coachid.py b/khana/planning/migrations/0012_round_coachid.py similarity index 100% rename from src/planning/migrations/0012_round_coachid.py rename to khana/planning/migrations/0012_round_coachid.py diff --git a/src/planning/migrations/0013_auto_20190525_0524.py b/khana/planning/migrations/0013_auto_20190525_0524.py similarity index 100% rename from src/planning/migrations/0013_auto_20190525_0524.py rename to khana/planning/migrations/0013_auto_20190525_0524.py diff --git a/src/planning/migrations/0014_auto_20190525_0526.py b/khana/planning/migrations/0014_auto_20190525_0526.py similarity index 100% rename from src/planning/migrations/0014_auto_20190525_0526.py rename to khana/planning/migrations/0014_auto_20190525_0526.py diff --git a/src/planning/migrations/0015_auto_20190525_0606.py b/khana/planning/migrations/0015_auto_20190525_0606.py similarity index 100% rename from src/planning/migrations/0015_auto_20190525_0606.py rename to khana/planning/migrations/0015_auto_20190525_0606.py diff --git a/src/planning/migrations/0016_auto_20190525_1453.py b/khana/planning/migrations/0016_auto_20190525_1453.py similarity index 100% rename from src/planning/migrations/0016_auto_20190525_1453.py rename to khana/planning/migrations/0016_auto_20190525_1453.py diff --git a/src/planning/migrations/0017_auto_20190525_1544.py b/khana/planning/migrations/0017_auto_20190525_1544.py similarity index 100% rename from src/planning/migrations/0017_auto_20190525_1544.py rename to khana/planning/migrations/0017_auto_20190525_1544.py diff --git a/src/planning/migrations/0018_remove_event_gymnasts.py b/khana/planning/migrations/0018_remove_event_gymnasts.py similarity index 100% rename from src/planning/migrations/0018_remove_event_gymnasts.py rename to khana/planning/migrations/0018_remove_event_gymnasts.py diff --git a/src/planning/migrations/0019_event_participation.py b/khana/planning/migrations/0019_event_participation.py similarity index 100% rename from src/planning/migrations/0019_event_participation.py rename to khana/planning/migrations/0019_event_participation.py diff --git a/src/planning/migrations/0020_event_gymnasts.py b/khana/planning/migrations/0020_event_gymnasts.py similarity index 100% rename from src/planning/migrations/0020_event_gymnasts.py rename to khana/planning/migrations/0020_event_gymnasts.py diff --git a/khana/planning/migrations/0021_auto_20210513_1058.py b/khana/planning/migrations/0021_auto_20210513_1058.py new file mode 100644 index 0000000..0986456 --- /dev/null +++ b/khana/planning/migrations/0021_auto_20210513_1058.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.2 on 2021-05-13 10:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("planning", "0020_event_gymnasts"), + ] + + operations = [ + migrations.RenameField( + model_name="course", old_name="information", new_name="content", + ), + migrations.RenameField( + model_name="event", old_name="information", new_name="content", + ), + migrations.RenameField( + model_name="planningline", old_name="information", new_name="content", + ), + migrations.RenameField( + model_name="round", old_name="information", new_name="content", + ), + migrations.RenameField( + model_name="unavailability", old_name="information", new_name="content", + ), + migrations.AlterField( + model_name="group", + name="season", + field=models.CharField(default="2021-2022", max_length=9), + ), + ] diff --git a/src/planning/tests/__init__.py b/khana/planning/migrations/__init__.py similarity index 100% rename from src/planning/tests/__init__.py rename to khana/planning/migrations/__init__.py diff --git a/khana/planning/models.py b/khana/planning/models.py new file mode 100644 index 0000000..ddabf7e --- /dev/null +++ b/khana/planning/models.py @@ -0,0 +1,507 @@ +from datetime import datetime, date, time, timedelta + +from django.contrib.auth.models import User +from django.db import models +from django.utils import timezone + +import pendulum + +from khana.base.models import Markdownizable +from khana.location.models import Club +from khana.people.models import Gymnast + + +def get_week(a_date): + """ + + Remarks: + Je ne comprends pas trop cette fonction... + Tu pars d'une date, et tu récupères le lundi et le samedi de la semaine correspondant ? + """ + the_date = pendulum.parse(a_date) + day = the_date.weekday() + monday = the_date - timedelta(days=day) + sunday = the_date + timedelta(days=(6 - day)) + return monday, sunday + + +def get_number_of_weeks_between(start, stop): + """ + Renvoie le nombre de semaines entre deux dates. + Par extension, cela permet de connaitre le nombre d'occurence d'un + évènement (entraînement, par exemple) hebdromadaire entre deux dates + et ainsi pouvoir plannifier. + + :param start: date de début de la période + :type start: datetime.date + :param stop: date de fin de la période + :type stop: datetime.date + :return: Le nombre de semaines entre les deux dates. + + Remarks: + Proposition d'utiliser isocalendar() sur une date. + L'indice 1 de la valeur de retour donne la semaine correspondant. + + Eg. + >>> from datetime import date + >>> d = date(2020, 9, 27) + >>> d.isocalendar() + (2020, 39, 7) + + -> Est-ce qu'il ne suffirait pas de faire la différence ? + """ + + tmp = stop - start + number_of_days = abs(tmp.days) + number_of_week = int((number_of_days + 1) / 7) + + if ((number_of_days + 1) % 7) > 0: + number_of_week += 1 + + if tmp.days < 0: + number_of_week *= -1 + + return number_of_week + + +class TemporizableQuerySet(models.QuerySet): + """ + Classe permettant de spécifier le `QuerySet` de la classe `Temporizable`. + """ + + def next(self, limit): + """ + Renvoie la liste des prochains "temporizable" (par rapport à la date du jour). + + :param limit: nombre d'éléments désirés. + :type limit: int + :return: une liste de `limit` éléments temporizables. + """ + return self.filter(datebegin__gte=timezone.now()).order_by("datebegin")[0:limit] + + def last(self, limit): + """ + Renvoie la liste des derniers "temporizable" (par rapport à la date du jour). + + :param limit: nombre d'éléments désirés. + :type limit: int + :return: une liste de `limit` éléments temporizables + """ + return self.filter(dateend__lte=timezone.now()).order_by("-dateend")[0:limit] + + # def get(self, date_string): + # """ + # """ + # try: + # selected_object = self.get(datebegin__lte=date_string, dateend__gte=date_string) + # except self.DoesNotExist: + # return None + # except self.MultipleObjectsReturned: + # return None + + # return selected_object + + +class Temporizable(models.Model): + """Classe abstraite définissant une période comprise entre deux dates. + + """ + + class Meta: + abstract = True + + datebegin = models.DateTimeField(verbose_name="Début") + dateend = models.DateTimeField(blank=True, verbose_name="Fin") + + objects = models.Manager.from_queryset(TemporizableQuerySet)() + + def get_total_occurence(self): + """ + Renvoie le nombre de semaines entre les deux dates d'une instance de la + classe `Temporizable`. + + :return: nombre de semaines. + """ + return get_number_of_weeks_between(self.datebegin.date(), self.dateend.date()) + + def get_number_of_occurence_to_event(self, the_date): + """ + Renvoie le nombre semaines entre une date choisie et le début + (datebegin) d'une instance de la classe `Temporizable`. + + :param the_date: date par rapport à laquelle le calcul sera fait. + :type the_date: datetime.date + :return: nombre de semaines. + """ + return get_number_of_weeks_between(the_date, self.datebegin.date()) + + def get_number_of_occurence_inbetween(self, the_date, rest=True): + """ + Renvoie le nombre semaines entre une date choisie et une instance de la + classe `Temporizable`. Le calcul peut se faire soit entre la date + choisie et le date de fin d'une occurence de la classe, soit entre la + date de début d'une occurence de la classe et la date choisie. + + :param the_date: date par rapport à laquelle le calcul sera fait. + :type the_date: datetime.date + :param rest: paramètre définissant s'il faut calculer le reste des + occurences à venir (depuis `the_date` jusqu'à la date de fin) ou + les occurences déjà passées (depuis la date de début jusqu'à + `the_date`) + :type rest: booléen + :return: nombre de semaines. + """ + if rest: + return get_number_of_weeks_between(the_date, self.dateend.date()) + else: + return get_number_of_weeks_between(self.datebegin.date(), the_date) + + +class Season(Temporizable): + """ + Classe représentant une saison. Une saison est déinie par : + - un id, + - un label, + - une date de début et + - une date de fin. + + La date de début est très souvent le : 01/09/xxxx + La date de fin est très souvent le : 31/08/xxxy + exemple : 1/9/2015 - 31/8/2016 + """ + + class Meta: + verbose_name = "Season" + verbose_name_plural = "Seasons" + + label = models.CharField(max_length=11, verbose_name="Label") + # active ou default = models.BooleanField(verbose_name='Défaut') + + def __str__(self): + return "%s" % (self.label) + + def week_number_from_begin(self, target_date): + return get_number_of_weeks_between(self.datebegin.date(), target_date) + + +class EventType(models.Model): + """ + Classe représentant les types d'évènements. + C'est un dictionnaire fini : + - compétiton qualificative, + - compétition finale, + - démonstration, + - … + """ + + class Meta: + verbose_name = "Event Type" + verbose_name_plural = "Event Types" + + name = models.CharField(max_length=255, verbose_name="Nom") + acronym = models.CharField(max_length=5, verbose_name="Acronyme") + + def __str__(self): + return "%s (%s)" % (self.name, self.acronym) + + +class Event(Markdownizable, Temporizable): + """Classe représentant les évènements. + + Un évènement est caractérisé par : + * un nom, + * un lieu (place), + * un type (compétition, démonstration, …), + * des gymnastes (participation prévue). + Je ne me rapelle plus à quoi sert le club. + """ + + class Meta: + verbose_name = "Event" + verbose_name_plural = "Event" + + place = models.ForeignKey( + "location.Place", verbose_name="Lieu", on_delete=models.CASCADE, default=None + ) + eventtype = models.ForeignKey( + EventType, verbose_name="Type", on_delete=models.CASCADE, default=None + ) + name = models.CharField(max_length=255, verbose_name="Nom") + # club = models.ManyToManyField('location.Club', related_name="concernate_by", blank=True) + gymnasts = models.ManyToManyField( + "people.Gymnast", + through="Event_Participation", + related_name="participate_to", + verbose_name="Participants", + ) + + def __str__(self): + return "%s (à %s)" % (self.name, self.place.city) + + def save(self, *args, **kwargs): + self.checkdates() + super().save(*args, **kwargs) + + def checkdates(self): + """ + Fonction assignant la date de fin d'un évènement à la date de début, si la date + de fin n'est pas définie, l'heure de fin est par défaut 18h00. + """ + if self.dateend is None and self.datebegin is not None: + self.dateend = datetime.combine(self.datebegin.date(), time(18, 0)) + + @property + def number_of_week_from_today(self): + today = pendulum.now().date() + return get_number_of_weeks_between(today, self.datebegin.date()) + + +class Event_Participation(models.Model): + """ + """ + + event = models.ForeignKey(Event, on_delete=models.CASCADE) + gymnast = models.ForeignKey("people.Gymnast", on_delete=models.CASCADE) + rank = models.PositiveSmallIntegerField(default=0) + + +class Course(Markdownizable, Temporizable): + """Classe représentant les cours. + + Un cours est défini par : + * une heure de début et une heure de fin, + * une date de début et une date de fin (un cours est considéré comme donné hebdromadairement entre + ces deux dates) (hérite de la classe `Temporizable`) + * est associé à un ou plusieurs entraineurs, + * est associé à un club + * est associé à un jour de la semaine (numéro du jour dans la semaine : 0 = lundi, 6 = dimanche). + """ + + class Meta: + verbose_name = "Course" + verbose_name_plural = "Courses" + + DAY_CHOICE = ( + (1, "Lundi"), + (2, "Mardi"), + (3, "Mercredi"), + (4, "Jeudi"), + (5, "Vendredi"), + (6, "Samedi"), + (7, "Dimanche"), + ) + + club = models.ForeignKey( + "location.Club", verbose_name="Club", on_delete=models.CASCADE, default=None + ) + iso_day_number = models.PositiveSmallIntegerField( + choices=DAY_CHOICE, verbose_name="Jour" + ) + hour_begin = models.TimeField(verbose_name="Heure de début") + hour_end = models.TimeField(verbose_name="Heure de fin") + season = models.ForeignKey(Season, on_delete=models.SET_NULL, null=True) + trainers = models.ManyToManyField( + User, verbose_name="Coach(es)", related_name="trainee" + ) + gymnasts = models.ManyToManyField( + Gymnast, verbose_name="Gymnasts", related_name="courses" + ) + + def __str__(self): + return "%s (%s à %s)" % ( + self.get_iso_day_number_display(), + self.hour_begin.strftime("%H:%M"), + self.hour_end.strftime("%H:%M"), + ) + + @property + def duration(self): + """ + Renvoie la durée d'un cours en heures + """ + date_begin = pendulum.datetime( + 2000, 1, 1, self.hour_begin.hour, self.hour_begin.minute + ) + date_end = pendulum.datetime( + 2000, 1, 1, self.hour_end.hour, self.hour_end.minute + ) + return date_end.diff(date_begin).in_hours() + + +class Group(models.Model): + """Classe représentant les groupes (Loisir, D1, D2, A, B, …). + + Un groupe appartient à un club. + """ + + class Meta: + verbose_name = "Group" + verbose_name_plural = "Groups" + + club = models.ForeignKey("location.Club", on_delete=models.CASCADE, default=None) + name = models.CharField(max_length=255) + acronym = models.CharField(max_length=50) + active = models.BooleanField(default=1) + season = models.CharField( + max_length=9, + default=str(timezone.now().year) + "-" + str(timezone.now().year + 1), + ) + + def __str__(self): + return "%s (%s)" % (self.name, self.acronym) + + +class Subgroup(models.Model): + """Classe représentant les sous-groupes. + + Un sous-groupe appartient à un groupe (lui-même lié à un club). + + De cette manière, quand un gymnaste est mis dans un sous-groupe, en remontant via le groupe, + nous pouvons connaître le(s) club(s) du gymnaste pour chaque saison. + """ + + class Meta: + verbose_name = "Subgroup" + verbose_name_plural = "Subgroups" + + name = models.CharField(max_length=255) + acronym = models.CharField(max_length=50) + group = models.ForeignKey(Group, on_delete=models.CASCADE, default=None) + courses = models.ManyToManyField(Course, related_name="to_subgroup") + gymnasts = models.ManyToManyField( + "people.Gymnast", related_name="to_gym", blank=True + ) + active = models.BooleanField(default=1) + + def __str__(self): + return "%s (%s)" % (self.name, self.group.name) + + +class UnavailabilityManager(models.Manager): + """Classe représentant le manager de la classe `Unavailability`. + """ + + def next(self, count): + return self.filter(datebegin__gte=timezone.now()).order_by("datebegin")[0:count] + + def last(self, count): + return self.filter(dateend__lte=timezone.now()).order_by("-dateend")[0:count] + + +class Unavailability(Markdownizable, Temporizable): + """Classe représentant les indisponibilités. + """ + + class Meta: + verbose_name = "Indisponibilité" + verbose_name_plural = "Indisponibilités" + + course = models.ManyToManyField(Course, related_name="unavailability") + + objects = UnavailabilityManager() + + def __str__(self): + return "du %s au %s" % (self.datebegin, self.dateend) + + def save(self, *args, **kwargs): + self.checkdates() + + super().save(*args, **kwargs) + + def checkdates(self): + if self.dateend is None and self.datebegin is not None: + self.dateend = self.datebegin + + +class Training(models.Model): + """Classe représentant les entraînements. + + Un entraînement est une occurence d'un cours pendant lequel des gmnastes sont présents. + + Un objet de cette classe lie donc un cours et un gymnaste à une date donnée. + """ + + class Meta: + verbose_name = "Training" + verbose_name_plural = "Trainings" + + gymnast = models.ForeignKey( + "people.Gymnast", + verbose_name="Gymnast", + on_delete=models.CASCADE, + default=None, + related_name="trainings", + ) + course = models.ForeignKey( + Course, verbose_name="Course", on_delete=models.CASCADE, default=None + ) + trainingdate = models.DateField(verbose_name="Date") + + def __str__(self): + return "%s - %s, %s" % (self.trainingdate, self.course, self.gymnast) + + @staticmethod + def create(gymnast, course, trainingdate): + t = Training() + t.gymnast = gymnast + t.course = course + t.trainingdate = trainingdate + t.save() + return t + + +class Round(Markdownizable): + """Classe représentant les passages des élèves lors d'un entrainement. + + Chaque record représente un passage. Il est donc lié à un record de la classe `Training`. + """ + + class Meta: + verbose_name = "Round" + verbose_name_plural = "Rounds" + + EVALUATION_CHOICES = ( + (0, "- -"), + (1, "- +"), + (2, "+ -"), + (3, "+ +"), + ) + + training = models.ForeignKey( + Training, on_delete=models.CASCADE, default=None, related_name="rounds" + ) + educative = models.ForeignKey( + "objective.Educative", + on_delete=models.CASCADE, + default=None, + blank=True, + null=True, + ) + round_information = models.CharField(max_length=255, blank=True, null=True) + round_number = models.PositiveSmallIntegerField(blank=True, null=True) + gymnast_evaluation = models.PositiveSmallIntegerField( + choices=EVALUATION_CHOICES, blank=True, null=True + ) + coach_evaluation = models.PositiveSmallIntegerField(blank=True, null=True) + coachid = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL) + nb_of_realisation = models.PositiveSmallIntegerField(blank=True, null=True) + nb_of_success = models.PositiveSmallIntegerField(blank=True, null=True) + is_important = models.BooleanField(default=False) + + def __str__(self): + return "%s" % (self.round_number) + + +class PlanningLine(Markdownizable): + """Classe représentant les passages prévisionnels (incubating idea). + """ + + class Meta: + verbose_name = "Planning Line" + verbose_name_plural = "Planning lines" + # ordering = ['gymnast', 'date', 'order'] + + gymnast = models.ForeignKey(Gymnast, on_delete=models.CASCADE, default=None) + date = models.DateField(verbose_name="Date") + order = models.PositiveSmallIntegerField() + todo = models.CharField(max_length=255) diff --git a/src/profile/__init__.py b/khana/planning/tests/__init__.py similarity index 100% rename from src/profile/__init__.py rename to khana/planning/tests/__init__.py diff --git a/src/planning/tests/tests_models.py b/khana/planning/tests/tests_models.py similarity index 99% rename from src/planning/tests/tests_models.py rename to khana/planning/tests/tests_models.py index 3416ed2..202bed7 100644 --- a/src/planning/tests/tests_models.py +++ b/khana/planning/tests/tests_models.py @@ -1,4 +1,3 @@ - from datetime import datetime from django.test import TestCase from ..models import get_number_of_weeks_between, Season diff --git a/src/planning/urls.py b/khana/planning/urls.py similarity index 99% rename from src/planning/urls.py rename to khana/planning/urls.py index 73400a7..9cde8bf 100644 --- a/src/planning/urls.py +++ b/khana/planning/urls.py @@ -1,4 +1,3 @@ - from django.urls import path, re_path from . import views diff --git a/src/planning/views.py b/khana/planning/views.py similarity index 99% rename from src/planning/views.py rename to khana/planning/views.py index 2fdd02c..72bc178 100644 --- a/src/planning/views.py +++ b/khana/planning/views.py @@ -1,4 +1,3 @@ - from django.shortcuts import render, get_object_or_404 from django.http import ( HttpResponseRedirect, @@ -82,8 +81,6 @@ def event_create_or_update(request, eventid=None): complètement le GET du POST. """ - - if request.method == "POST": form = EventForm(request.POST, instance=event) @@ -1302,7 +1299,7 @@ def planningline_update(request, planninglineid=None): else: form = PlanningLineForm( initial={ - "gymnast": planningline.gymnast.id, + "gymnast": planningline.gymnast.id, "gymnast_related": planningline.gymnast, "date": planningline.date, "order": planningline.order, diff --git a/src/profile/migrations/__init__.py b/khana/profile/__init__.py similarity index 100% rename from src/profile/migrations/__init__.py rename to khana/profile/__init__.py diff --git a/src/profile/admin.py b/khana/profile/admin.py similarity index 100% rename from src/profile/admin.py rename to khana/profile/admin.py diff --git a/src/profile/apps.py b/khana/profile/apps.py similarity index 71% rename from src/profile/apps.py rename to khana/profile/apps.py index 9c0b232..e48706e 100644 --- a/src/profile/apps.py +++ b/khana/profile/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class ProfileConfig(AppConfig): - name = "profile" + name = "khana.profile" diff --git a/src/profile/forms.py b/khana/profile/forms.py similarity index 100% rename from src/profile/forms.py rename to khana/profile/forms.py diff --git a/src/profile/migrations/0001_initial.py b/khana/profile/migrations/0001_initial.py similarity index 100% rename from src/profile/migrations/0001_initial.py rename to khana/profile/migrations/0001_initial.py diff --git a/src/profile/migrations/0002_auto_20190321_1739.py b/khana/profile/migrations/0002_auto_20190321_1739.py similarity index 100% rename from src/profile/migrations/0002_auto_20190321_1739.py rename to khana/profile/migrations/0002_auto_20190321_1739.py diff --git a/src/profile/migrations/0003_auto_20190330_0920.py b/khana/profile/migrations/0003_auto_20190330_0920.py similarity index 100% rename from src/profile/migrations/0003_auto_20190330_0920.py rename to khana/profile/migrations/0003_auto_20190330_0920.py diff --git a/src/profile/migrations/0004_auto_20190402_2032.py b/khana/profile/migrations/0004_auto_20190402_2032.py similarity index 100% rename from src/profile/migrations/0004_auto_20190402_2032.py rename to khana/profile/migrations/0004_auto_20190402_2032.py diff --git a/src/profile/migrations/0005_auto_20190402_2035.py b/khana/profile/migrations/0005_auto_20190402_2035.py similarity index 100% rename from src/profile/migrations/0005_auto_20190402_2035.py rename to khana/profile/migrations/0005_auto_20190402_2035.py diff --git a/src/profile/migrations/0006_profile_picture.py b/khana/profile/migrations/0006_profile_picture.py similarity index 100% rename from src/profile/migrations/0006_profile_picture.py rename to khana/profile/migrations/0006_profile_picture.py diff --git a/src/profile/migrations/0007_auto_20190413_1014.py b/khana/profile/migrations/0007_auto_20190413_1014.py similarity index 100% rename from src/profile/migrations/0007_auto_20190413_1014.py rename to khana/profile/migrations/0007_auto_20190413_1014.py diff --git a/src/profile/migrations/0008_profile_is_sidebar_minified.py b/khana/profile/migrations/0008_profile_is_sidebar_minified.py similarity index 100% rename from src/profile/migrations/0008_profile_is_sidebar_minified.py rename to khana/profile/migrations/0008_profile_is_sidebar_minified.py diff --git a/src/tools/__init__.py b/khana/profile/migrations/__init__.py similarity index 100% rename from src/tools/__init__.py rename to khana/profile/migrations/__init__.py diff --git a/src/profile/models.py b/khana/profile/models.py similarity index 100% rename from src/profile/models.py rename to khana/profile/models.py diff --git a/src/profile/urls.py b/khana/profile/urls.py similarity index 100% rename from src/profile/urls.py rename to khana/profile/urls.py diff --git a/src/profile/views.py b/khana/profile/views.py similarity index 98% rename from src/profile/views.py rename to khana/profile/views.py index 0cb8af9..ef90c6b 100644 --- a/src/profile/views.py +++ b/khana/profile/views.py @@ -82,5 +82,7 @@ def profile_update(request): else: form = ProfileForm(instance=profile) - context = {"form": form,} + context = { + "form": form, + } return render(request, "profile_create.html", context) diff --git a/src/tools/templatetags/__init__.py b/khana/tools/__init__.py similarity index 100% rename from src/tools/templatetags/__init__.py rename to khana/tools/__init__.py diff --git a/khana/tools/templatetags/__init__.py b/khana/tools/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/templatetags/menuitems.py b/khana/tools/templatetags/menuitems.py similarity index 100% rename from src/tools/templatetags/menuitems.py rename to khana/tools/templatetags/menuitems.py diff --git a/src/manage.py b/manage.py similarity index 71% rename from src/manage.py rename to manage.py index 87919cc..5ec273e 100755 --- a/src/manage.py +++ b/manage.py @@ -3,7 +3,7 @@ import os import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "khana.settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") from django.core.management import execute_from_command_line diff --git a/src/pytest.ini b/pytest.ini similarity index 100% rename from src/pytest.ini rename to pytest.ini diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 85222af..0000000 --- a/src/Makefile +++ /dev/null @@ -1,192 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Khana.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Khana.qhc" - -applehelp: - $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp - @echo - @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." - @echo "N.B. You won't be able to view it unless you put it in" \ - "~/Library/Documentation/Help or install it in your application" \ - "bundle." - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/Khana" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Khana" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/src/communication/migrations/0003_auto_20210513_1058.py b/src/communication/migrations/0003_auto_20210513_1058.py deleted file mode 100644 index 32937c0..0000000 --- a/src/communication/migrations/0003_auto_20210513_1058.py +++ /dev/null @@ -1,65 +0,0 @@ -# Generated by Django 3.2.2 on 2021-05-13 10:58 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('communication', '0002_auto_20190413_1028'), - ] - - operations = [ - migrations.RenameField( - model_name='message', - old_name='message_body', - new_name='body', - ), - migrations.RenameField( - model_name='message', - old_name='date_of_reading', - new_name='read_at', - ), - migrations.RenameField( - model_name='message', - old_name='message_title', - new_name='title', - ), - migrations.RenameField( - model_name='message', - old_name='date_of_writing', - new_name='written_at', - ), - migrations.RemoveField( - model_name='message', - name='is_read', - ), - migrations.RemoveField( - model_name='message', - name='reader', - ), - migrations.RemoveField( - model_name='message', - name='writer', - ), - migrations.AddField( - model_name='message', - name='content', - field=models.TextField(blank=True, help_text='Seul le MarkDown simple est accepté', null=True, verbose_name='Comments'), - ), - migrations.AddField( - model_name='message', - name='recipient', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='received_messages', to='auth.user'), - preserve_default=False, - ), - migrations.AddField( - model_name='message', - name='sender', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='sent_messages', to='auth.user'), - preserve_default=False, - ), - ] diff --git a/src/communication/views.py b/src/communication/views.py deleted file mode 100644 index 0f726b0..0000000 --- a/src/communication/views.py +++ /dev/null @@ -1,89 +0,0 @@ -"""Vues et fonctions pour tout ce qui touche à la communication entre plusieurs utilisateurs.""" - -from datetime import datetime - -from django.contrib.auth.decorators import login_required -from django.http import HttpResponse, HttpResponseRedirect -from django.shortcuts import render, get_object_or_404 -from django.views.decorators.http import require_http_methods -from django.urls import reverse - -from .forms import MessageForm -from .models import Message - - - -@login_required -def get_number_of_unread_message(request): - """Récupère le nombre de messages non lus associés à l'utilisateur en session. - """ - return Message.objects.filter(recipient=request.user).filter(read_at__isnull=True).count() - - -@login_required -@require_http_methods(["GET"]) -def get_received_messages(request): - """Récupère des messages recus pour l'utilisateur connecté. - """ - return request.user.received_messages.all() - - -@login_required -@require_http_methods(["GET"]) -def get_sent_messages(request): - """Récupère des messages envoyés par l'utilisateur connecté. - """ - return request.user.sent_messages.all() - - -@login_required -@require_http_methods(["GET"]) -def get_message_details(request, messageid): - """Récupère les détails (l'affichage ?) d'un message. - """ - message = get_object_or_404(Message, pk=messageid) - if not message.read_at and message.recipient == request.user.id: - message.read_at = datetime.now() - message.save() - - context = {"message": message, "type": None} - return render(request, "message_details.html", context) - - -@login_required -@require_http_methods(["POST"]) -def delete_message(request, messageid): - """Supprime le message dont la clé est passée en paramètre. - """ - try: - message = Message.objects.get(pk=messageid) - - if message.sender == request.user or message.recipient == request.user : - message.delete() - else: - return HttpResponse(401) - except: - return HttpResponse(400) - - return HttpResponse(200) - - -@login_required -@require_http_methods(["GET", "POST"]) -def compose_message(request): - """Permet à l'utilisateur connecté de rédiger un nouveau message. - """ - - if request.method == "POST": - form = MessageForm(request.POST) - - if form.is_valid(): - form.save() - return HttpResponseRedirect(reverse("sent_messages")) - - print("Invalid form") - else: - form = MessageForm() - - context = {"form": form, "writer": request.user.id} - return render(request, "message_create.html", context) diff --git a/src/media/Lucasljpg b/src/media/Lucasljpg deleted file mode 100644 index 9281951..0000000 Binary files a/src/media/Lucasljpg and /dev/null differ diff --git a/src/media/Nell_blog.jpg b/src/media/Nell_blog.jpg deleted file mode 100644 index ce6eba5..0000000 Binary files a/src/media/Nell_blog.jpg and /dev/null differ diff --git a/src/media/image.jpeg b/src/media/image.jpeg deleted file mode 100644 index 58db042..0000000 Binary files a/src/media/image.jpeg and /dev/null differ diff --git a/src/media/image_JjsR0hO.jpeg b/src/media/image_JjsR0hO.jpeg deleted file mode 100644 index 041ee75..0000000 Binary files a/src/media/image_JjsR0hO.jpeg and /dev/null differ diff --git a/src/media/image_p95XIFd.jpeg b/src/media/image_p95XIFd.jpeg deleted file mode 100644 index d00f8f9..0000000 Binary files a/src/media/image_p95XIFd.jpeg and /dev/null differ diff --git a/src/media/noelia.jpg b/src/media/noelia.jpg deleted file mode 100644 index 5230bd4..0000000 Binary files a/src/media/noelia.jpg and /dev/null differ diff --git a/src/objective/migrations/0017_auto_20210620_1618.py b/src/objective/migrations/0017_auto_20210620_1618.py deleted file mode 100644 index c704b3f..0000000 --- a/src/objective/migrations/0017_auto_20210620_1618.py +++ /dev/null @@ -1,85 +0,0 @@ -# Generated by Django 3.2.2 on 2021-06-20 16:18 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('objective', '0016_rename_information_educative_content'), - ] - - operations = [ - migrations.RenameModel( - old_name='Routine_Skill', - new_name='RoutineSkill', - ), - migrations.AlterModelOptions( - name='educative', - options={'ordering': ['label', 'short_label'], 'verbose_name': 'Educatif', 'verbose_name_plural': 'Educatifs'}, - ), - migrations.AlterModelOptions( - name='touchposition', - options={'ordering': ['label', 'short_label', 'is_default', 'allowed_in_competition'], 'verbose_name': 'Landing', 'verbose_name_plural': 'Landings'}, - ), - migrations.RenameField( - model_name='educative', - old_name='ageBoy', - new_name='age_boy', - ), - migrations.RenameField( - model_name='educative', - old_name='ageGirl', - new_name='age_girl', - ), - migrations.RenameField( - model_name='educative', - old_name='educative', - new_name='educatives', - ), - migrations.RenameField( - model_name='educative', - old_name='longLabel', - new_name='label', - ), - migrations.RenameField( - model_name='educative', - old_name='prerequisite', - new_name='prerequisites', - ), - migrations.RenameField( - model_name='educative', - old_name='shortLabel', - new_name='short_label', - ), - migrations.RenameField( - model_name='skill', - old_name='rotationType', - new_name='rotation_type', - ), - migrations.RenameField( - model_name='skill', - old_name='simplyNotation', - new_name='simplified_notation', - ), - migrations.RenameField( - model_name='touchposition', - old_name='competition', - new_name='allowed_in_competition', - ), - migrations.RenameField( - model_name='touchposition', - old_name='default', - new_name='is_default', - ), - migrations.RenameField( - model_name='touchposition', - old_name='longLabel', - new_name='label', - ), - migrations.RenameField( - model_name='touchposition', - old_name='shortLabel', - new_name='short_label', - ), - ] diff --git a/src/people/admin.py b/src/people/admin.py deleted file mode 100644 index 2c6c21e..0000000 --- a/src/people/admin.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Administration des gymnastes et des personnes. - -Remarks: - * Je ne pense pas qu'il faille encore passer par `ForeignKeyAutocompleteAdmin`. - https://docs.djangoproject.com/fr/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.autocomplete_fields -""" - -from django.contrib import admin - -from django_extensions.admin import ForeignKeyAutocompleteAdmin - -from .models import ( - Gymnast, - Accident, - CanDoRelation, - ToDoRelation, - GymnastHasRoutine, -) - -class CanDoRelationAdmin(ForeignKeyAutocompleteAdmin): - model = CanDoRelation - - list_display = ("date", "gymnast", "educative") - list_filter = ("gymnast",) - search_fields = ("gymnast", "educative") - autocomplete_fields = ("gymnast",) - - # related_search_fields = { - # 'gymnast': ('lastname', 'firstname'), - # } - - -class InlineCanDoRelation(admin.TabularInline): - model = CanDoRelation - - -class ToDoRelationAdmin(ForeignKeyAutocompleteAdmin): - model = ToDoRelation - - list_display = ("date", "gymnast", "educative") - list_filter = ("gymnast",) - search_fields = ("gymnast", "educative") - autocomplete_fields = ("gymnast",) - - # related_search_fields = { - # 'gymnast': ('lastname', 'firstname'), - # # 'educative': ('longLabel', 'shortLabel'), # TO_FRED : Pq ca marche pas ca ? - # } - - -class GymnastHasRoutineAdmin(ForeignKeyAutocompleteAdmin): - model = GymnastHasRoutine - - list_display = ("gymnast", "routine", "routine_type", "datebegin", "dateend") - list_filter = ("gymnast", "routine_type") - search_fields = ("gymnast", "routine") - autocomplete_fields = ("gymnast", "routine") - - -class InlineToDoRelation(admin.TabularInline): - model = ToDoRelation - - -class GymnastAdmin(admin.ModelAdmin): - model = Gymnast - - def lastname(self, obj): - return obj.user.last_name - - def firstname(self, obj): - return obj.user.first_name - - def email(self, obj): - return obj.user.email - - def is_active(self, obj): - return obj.user.is_active - - fields = ( - # "lastname", - # "firstname", - "birthdate", - "gender", - "club", - "niss", - "address", - "postal", - "city", - "phone", - "gsm", - # "user__email", - "fedid", - "year_of_practice", - "gsm_mother", - "email_mother", - "gsm_father", - "email_father", - # "user.is_active", - "orientation", - "trainer", - "picture", - "content", - ) - - list_display = ("lastname", "firstname", "birthdate", "age", "is_active") - list_filter = ("gender", "user__is_active") - search_fields = ("lastname", "firstname", "email") - inlines = [InlineToDoRelation, InlineCanDoRelation] - autocomplete_fields = ("club",) - - -class AccidentAdmin(admin.ModelAdmin): - model = Accident - - fields = ("date", "gymnast", "educative", "information") - list_display = ("date", "gymnast", "educative") - list_filter = ("date",) - search_fields = ("date", "gymnast", "educative") - autocomplete_fields = ["gymnast", "educative"] - - -admin.site.register(Gymnast, GymnastAdmin) -admin.site.register(Accident, AccidentAdmin) -admin.site.register(CanDoRelation, CanDoRelationAdmin) -admin.site.register(ToDoRelation, ToDoRelationAdmin) -admin.site.register(GymnastHasRoutine, GymnastHasRoutineAdmin) diff --git a/src/people/forms.py b/src/people/forms.py deleted file mode 100644 index 8816f52..0000000 --- a/src/people/forms.py +++ /dev/null @@ -1,174 +0,0 @@ -"""Formulaires de gestion des données entrantes pour les gymnastes et accidents.""" - -from django import forms - -from .models import ( - Accident, - Gymnast, - GymnastHasRoutine, -) - -from django.contrib.auth.models import User - - -class AccidentForm(forms.ModelForm): - class Meta: - model = Accident - fields = ("gymnast", "educative", "date", "content") - widgets = { - "date": forms.DateInput( - attrs={ - "class": "form-control datepicker", - # "value": date.today().strftime("%Y-%m-%d"), - } - ), - "gymnast": forms.HiddenInput(), - "educative": forms.HiddenInput(), - "content": forms.Textarea( - attrs={ - "class": "form-control", - "placeholder": "Informations about accident: context (why, where, …), consequencies, …", - } - ), - } - - gymnast_related = forms.CharField( - widget=forms.TextInput( - attrs={ - "class": "form-control", - "placeholder": "Searching gymnast…", - "data-ref": "#id_gymnast", - } - ) - ) - educative_related = forms.CharField( - widget=forms.TextInput( - attrs={ - "class": "form-control", - "placeholder": "Searching skill…", - "data-ref": "#id_educative", - } - ) - ) - - -class UserForm(forms.ModelForm): - class Meta: - model = User - fields = ( - "last_name", - "first_name", - "email", - "is_active", - "username", - ) - - -class GymnastForm(forms.ModelForm): - - lastname = forms.CharField( - widget = forms.TextInput( - attrs={ - "class": "form-control", - "placeholder":"Lastname" - } - ) - ) - firstname = forms.CharField( - widget = forms.TextInput( - attrs={ - "class": "form-control", - "placeholder":"Firstname" - } - ) - ) - email = forms.EmailField() - # is_active = forms.CheckboxInput() - - class Meta: - model = Gymnast - fields = ( - "id", - "birthdate", - "gender", - "address", - "postal", - "city", - "phone", - "gsm", - "gsm_main_responsible", - "email_main_responsible", - "gsm_second_responsible", - "email_second_responsible", - "orientation", - "picture", - "content", - ) - - widgets = { - "id": forms.HiddenInput(), - "lastname": forms.TextInput(attrs={"class": "form-control", "placeholder":"Lastname"}), - "firstname": forms.TextInput(attrs={"class": "form-control", "placeholder":"Firstname"}), - "birthdate": forms.DateInput(attrs={"class": "form-control datepicker"}), - "gender": forms.Select(attrs={"class": "form-control"}), - "address": forms.TextInput(attrs={"class": "form-control"}), - "postal": forms.TextInput(attrs={"class": "form-control"}), - "city": forms.TextInput(attrs={"class": "form-control"}), - "phone": forms.TextInput(attrs={"class": "form-control"}), - "gsm": forms.TextInput(attrs={"class": "form-control"}), - "email": forms.EmailInput(attrs={"class": "form-control"}), - "gsm_main_responsible": forms.TextInput(attrs={"class": "form-control"}), - "email_main_responsible": forms.EmailInput(attrs={"class": "form-control"}), - "gsm_second_responsible": forms.TextInput(attrs={"class": "form-control"}), - "email_second_responsible": forms.EmailInput(attrs={"class": "form-control"}), - # "is_active": forms.CheckboxInput( - # attrs={ - # "class": "bootstrap-switch mt-0", - # "data-on-label": "", - # "data-off-label": "", - # } - # ), - "orientation": forms.Select(attrs={"class": "form-control"}), - "picture": forms.Select(attrs={"class": "form-control"}), - "content": forms.Textarea( - attrs={ - "class": "form-control", - "placeholder": "Informations about the gymnast.", - } - ), - } - - -class GymnastHasRoutineForm(forms.ModelForm): - """ - """ - - class Meta: - model = GymnastHasRoutine - fields = ("gymnast", "routine", "routine_type", "datebegin", "dateend") - widgets = { - "gymnast": forms.HiddenInput(), - "routine": forms.HiddenInput(), - "routine_type": forms.Select(attrs={"class": "form-control"}), - "datebegin": forms.DateInput(attrs={"class": "form-control datepicker",}), - "dateend": forms.DateInput(attrs={"class": "form-control datepicker",}), - } - - gymnast_related = forms.CharField( - widget=forms.TextInput( - attrs={ - "class": "form-control", - "placeholder": "Searching gymnast…", - "data-ref": "#id_gymnast", - } - ) - ) - routine_related = forms.CharField( - widget=forms.TextInput( - attrs={ - "class": "form-control", - "placeholder": "Searching routine…", - "data-ref": "#id_routine", - } - ) - ) diff --git a/src/people/migrations/0015_auto_20210514_1619.py b/src/people/migrations/0015_auto_20210514_1619.py deleted file mode 100644 index 4a119d4..0000000 --- a/src/people/migrations/0015_auto_20210514_1619.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2.2 on 2021-05-14 16:19 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('people', '0014_auto_20210513_1058'), - ] - - operations = [ - migrations.AlterModelOptions( - name='gymnast', - options={'ordering': ['user__last_name', 'user__first_name'], 'verbose_name': 'Gymnast', 'verbose_name_plural': 'Gymnasts'}, - ), - migrations.RemoveField( - model_name='gymnast', - name='email', - ), - migrations.RemoveField( - model_name='gymnast', - name='firstname', - ), - migrations.RemoveField( - model_name='gymnast', - name='lastname', - ), - ] diff --git a/src/people/migrations/0016_auto_20210515_0800.py b/src/people/migrations/0016_auto_20210515_0800.py deleted file mode 100644 index 60b2456..0000000 --- a/src/people/migrations/0016_auto_20210515_0800.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 3.2.2 on 2021-05-15 08:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('people', '0015_auto_20210514_1619'), - ] - - operations = [ - migrations.RemoveField( - model_name='gymnast', - name='active', - ), - migrations.AlterField( - model_name='gymnast', - name='fedid', - field=models.CharField(blank=True, max_length=10, null=True, verbose_name='Federation ID'), - ), - migrations.AlterField( - model_name='gymnast', - name='orientation', - field=models.PositiveSmallIntegerField(blank=True, choices=[(None, 'Unknown'), (0, 'Left'), (1, 'Right')], null=True, verbose_name='Twist side'), - ), - migrations.AlterField( - model_name='gymnast', - name='phone', - field=models.CharField(blank=True, max_length=9, null=True, verbose_name='Phond'), - ), - ] diff --git a/src/people/migrations/0019_auto_20210621_1809.py b/src/people/migrations/0019_auto_20210621_1809.py deleted file mode 100644 index 2bc2775..0000000 --- a/src/people/migrations/0019_auto_20210621_1809.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 3.2.2 on 2021-06-21 18:09 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('people', '0018_auto_20210621_1804'), - ] - - operations = [ - migrations.RenameField( - model_name='gymnast', - old_name='cando', - new_name='can_do', - ), - migrations.RenameField( - model_name='gymnast', - old_name='email_father', - new_name='email_main_responsible', - ), - migrations.RenameField( - model_name='gymnast', - old_name='email_mother', - new_name='email_second_responsible', - ), - migrations.RenameField( - model_name='gymnast', - old_name='gsm_mother', - new_name='gsm_main_responsible', - ), - migrations.RenameField( - model_name='gymnast', - old_name='gsm_father', - new_name='gsm_second_responsible', - ), - migrations.RenameField( - model_name='gymnast', - old_name='haveToDo', - new_name='have_to_do', - ), - ] diff --git a/src/people/models.py b/src/people/models.py deleted file mode 100644 index 6adc6e7..0000000 --- a/src/people/models.py +++ /dev/null @@ -1,353 +0,0 @@ -"""Modélisation des gymnastes, accidents et relations à faire/faites. - -Notes: - Est-ce qu'il ne faudrait pas refactoriser les GSM père/mère ? - Avec une table d'association, et un champ qui indique la qualité du contact ? - Du coup, cela permettrait se débarasser des champs phone, gsm, gsm_father et gsm_mother. - - Comment sont gérées les évolutions ? Changement de clubs, de fédération, - - A quoi correspond le champ `year_of_practice` ? - Comment se comportera-t-il dans un an ? Dans deux ans ? - Est-ce qu'il ne faudrait pas une date de début, plutôt ? - - Idem pour la méthode `actual_year_of_pratice`. - Comme elle se base sur le champ `created_at`, il suffit que l'enregistrement ne soit pas - réalisé correctement pour que la méthode retourne une mauvaise information. - - Que signifie qu'un gymnaste soit actif ou inactif ? Est-ce que cela ne devrait pas plutôt - être géré au niveau des utilisateurs ? - - Au niveau des utilisateurs, comme un User Django a déjà les champs lastname/firstname - pourquoi ne pas les réutiliser ? On garde la clé OneToOne, mais on déplace dans l'autre - classe les champs qui peuvent l'être. `Email` s'y retrouve également. - - Les relations `cando`, `haveToDo` et `have_routine` ne sont pas correctement nommées. - Si tu as une instance de `Gymnast`, tu devrais faire ceci : - - >>> gregg = Gymnast.objects.first() - >>> gregg.have_routine <-- pas bien - >>> gregg.routines <-- ok - - Idéalement, cela pourrait même être une indirection. - - >>> gregg.routines <-- retourne la relation de type `have_routine` - >>> gregg.educatives.can_do <-- retourne les éducatifs qu'il **peut** faire - >>> gregg.educatives.must_do <-- retourne les éducatifs qu'il **doit** faire - (j'avoue ne pas tout à fait comprendre la nuance entre les deux) - - Tu as des fonctions qui ne sont pas du tout utilisées et qui pourrissent un peu le fichier. - >>> next_age() ? N'est appelé nulle part ailleurs. - >>> known_skills() <-- peut être récupéré directement via l'attribut `cando` - (du coup, tu as sans doute une piste de renommage ;-)) -""" - -from datetime import date - -from django.contrib.auth.models import User -from django.db import models - -import pendulum - -from base.models import Markdownizable - - -class Gymnast(Markdownizable): - """Représente un gymnaste. - - En plus de sa signalétique (nom, prénom, date de naissance, ...), - un gymnaste aura une photo et une orientation (de vrille). - - Un gymnaste peut être actif ou inactif. - """ - - class Meta: - verbose_name = "Gymnast" - verbose_name_plural = "Gymnasts" - ordering = ["user__last_name", "user__first_name"] - - GENDER_CHOICES = ((0, "Male"), (1, "Female")) - ORIENTATION_CHOICES = ((None, "Unknown"), (0, "Left"), (1, "Right")) - - birthdate = models.DateField(verbose_name="Date de naissance") - gender = models.PositiveSmallIntegerField( - choices=GENDER_CHOICES, verbose_name="Sexe" - ) - niss = models.CharField(max_length=11, null=True, blank=True) - address = models.CharField(max_length=255, null=True, blank=True) - postal = models.CharField(max_length=6, null=True, blank=True) - city = models.CharField(max_length=150, null=True, blank=True) - phone = models.CharField( - max_length=9, null=True, blank=True, verbose_name="Phond" - ) - gsm = models.CharField(max_length=10, null=True, blank=True) - federation_id = models.CharField( - max_length=10, null=True, blank=True, verbose_name="Federation ID" - ) - gsm_main_responsible = models.CharField( - max_length=10, null=True, blank=True, verbose_name="GSM mère" - ) - email_main_responsible = models.EmailField(max_length=255, null=True, blank=True) - gsm_second_responsible= models.CharField( - max_length=10, null=True, blank=True, verbose_name="GSM père" - ) - email_second_responsible = models.EmailField(max_length=255, null=True, blank=True) - orientation = models.PositiveSmallIntegerField( - choices=ORIENTATION_CHOICES, - null=True, - blank=True, - verbose_name="Twist side", - ) - user = models.OneToOneField( - User, on_delete=models.CASCADE, related_name="gymnast" - ) - trainer = models.ForeignKey( - User, null=True, on_delete=models.SET_NULL, related_name="gymnasts" - ) - club = models.ForeignKey( - "location.Club", null=True, on_delete=models.SET_NULL, related_name="gymnasts" - ) - can_do = models.ManyToManyField( - "objective.Educative", - through="CanDoRelation", - related_name="isdoneby", # related_name to change - ) - have_to_do = models.ManyToManyField( - "objective.Educative", - through="ToDoRelation", - related_name="mustBeDoneBy", # related_name to change - ) - routine = models.ManyToManyField( - "objective.Routine", - through="GymnastHasRoutine", - related_name="doneBy", # related_name to change - ) - picture = models.ImageField(upload_to="gymnasts", null=True, blank=True) - year_of_practice = models.PositiveSmallIntegerField(default=0) - created_at = models.DateTimeField(auto_now_add=True) - - def __str__(self): - return u"%s, %s" % (self.user.last_name, self.user.first_name) - - @property - def next_birthday(self): - """Définit la prochaine date (de fête) d'anniversaire pour cette personne. - - Returns: - Soit le jour/mois pour cette année - Soit le jour/mois pour l'année prochaine. - - Examples: en supposant qu'on soit le 23/05/2019 - >>> from datetime import date - >>> gregg = People(name='Tru', firstname='Gregg', birthdate=date(1982, 2, 5) - >>> gregg.next_birthday() - Date(2020, 2, 5) - """ - - now = pendulum.now() - - this_year_birthday = pendulum.date( - now.year, self.birthdate.month, self.birthdate.day - ) - - if this_year_birthday.is_past(): - return pendulum.date(now.year + 1, self.birthdate.month, self.birthdate.day) - return this_year_birthday - - @property - def next_birthday_in_days(self): - now = pendulum.now() - return self.next_birthday.diff(now).in_days() - - @property - def age(self): - """ Renvoie l'âge d'un gymnaste. """ - today = date.today() - return ( - today.year - - self.birthdate.year - - ((today.month, today.day) < (self.birthdate.month, self.birthdate.day)) - ) - - @property - def next_age(self): - """ Renvoie l'âge prochain du gymnaste. """ - return (self.age) + 1 - - @property - def known_skills(self): - """ Renvoie la liste des objectifs qu'un gymnaste sait faire. """ - return CanDoRelation.objects.filter(gymnast=self.id) - - @property - def actual_year_of_pratice(self): - """ - Renvoie le nombre d'année de pratique du gymnaste en se basant sur le - nombre d'année qu'il avait déjà lors de son introduction dans le - système + le nombre d'année qu'il est dans le système. - """ - period = pendulum.now() - pendulum.instance(self.created_at, "Europe/Brussels") - return int(self.year_of_practice + period.in_years()) - - -class Accident(Markdownizable): - """La classe `Accident` permet d'indiquer qu'un gymnaste est tombé durant un saut. - """ - - class Meta: - verbose_name = "Accident" - verbose_name_plural = "Accidents" - # ordering = ["date", "gymnast"] - - gymnast = models.ForeignKey( - Gymnast, - verbose_name="Gymnast", - related_name="accident", - on_delete=models.CASCADE, - ) - educative = models.ForeignKey( - "objective.Skill", - verbose_name="Skill", - related_name="accident", - on_delete=models.CASCADE, - ) - date = models.DateField(verbose_name="Date") - - def __str__(self): - return "%s, %s (%s)" % ( - self.gymnast.lastname, - self.gymnast.firstname, - self.date, - ) - - -class CanDoRelation(models.Model): - """ - Représente les objectifs qu'un gymnaste sait faire (à partir d'une date donnée). - """ - - class Meta: - verbose_name = "CanDo" - verbose_name_plural = "CanDos" - ordering = ["date", "educative", "gymnast"] - unique_together = ("gymnast", "educative") - - # TO_FRED : puis-je avoir ton avis là-dessus ? - # - # JE VAIS INDIQUER QUE DES ELEVES SAVENT FAIRE DES FIGURES (SKILL) - # - # - # QUESTIONS - # --------- - # - # 1) Cela ne dervait pas être deux relations ManyToMany dans chacune des tables GYMNAST et EDUCATIVE (ou SKILL) ? - # Un élève saura faire plusieurs figures. - # Et une figure pourra savoir être faite par plusieurs élèves... Donc, ? - # - # 2) Dans un premier temps, même si je l'appelle EDUCATIVE mon champs, pour être pérenne, - # je n'associerai QUE des figures à un élève. Je voudrais donc, dans l'admin, que mon champs - # ne soit populer qu'avec les éléments de la table SKILL et non pas tous les éléments de la - # table EDUCATIVE. - gymnast = models.ForeignKey( - Gymnast, - verbose_name="Gymnast", - related_name="toObjective", - on_delete=models.CASCADE, - ) - educative = models.ForeignKey( - "objective.Educative", - verbose_name="Skill", - related_name="cando", - on_delete=models.CASCADE, - ) - date = models.DateField(default=date.today, verbose_name="Date") - - def __str__(self): - return "%s, %s - %s" % ( - self.gymnast.user.last_name, - self.gymnast.user.first_name, - self.educative.shortLabel, - ) - - @staticmethod - def create(gymnast, skill, linkdate): - c = CanDoRelation() - c.gymnast = gymnast - c.educative = skill - c.date = linkdate - c.save() - return c - - -class ToDoRelation(models.Model): - """ - Classe représentant les objectifs qu'une gymnaste devra savoir faire pour une date donnée. - """ - - class Meta: - verbose_name = "ToDo" - verbose_name_plural = "ToDos" - ordering = ["date", "educative", "gymnast"] - unique_together = ("gymnast", "educative") - - gymnast = models.ForeignKey( - Gymnast, verbose_name="Gymnast", related_name="todo", on_delete=models.CASCADE - ) - educative = models.ForeignKey( - "objective.Educative", - verbose_name="Skill", - related_name="isInToDo", - on_delete=models.CASCADE, - ) - date = models.DateField(default=date.today, verbose_name="Date") - - def __str__(self): - return "%s, %s - %s" % ( - self.gymnast.lastname, - self.gymnast.firstname, - self.educative.shortLabel, - ) - - -class GymnastHasRoutine(models.Model): - """ - Classe représentant le lien entre les gymnastes et leurs séries. - """ - - class Meta: - verbose_name = "Gymnast Has Routine" - verbose_name_plural = "Gymnast Has Routines" - - ROUTINETYPE_CHOICE = ( - (1, "L1"), - (2, "L2"), - (3, "L3"), - (4, "L4"), - (5, "L1S"), - (6, "L2S"), - (7, "L3S"), - (8, "L4S"), - ) - - gymnast = models.ForeignKey( - Gymnast, - verbose_name="Gymnast", - related_name="has_routine", - on_delete=models.CASCADE, - ) - routine = models.ForeignKey( - "objective.Routine", - verbose_name="Routine", - related_name="used_by_gymnast", - on_delete=models.CASCADE, - ) - routine_type = models.PositiveSmallIntegerField( - choices=ROUTINETYPE_CHOICE, verbose_name="Type", default="1" - ) - datebegin = models.DateField(default=date.today, verbose_name="Date begin") - dateend = models.DateField( - default=date.today, verbose_name="Date end", null=True, blank=True - ) - - def __str__(self): - return "%s - %s : %s" % (self.gymnast, self.routine_type, self.routine) diff --git a/src/planning/migrations/0021_auto_20210513_1058.py b/src/planning/migrations/0021_auto_20210513_1058.py deleted file mode 100644 index ad43a23..0000000 --- a/src/planning/migrations/0021_auto_20210513_1058.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 3.2.2 on 2021-05-13 10:58 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('planning', '0020_event_gymnasts'), - ] - - operations = [ - migrations.RenameField( - model_name='course', - old_name='information', - new_name='content', - ), - migrations.RenameField( - model_name='event', - old_name='information', - new_name='content', - ), - migrations.RenameField( - model_name='planningline', - old_name='information', - new_name='content', - ), - migrations.RenameField( - model_name='round', - old_name='information', - new_name='content', - ), - migrations.RenameField( - model_name='unavailability', - old_name='information', - new_name='content', - ), - migrations.AlterField( - model_name='group', - name='season', - field=models.CharField(default='2021-2022', max_length=9), - ), - ] diff --git a/src/planning/models.py b/src/planning/models.py deleted file mode 100644 index 393a8f4..0000000 --- a/src/planning/models.py +++ /dev/null @@ -1,508 +0,0 @@ - -from datetime import datetime, date, time, timedelta - -from django.contrib.auth.models import User -from django.db import models -from django.utils import timezone - -import pendulum - -from base.models import Markdownizable -from location.models import Club -from people.models import Gymnast - - -def get_week(a_date): - """ - - Remarks: - Je ne comprends pas trop cette fonction... - Tu pars d'une date, et tu récupères le lundi et le samedi de la semaine correspondant ? - """ - the_date = pendulum.parse(a_date) - day = the_date.weekday() - monday = the_date - timedelta(days=day) - sunday = the_date + timedelta(days=(6 - day)) - return monday, sunday - - -def get_number_of_weeks_between(start, stop): - """ - Renvoie le nombre de semaines entre deux dates. - Par extension, cela permet de connaitre le nombre d'occurence d'un - évènement (entraînement, par exemple) hebdromadaire entre deux dates - et ainsi pouvoir plannifier. - - :param start: date de début de la période - :type start: datetime.date - :param stop: date de fin de la période - :type stop: datetime.date - :return: Le nombre de semaines entre les deux dates. - - Remarks: - Proposition d'utiliser isocalendar() sur une date. - L'indice 1 de la valeur de retour donne la semaine correspondant. - - Eg. - >>> from datetime import date - >>> d = date(2020, 9, 27) - >>> d.isocalendar() - (2020, 39, 7) - - -> Est-ce qu'il ne suffirait pas de faire la différence ? - """ - - tmp = stop - start - number_of_days = abs(tmp.days) - number_of_week = int((number_of_days + 1) / 7) - - if ((number_of_days + 1) % 7) > 0: - number_of_week += 1 - - if tmp.days < 0: - number_of_week *= -1 - - return number_of_week - - -class TemporizableQuerySet(models.QuerySet): - """ - Classe permettant de spécifier le `QuerySet` de la classe `Temporizable`. - """ - - def next(self, limit): - """ - Renvoie la liste des prochains "temporizable" (par rapport à la date du jour). - - :param limit: nombre d'éléments désirés. - :type limit: int - :return: une liste de `limit` éléments temporizables. - """ - return self.filter(datebegin__gte=timezone.now()).order_by("datebegin")[0:limit] - - def last(self, limit): - """ - Renvoie la liste des derniers "temporizable" (par rapport à la date du jour). - - :param limit: nombre d'éléments désirés. - :type limit: int - :return: une liste de `limit` éléments temporizables - """ - return self.filter(dateend__lte=timezone.now()).order_by("-dateend")[0:limit] - - # def get(self, date_string): - # """ - # """ - # try: - # selected_object = self.get(datebegin__lte=date_string, dateend__gte=date_string) - # except self.DoesNotExist: - # return None - # except self.MultipleObjectsReturned: - # return None - - # return selected_object - - -class Temporizable(models.Model): - """Classe abstraite définissant une période comprise entre deux dates. - - """ - - class Meta: - abstract = True - - datebegin = models.DateTimeField(verbose_name="Début") - dateend = models.DateTimeField(blank=True, verbose_name="Fin") - - objects = models.Manager.from_queryset(TemporizableQuerySet)() - - def get_total_occurence(self): - """ - Renvoie le nombre de semaines entre les deux dates d'une instance de la - classe `Temporizable`. - - :return: nombre de semaines. - """ - return get_number_of_weeks_between(self.datebegin.date(), self.dateend.date()) - - def get_number_of_occurence_to_event(self, the_date): - """ - Renvoie le nombre semaines entre une date choisie et le début - (datebegin) d'une instance de la classe `Temporizable`. - - :param the_date: date par rapport à laquelle le calcul sera fait. - :type the_date: datetime.date - :return: nombre de semaines. - """ - return get_number_of_weeks_between(the_date, self.datebegin.date()) - - def get_number_of_occurence_inbetween(self, the_date, rest=True): - """ - Renvoie le nombre semaines entre une date choisie et une instance de la - classe `Temporizable`. Le calcul peut se faire soit entre la date - choisie et le date de fin d'une occurence de la classe, soit entre la - date de début d'une occurence de la classe et la date choisie. - - :param the_date: date par rapport à laquelle le calcul sera fait. - :type the_date: datetime.date - :param rest: paramètre définissant s'il faut calculer le reste des - occurences à venir (depuis `the_date` jusqu'à la date de fin) ou - les occurences déjà passées (depuis la date de début jusqu'à - `the_date`) - :type rest: booléen - :return: nombre de semaines. - """ - if rest: - return get_number_of_weeks_between(the_date, self.dateend.date()) - else: - return get_number_of_weeks_between(self.datebegin.date(), the_date) - - -class Season(Temporizable): - """ - Classe représentant une saison. Une saison est déinie par : - - un id, - - un label, - - une date de début et - - une date de fin. - - La date de début est très souvent le : 01/09/xxxx - La date de fin est très souvent le : 31/08/xxxy - exemple : 1/9/2015 - 31/8/2016 - """ - - class Meta: - verbose_name = "Season" - verbose_name_plural = "Seasons" - - label = models.CharField(max_length=11, verbose_name="Label") - # active ou default = models.BooleanField(verbose_name='Défaut') - - def __str__(self): - return "%s" % (self.label) - - def week_number_from_begin(self, target_date): - return get_number_of_weeks_between(self.datebegin.date(), target_date) - - -class EventType(models.Model): - """ - Classe représentant les types d'évènements. - C'est un dictionnaire fini : - - compétiton qualificative, - - compétition finale, - - démonstration, - - … - """ - - class Meta: - verbose_name = "Event Type" - verbose_name_plural = "Event Types" - - name = models.CharField(max_length=255, verbose_name="Nom") - acronym = models.CharField(max_length=5, verbose_name="Acronyme") - - def __str__(self): - return "%s (%s)" % (self.name, self.acronym) - - -class Event(Markdownizable, Temporizable): - """Classe représentant les évènements. - - Un évènement est caractérisé par : - * un nom, - * un lieu (place), - * un type (compétition, démonstration, …), - * des gymnastes (participation prévue). - Je ne me rapelle plus à quoi sert le club. - """ - - class Meta: - verbose_name = "Event" - verbose_name_plural = "Event" - - place = models.ForeignKey( - "location.Place", verbose_name="Lieu", on_delete=models.CASCADE, default=None - ) - eventtype = models.ForeignKey( - EventType, verbose_name="Type", on_delete=models.CASCADE, default=None - ) - name = models.CharField(max_length=255, verbose_name="Nom") - # club = models.ManyToManyField('location.Club', related_name="concernate_by", blank=True) - gymnasts = models.ManyToManyField( - "people.Gymnast", - through="Event_Participation", - related_name="participate_to", - verbose_name="Participants", - ) - - def __str__(self): - return "%s (à %s)" % (self.name, self.place.city) - - def save(self, *args, **kwargs): - self.checkdates() - super().save(*args, **kwargs) - - def checkdates(self): - """ - Fonction assignant la date de fin d'un évènement à la date de début, si la date - de fin n'est pas définie, l'heure de fin est par défaut 18h00. - """ - if self.dateend is None and self.datebegin is not None: - self.dateend = datetime.combine(self.datebegin.date(), time(18, 0)) - - @property - def number_of_week_from_today(self): - today = pendulum.now().date() - return get_number_of_weeks_between(today, self.datebegin.date()) - - -class Event_Participation(models.Model): - """ - """ - - event = models.ForeignKey(Event, on_delete=models.CASCADE) - gymnast = models.ForeignKey("people.Gymnast", on_delete=models.CASCADE) - rank = models.PositiveSmallIntegerField(default=0) - - -class Course(Markdownizable, Temporizable): - """Classe représentant les cours. - - Un cours est défini par : - * une heure de début et une heure de fin, - * une date de début et une date de fin (un cours est considéré comme donné hebdromadairement entre - ces deux dates) (hérite de la classe `Temporizable`) - * est associé à un ou plusieurs entraineurs, - * est associé à un club - * est associé à un jour de la semaine (numéro du jour dans la semaine : 0 = lundi, 6 = dimanche). - """ - - class Meta: - verbose_name = "Course" - verbose_name_plural = "Courses" - - DAY_CHOICE = ( - (1, "Lundi"), - (2, "Mardi"), - (3, "Mercredi"), - (4, "Jeudi"), - (5, "Vendredi"), - (6, "Samedi"), - (7, "Dimanche"), - ) - - club = models.ForeignKey( - "location.Club", verbose_name="Club", on_delete=models.CASCADE, default=None - ) - iso_day_number = models.PositiveSmallIntegerField( - choices=DAY_CHOICE, verbose_name="Jour" - ) - hour_begin = models.TimeField(verbose_name="Heure de début") - hour_end = models.TimeField(verbose_name="Heure de fin") - season = models.ForeignKey(Season, on_delete=models.SET_NULL, null=True) - trainers = models.ManyToManyField( - User, verbose_name="Coach(es)", related_name="trainee" - ) - gymnasts = models.ManyToManyField( - Gymnast, verbose_name="Gymnasts", related_name="courses" - ) - - def __str__(self): - return "%s (%s à %s)" % ( - self.get_iso_day_number_display(), - self.hour_begin.strftime("%H:%M"), - self.hour_end.strftime("%H:%M"), - ) - - @property - def duration(self): - """ - Renvoie la durée d'un cours en heures - """ - date_begin = pendulum.datetime( - 2000, 1, 1, self.hour_begin.hour, self.hour_begin.minute - ) - date_end = pendulum.datetime( - 2000, 1, 1, self.hour_end.hour, self.hour_end.minute - ) - return date_end.diff(date_begin).in_hours() - - -class Group(models.Model): - """Classe représentant les groupes (Loisir, D1, D2, A, B, …). - - Un groupe appartient à un club. - """ - - class Meta: - verbose_name = "Group" - verbose_name_plural = "Groups" - - club = models.ForeignKey("location.Club", on_delete=models.CASCADE, default=None) - name = models.CharField(max_length=255) - acronym = models.CharField(max_length=50) - active = models.BooleanField(default=1) - season = models.CharField( - max_length=9, - default=str(timezone.now().year) + "-" + str(timezone.now().year + 1), - ) - - def __str__(self): - return "%s (%s)" % (self.name, self.acronym) - - -class Subgroup(models.Model): - """Classe représentant les sous-groupes. - - Un sous-groupe appartient à un groupe (lui-même lié à un club). - - De cette manière, quand un gymnaste est mis dans un sous-groupe, en remontant via le groupe, - nous pouvons connaître le(s) club(s) du gymnaste pour chaque saison. - """ - - class Meta: - verbose_name = "Subgroup" - verbose_name_plural = "Subgroups" - - name = models.CharField(max_length=255) - acronym = models.CharField(max_length=50) - group = models.ForeignKey(Group, on_delete=models.CASCADE, default=None) - courses = models.ManyToManyField(Course, related_name="to_subgroup") - gymnasts = models.ManyToManyField( - "people.Gymnast", related_name="to_gym", blank=True - ) - active = models.BooleanField(default=1) - - def __str__(self): - return "%s (%s)" % (self.name, self.group.name) - - -class UnavailabilityManager(models.Manager): - """Classe représentant le manager de la classe `Unavailability`. - """ - - def next(self, count): - return self.filter(datebegin__gte=timezone.now()).order_by("datebegin")[0:count] - - def last(self, count): - return self.filter(dateend__lte=timezone.now()).order_by("-dateend")[0:count] - - -class Unavailability(Markdownizable, Temporizable): - """Classe représentant les indisponibilités. - """ - - class Meta: - verbose_name = "Indisponibilité" - verbose_name_plural = "Indisponibilités" - - course = models.ManyToManyField(Course, related_name="unavailability") - - objects = UnavailabilityManager() - - def __str__(self): - return "du %s au %s" % (self.datebegin, self.dateend) - - def save(self, *args, **kwargs): - self.checkdates() - - super().save(*args, **kwargs) - - def checkdates(self): - if self.dateend is None and self.datebegin is not None: - self.dateend = self.datebegin - - -class Training(models.Model): - """Classe représentant les entraînements. - - Un entraînement est une occurence d'un cours pendant lequel des gmnastes sont présents. - - Un objet de cette classe lie donc un cours et un gymnaste à une date donnée. - """ - - class Meta: - verbose_name = "Training" - verbose_name_plural = "Trainings" - - gymnast = models.ForeignKey( - "people.Gymnast", - verbose_name="Gymnast", - on_delete=models.CASCADE, - default=None, - related_name="trainings", - ) - course = models.ForeignKey( - Course, verbose_name="Course", on_delete=models.CASCADE, default=None - ) - trainingdate = models.DateField(verbose_name="Date") - - def __str__(self): - return "%s - %s, %s" % (self.trainingdate, self.course, self.gymnast) - - @staticmethod - def create(gymnast, course, trainingdate): - t = Training() - t.gymnast = gymnast - t.course = course - t.trainingdate = trainingdate - t.save() - return t - - -class Round(Markdownizable): - """Classe représentant les passages des élèves lors d'un entrainement. - - Chaque record représente un passage. Il est donc lié à un record de la classe `Training`. - """ - - class Meta: - verbose_name = "Round" - verbose_name_plural = "Rounds" - - EVALUATION_CHOICES = ( - (0, "- -"), - (1, "- +"), - (2, "+ -"), - (3, "+ +"), - ) - - training = models.ForeignKey( - Training, on_delete=models.CASCADE, default=None, related_name="rounds" - ) - educative = models.ForeignKey( - "objective.Educative", - on_delete=models.CASCADE, - default=None, - blank=True, - null=True, - ) - round_information = models.CharField(max_length=255, blank=True, null=True) - round_number = models.PositiveSmallIntegerField(blank=True, null=True) - gymnast_evaluation = models.PositiveSmallIntegerField( - choices=EVALUATION_CHOICES, blank=True, null=True - ) - coach_evaluation = models.PositiveSmallIntegerField(blank=True, null=True) - coachid = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL) - nb_of_realisation = models.PositiveSmallIntegerField(blank=True, null=True) - nb_of_success = models.PositiveSmallIntegerField(blank=True, null=True) - is_important = models.BooleanField(default=False) - - def __str__(self): - return "%s" % (self.round_number) - - -class PlanningLine(Markdownizable): - """Classe représentant les passages prévisionnels (incubating idea). - """ - - class Meta: - verbose_name = "Planning Line" - verbose_name_plural = "Planning lines" - # ordering = ['gymnast', 'date', 'order'] - - gymnast = models.ForeignKey(Gymnast, on_delete=models.CASCADE, default=None) - date = models.DateField(verbose_name="Date") - order = models.PositiveSmallIntegerField() - todo = models.CharField(max_length=255) diff --git a/src/static/css/app2.css b/static/css/app2.css similarity index 100% rename from src/static/css/app2.css rename to static/css/app2.css diff --git a/src/static/css/application.css b/static/css/application.css similarity index 100% rename from src/static/css/application.css rename to static/css/application.css diff --git a/src/static/css/black-dashboard.css b/static/css/black-dashboard.css similarity index 100% rename from src/static/css/black-dashboard.css rename to static/css/black-dashboard.css diff --git a/src/static/css/black-dashboard.css.map b/static/css/black-dashboard.css.map similarity index 100% rename from src/static/css/black-dashboard.css.map rename to static/css/black-dashboard.css.map diff --git a/src/static/css/black-dashboard.min.css b/static/css/black-dashboard.min.css similarity index 100% rename from src/static/css/black-dashboard.min.css rename to static/css/black-dashboard.min.css diff --git a/src/static/css/nucleo-icons.css b/static/css/nucleo-icons.css similarity index 100% rename from src/static/css/nucleo-icons.css rename to static/css/nucleo-icons.css diff --git a/src/static/fonts/nucleo.eot b/static/fonts/nucleo.eot similarity index 100% rename from src/static/fonts/nucleo.eot rename to static/fonts/nucleo.eot diff --git a/src/static/fonts/nucleo.ttf b/static/fonts/nucleo.ttf similarity index 100% rename from src/static/fonts/nucleo.ttf rename to static/fonts/nucleo.ttf diff --git a/src/static/fonts/nucleo.woff b/static/fonts/nucleo.woff similarity index 100% rename from src/static/fonts/nucleo.woff rename to static/fonts/nucleo.woff diff --git a/src/static/fonts/nucleo.woff2 b/static/fonts/nucleo.woff2 similarity index 100% rename from src/static/fonts/nucleo.woff2 rename to static/fonts/nucleo.woff2 diff --git a/src/static/img/AU.png b/static/img/AU.png similarity index 100% rename from src/static/img/AU.png rename to static/img/AU.png diff --git a/src/static/img/BR.png b/static/img/BR.png similarity index 100% rename from src/static/img/BR.png rename to static/img/BR.png diff --git a/src/static/img/DE.png b/static/img/DE.png similarity index 100% rename from src/static/img/DE.png rename to static/img/DE.png diff --git a/src/static/img/GB.png b/static/img/GB.png similarity index 100% rename from src/static/img/GB.png rename to static/img/GB.png diff --git a/src/static/img/RO.png b/static/img/RO.png similarity index 100% rename from src/static/img/RO.png rename to static/img/RO.png diff --git a/src/static/img/US.png b/static/img/US.png similarity index 100% rename from src/static/img/US.png rename to static/img/US.png diff --git a/src/static/img/apple-icon.png b/static/img/apple-icon.png similarity index 100% rename from src/static/img/apple-icon.png rename to static/img/apple-icon.png diff --git a/src/static/img/asc.gif b/static/img/asc.gif similarity index 100% rename from src/static/img/asc.gif rename to static/img/asc.gif diff --git a/src/static/img/bg.gif b/static/img/bg.gif similarity index 100% rename from src/static/img/bg.gif rename to static/img/bg.gif diff --git a/src/static/img/bg1.jpg b/static/img/bg1.jpg similarity index 100% rename from src/static/img/bg1.jpg rename to static/img/bg1.jpg diff --git a/src/static/img/bg13.jpg b/static/img/bg13.jpg similarity index 100% rename from src/static/img/bg13.jpg rename to static/img/bg13.jpg diff --git a/src/static/img/bg14.jpg b/static/img/bg14.jpg similarity index 100% rename from src/static/img/bg14.jpg rename to static/img/bg14.jpg diff --git a/src/static/img/bg15.jpg b/static/img/bg15.jpg similarity index 100% rename from src/static/img/bg15.jpg rename to static/img/bg15.jpg diff --git a/src/static/img/bg16.jpg b/static/img/bg16.jpg similarity index 100% rename from src/static/img/bg16.jpg rename to static/img/bg16.jpg diff --git a/src/static/img/bg3.jpg b/static/img/bg3.jpg similarity index 100% rename from src/static/img/bg3.jpg rename to static/img/bg3.jpg diff --git a/src/static/img/bg5.jpg b/static/img/bg5.jpg similarity index 100% rename from src/static/img/bg5.jpg rename to static/img/bg5.jpg diff --git a/src/static/img/card-danger.png b/static/img/card-danger.png similarity index 100% rename from src/static/img/card-danger.png rename to static/img/card-danger.png diff --git a/src/static/img/card-info.png b/static/img/card-info.png similarity index 100% rename from src/static/img/card-info.png rename to static/img/card-info.png diff --git a/src/static/img/card-primary.png b/static/img/card-primary.png similarity index 100% rename from src/static/img/card-primary.png rename to static/img/card-primary.png diff --git a/src/static/img/card-success.png b/static/img/card-success.png similarity index 100% rename from src/static/img/card-success.png rename to static/img/card-success.png diff --git a/src/static/img/card-warning.png b/static/img/card-warning.png similarity index 100% rename from src/static/img/card-warning.png rename to static/img/card-warning.png diff --git a/src/static/img/default-avatar.png b/static/img/default-avatar.png similarity index 100% rename from src/static/img/default-avatar.png rename to static/img/default-avatar.png diff --git a/src/static/img/desc.gif b/static/img/desc.gif similarity index 100% rename from src/static/img/desc.gif rename to static/img/desc.gif diff --git a/src/static/img/emilyz.jpg b/static/img/emilyz.jpg similarity index 100% rename from src/static/img/emilyz.jpg rename to static/img/emilyz.jpg diff --git a/src/static/img/favicon.png b/static/img/favicon.png similarity index 100% rename from src/static/img/favicon.png rename to static/img/favicon.png diff --git a/src/static/img/gucci.png b/static/img/gucci.png similarity index 100% rename from src/static/img/gucci.png rename to static/img/gucci.png diff --git a/src/static/img/header.jpg b/static/img/header.jpg similarity index 100% rename from src/static/img/header.jpg rename to static/img/header.jpg diff --git a/src/static/img/image_placeholder.jpg b/static/img/image_placeholder.jpg similarity index 100% rename from src/static/img/image_placeholder.jpg rename to static/img/image_placeholder.jpg diff --git a/src/static/img/img_3115.jpg b/static/img/img_3115.jpg similarity index 100% rename from src/static/img/img_3115.jpg rename to static/img/img_3115.jpg diff --git a/src/static/img/jacket.png b/static/img/jacket.png similarity index 100% rename from src/static/img/jacket.png rename to static/img/jacket.png diff --git a/src/static/img/james.jpg b/static/img/james.jpg similarity index 100% rename from src/static/img/james.jpg rename to static/img/james.jpg diff --git a/src/static/img/jana.jpg b/static/img/jana.jpg similarity index 100% rename from src/static/img/jana.jpg rename to static/img/jana.jpg diff --git a/src/static/img/lora.jpg b/static/img/lora.jpg similarity index 100% rename from src/static/img/lora.jpg rename to static/img/lora.jpg diff --git a/src/static/img/mike.jpg b/static/img/mike.jpg similarity index 100% rename from src/static/img/mike.jpg rename to static/img/mike.jpg diff --git a/src/static/img/placeholder.jpg b/static/img/placeholder.jpg similarity index 100% rename from src/static/img/placeholder.jpg rename to static/img/placeholder.jpg diff --git a/src/static/img/robi.jpg b/static/img/robi.jpg similarity index 100% rename from src/static/img/robi.jpg rename to static/img/robi.jpg diff --git a/src/static/img/saint-laurent.jpg b/static/img/saint-laurent.jpg similarity index 100% rename from src/static/img/saint-laurent.jpg rename to static/img/saint-laurent.jpg diff --git a/src/static/img/t-shirt.png b/static/img/t-shirt.png similarity index 100% rename from src/static/img/t-shirt.png rename to static/img/t-shirt.png diff --git a/src/static/img/tania.jpg b/static/img/tania.jpg similarity index 100% rename from src/static/img/tania.jpg rename to static/img/tania.jpg diff --git a/src/static/js/black-dashboard.js b/static/js/black-dashboard.js similarity index 100% rename from src/static/js/black-dashboard.js rename to static/js/black-dashboard.js diff --git a/src/static/js/black-dashboard.js.map b/static/js/black-dashboard.js.map similarity index 100% rename from src/static/js/black-dashboard.js.map rename to static/js/black-dashboard.js.map diff --git a/src/static/js/black-dashboard.min.js b/static/js/black-dashboard.min.js similarity index 100% rename from src/static/js/black-dashboard.min.js rename to static/js/black-dashboard.min.js diff --git a/src/static/js/core/bootstrap.min.js b/static/js/core/bootstrap.min.js similarity index 100% rename from src/static/js/core/bootstrap.min.js rename to static/js/core/bootstrap.min.js diff --git a/src/static/js/core/jquery.min.js b/static/js/core/jquery.min.js similarity index 100% rename from src/static/js/core/jquery.min.js rename to static/js/core/jquery.min.js diff --git a/src/static/js/core/popper.min.js b/static/js/core/popper.min.js similarity index 100% rename from src/static/js/core/popper.min.js rename to static/js/core/popper.min.js diff --git a/src/static/js/courseAdmin.js b/static/js/courseAdmin.js similarity index 100% rename from src/static/js/courseAdmin.js rename to static/js/courseAdmin.js diff --git a/src/static/js/plugins/bootstrap-datetimepicker.js b/static/js/plugins/bootstrap-datetimepicker.js similarity index 100% rename from src/static/js/plugins/bootstrap-datetimepicker.js rename to static/js/plugins/bootstrap-datetimepicker.js diff --git a/src/static/js/plugins/bootstrap-notify.js b/static/js/plugins/bootstrap-notify.js similarity index 100% rename from src/static/js/plugins/bootstrap-notify.js rename to static/js/plugins/bootstrap-notify.js diff --git a/src/static/js/plugins/bootstrap-selectpicker.js b/static/js/plugins/bootstrap-selectpicker.js similarity index 100% rename from src/static/js/plugins/bootstrap-selectpicker.js rename to static/js/plugins/bootstrap-selectpicker.js diff --git a/src/static/js/plugins/bootstrap-switch.js b/static/js/plugins/bootstrap-switch.js similarity index 100% rename from src/static/js/plugins/bootstrap-switch.js rename to static/js/plugins/bootstrap-switch.js diff --git a/src/static/js/plugins/bootstrap-tagsinput.js b/static/js/plugins/bootstrap-tagsinput.js similarity index 100% rename from src/static/js/plugins/bootstrap-tagsinput.js rename to static/js/plugins/bootstrap-tagsinput.js diff --git a/src/static/js/plugins/chartjs.min.js b/static/js/plugins/chartjs.min.js similarity index 100% rename from src/static/js/plugins/chartjs.min.js rename to static/js/plugins/chartjs.min.js diff --git a/src/static/js/plugins/fullcalendar.min.js b/static/js/plugins/fullcalendar.min.js similarity index 100% rename from src/static/js/plugins/fullcalendar.min.js rename to static/js/plugins/fullcalendar.min.js diff --git a/src/static/js/plugins/jasny-bootstrap.min.js b/static/js/plugins/jasny-bootstrap.min.js similarity index 100% rename from src/static/js/plugins/jasny-bootstrap.min.js rename to static/js/plugins/jasny-bootstrap.min.js diff --git a/src/static/js/plugins/jquery-jvectormap.js b/static/js/plugins/jquery-jvectormap.js similarity index 100% rename from src/static/js/plugins/jquery-jvectormap.js rename to static/js/plugins/jquery-jvectormap.js diff --git a/src/static/js/plugins/jquery.bootstrap-wizard.js b/static/js/plugins/jquery.bootstrap-wizard.js similarity index 100% rename from src/static/js/plugins/jquery.bootstrap-wizard.js rename to static/js/plugins/jquery.bootstrap-wizard.js diff --git a/src/static/js/plugins/jquery.dataTables.min.js b/static/js/plugins/jquery.dataTables.min.js similarity index 100% rename from src/static/js/plugins/jquery.dataTables.min.js rename to static/js/plugins/jquery.dataTables.min.js diff --git a/src/static/js/plugins/jquery.tablesorter.js b/static/js/plugins/jquery.tablesorter.js similarity index 100% rename from src/static/js/plugins/jquery.tablesorter.js rename to static/js/plugins/jquery.tablesorter.js diff --git a/src/static/js/plugins/jquery.validate.min.js b/static/js/plugins/jquery.validate.min.js similarity index 100% rename from src/static/js/plugins/jquery.validate.min.js rename to static/js/plugins/jquery.validate.min.js diff --git a/src/static/js/plugins/jqueryui/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png b/static/js/plugins/jqueryui/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png rename to static/js/plugins/jqueryui/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png diff --git a/src/static/js/plugins/jqueryui/images/ui-bg_glass_100_e4f1fb_1x400.png b/static/js/plugins/jqueryui/images/ui-bg_glass_100_e4f1fb_1x400.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-bg_glass_100_e4f1fb_1x400.png rename to static/js/plugins/jqueryui/images/ui-bg_glass_100_e4f1fb_1x400.png diff --git a/src/static/js/plugins/jqueryui/images/ui-bg_glass_50_3baae3_1x400.png b/static/js/plugins/jqueryui/images/ui-bg_glass_50_3baae3_1x400.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-bg_glass_50_3baae3_1x400.png rename to static/js/plugins/jqueryui/images/ui-bg_glass_50_3baae3_1x400.png diff --git a/src/static/js/plugins/jqueryui/images/ui-bg_glass_80_d7ebf9_1x400.png b/static/js/plugins/jqueryui/images/ui-bg_glass_80_d7ebf9_1x400.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-bg_glass_80_d7ebf9_1x400.png rename to static/js/plugins/jqueryui/images/ui-bg_glass_80_d7ebf9_1x400.png diff --git a/src/static/js/plugins/jqueryui/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png b/static/js/plugins/jqueryui/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png rename to static/js/plugins/jqueryui/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png diff --git a/src/static/js/plugins/jqueryui/images/ui-bg_highlight-hard_70_000000_1x100.png b/static/js/plugins/jqueryui/images/ui-bg_highlight-hard_70_000000_1x100.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-bg_highlight-hard_70_000000_1x100.png rename to static/js/plugins/jqueryui/images/ui-bg_highlight-hard_70_000000_1x100.png diff --git a/src/static/js/plugins/jqueryui/images/ui-bg_highlight-soft_100_deedf7_1x100.png b/static/js/plugins/jqueryui/images/ui-bg_highlight-soft_100_deedf7_1x100.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-bg_highlight-soft_100_deedf7_1x100.png rename to static/js/plugins/jqueryui/images/ui-bg_highlight-soft_100_deedf7_1x100.png diff --git a/src/static/js/plugins/jqueryui/images/ui-bg_highlight-soft_25_ffef8f_1x100.png b/static/js/plugins/jqueryui/images/ui-bg_highlight-soft_25_ffef8f_1x100.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-bg_highlight-soft_25_ffef8f_1x100.png rename to static/js/plugins/jqueryui/images/ui-bg_highlight-soft_25_ffef8f_1x100.png diff --git a/src/static/js/plugins/jqueryui/images/ui-icons_2694e8_256x240.png b/static/js/plugins/jqueryui/images/ui-icons_2694e8_256x240.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-icons_2694e8_256x240.png rename to static/js/plugins/jqueryui/images/ui-icons_2694e8_256x240.png diff --git a/src/static/js/plugins/jqueryui/images/ui-icons_2e83ff_256x240.png b/static/js/plugins/jqueryui/images/ui-icons_2e83ff_256x240.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-icons_2e83ff_256x240.png rename to static/js/plugins/jqueryui/images/ui-icons_2e83ff_256x240.png diff --git a/src/static/js/plugins/jqueryui/images/ui-icons_3d80b3_256x240.png b/static/js/plugins/jqueryui/images/ui-icons_3d80b3_256x240.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-icons_3d80b3_256x240.png rename to static/js/plugins/jqueryui/images/ui-icons_3d80b3_256x240.png diff --git a/src/static/js/plugins/jqueryui/images/ui-icons_72a7cf_256x240.png b/static/js/plugins/jqueryui/images/ui-icons_72a7cf_256x240.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-icons_72a7cf_256x240.png rename to static/js/plugins/jqueryui/images/ui-icons_72a7cf_256x240.png diff --git a/src/static/js/plugins/jqueryui/images/ui-icons_ffffff_256x240.png b/static/js/plugins/jqueryui/images/ui-icons_ffffff_256x240.png similarity index 100% rename from src/static/js/plugins/jqueryui/images/ui-icons_ffffff_256x240.png rename to static/js/plugins/jqueryui/images/ui-icons_ffffff_256x240.png diff --git a/src/static/js/plugins/jqueryui/index.html b/static/js/plugins/jqueryui/index.html similarity index 100% rename from src/static/js/plugins/jqueryui/index.html rename to static/js/plugins/jqueryui/index.html diff --git a/src/static/js/plugins/jqueryui/jquery-ui.css b/static/js/plugins/jqueryui/jquery-ui.css similarity index 100% rename from src/static/js/plugins/jqueryui/jquery-ui.css rename to static/js/plugins/jqueryui/jquery-ui.css diff --git a/src/static/js/plugins/jqueryui/jquery-ui.js b/static/js/plugins/jqueryui/jquery-ui.js similarity index 100% rename from src/static/js/plugins/jqueryui/jquery-ui.js rename to static/js/plugins/jqueryui/jquery-ui.js diff --git a/src/static/js/plugins/jqueryui/jquery-ui.min.css b/static/js/plugins/jqueryui/jquery-ui.min.css similarity index 100% rename from src/static/js/plugins/jqueryui/jquery-ui.min.css rename to static/js/plugins/jqueryui/jquery-ui.min.css diff --git a/src/static/js/plugins/jqueryui/jquery-ui.min.js b/static/js/plugins/jqueryui/jquery-ui.min.js similarity index 100% rename from src/static/js/plugins/jqueryui/jquery-ui.min.js rename to static/js/plugins/jqueryui/jquery-ui.min.js diff --git a/src/static/js/plugins/jqueryui/jquery-ui.structure.css b/static/js/plugins/jqueryui/jquery-ui.structure.css similarity index 100% rename from src/static/js/plugins/jqueryui/jquery-ui.structure.css rename to static/js/plugins/jqueryui/jquery-ui.structure.css diff --git a/src/static/js/plugins/jqueryui/jquery-ui.structure.min.css b/static/js/plugins/jqueryui/jquery-ui.structure.min.css similarity index 100% rename from src/static/js/plugins/jqueryui/jquery-ui.structure.min.css rename to static/js/plugins/jqueryui/jquery-ui.structure.min.css diff --git a/src/static/js/plugins/jqueryui/jquery-ui.theme.css b/static/js/plugins/jqueryui/jquery-ui.theme.css similarity index 100% rename from src/static/js/plugins/jqueryui/jquery-ui.theme.css rename to static/js/plugins/jqueryui/jquery-ui.theme.css diff --git a/src/static/js/plugins/jqueryui/jquery-ui.theme.min.css b/static/js/plugins/jqueryui/jquery-ui.theme.min.css similarity index 100% rename from src/static/js/plugins/jqueryui/jquery-ui.theme.min.css rename to static/js/plugins/jqueryui/jquery-ui.theme.min.css diff --git a/src/static/js/plugins/moment.min.js b/static/js/plugins/moment.min.js similarity index 100% rename from src/static/js/plugins/moment.min.js rename to static/js/plugins/moment.min.js diff --git a/src/static/js/plugins/nouislider.min.js b/static/js/plugins/nouislider.min.js similarity index 100% rename from src/static/js/plugins/nouislider.min.js rename to static/js/plugins/nouislider.min.js diff --git a/src/static/js/plugins/pdfmake.min.js.map b/static/js/plugins/pdfmake.min.js.map similarity index 100% rename from src/static/js/plugins/pdfmake.min.js.map rename to static/js/plugins/pdfmake.min.js.map diff --git a/src/static/js/plugins/perfect-scrollbar.jquery.min.js b/static/js/plugins/perfect-scrollbar.jquery.min.js similarity index 100% rename from src/static/js/plugins/perfect-scrollbar.jquery.min.js rename to static/js/plugins/perfect-scrollbar.jquery.min.js diff --git a/src/static/js/plugins/sweetalert2.min.js b/static/js/plugins/sweetalert2.min.js similarity index 100% rename from src/static/js/plugins/sweetalert2.min.js rename to static/js/plugins/sweetalert2.min.js diff --git a/src/static/js/routine.js b/static/js/routine.js similarity index 100% rename from src/static/js/routine.js rename to static/js/routine.js diff --git a/src/static/js/seasonAdmin.js b/static/js/seasonAdmin.js similarity index 100% rename from src/static/js/seasonAdmin.js rename to static/js/seasonAdmin.js diff --git a/src/static/js/skill.js b/static/js/skill.js similarity index 100% rename from src/static/js/skill.js rename to static/js/skill.js diff --git a/src/static/reglement/Sans titre.rtf b/static/reglement/Sans titre.rtf similarity index 100% rename from src/static/reglement/Sans titre.rtf rename to static/reglement/Sans titre.rtf diff --git a/src/static/reglement/info.rtf b/static/reglement/info.rtf similarity index 100% rename from src/static/reglement/info.rtf rename to static/reglement/info.rtf diff --git a/src/templates/accident_create.html b/templates/accident_create.html similarity index 100% rename from src/templates/accident_create.html rename to templates/accident_create.html diff --git a/src/templates/accident_details.html b/templates/accident_details.html similarity index 100% rename from src/templates/accident_details.html rename to templates/accident_details.html diff --git a/src/templates/accident_list.html b/templates/accident_list.html similarity index 100% rename from src/templates/accident_list.html rename to templates/accident_list.html diff --git a/src/templates/attendance.html b/templates/attendance.html similarity index 100% rename from src/templates/attendance.html rename to templates/attendance.html diff --git a/src/templates/base.html b/templates/base.html similarity index 100% rename from src/templates/base.html rename to templates/base.html diff --git a/src/templates/calendar.html b/templates/calendar.html similarity index 100% rename from src/templates/calendar.html rename to templates/calendar.html diff --git a/src/templates/chrono_create.html b/templates/chrono_create.html similarity index 100% rename from src/templates/chrono_create.html rename to templates/chrono_create.html diff --git a/src/templates/chrono_list.html b/templates/chrono_list.html similarity index 100% rename from src/templates/chrono_list.html rename to templates/chrono_list.html diff --git a/src/templates/club_statistics.html b/templates/club_statistics.html similarity index 100% rename from src/templates/club_statistics.html rename to templates/club_statistics.html diff --git a/src/templates/course_details.html b/templates/course_details.html similarity index 100% rename from src/templates/course_details.html rename to templates/course_details.html diff --git a/src/templates/event_create.html b/templates/event_create.html similarity index 100% rename from src/templates/event_create.html rename to templates/event_create.html diff --git a/src/templates/event_details.html b/templates/event_details.html similarity index 100% rename from src/templates/event_details.html rename to templates/event_details.html diff --git a/src/templates/event_list.html b/templates/event_list.html similarity index 100% rename from src/templates/event_list.html rename to templates/event_list.html diff --git a/src/templates/gymnast_accident.html b/templates/gymnast_accident.html similarity index 100% rename from src/templates/gymnast_accident.html rename to templates/gymnast_accident.html diff --git a/src/templates/gymnast_chrono.html b/templates/gymnast_chrono.html similarity index 100% rename from src/templates/gymnast_chrono.html rename to templates/gymnast_chrono.html diff --git a/src/templates/gymnast_create.html b/templates/gymnast_create.html similarity index 100% rename from src/templates/gymnast_create.html rename to templates/gymnast_create.html diff --git a/src/templates/gymnast_details.html b/templates/gymnast_details.html similarity index 100% rename from src/templates/gymnast_details.html rename to templates/gymnast_details.html diff --git a/src/templates/gymnast_display_level.html b/templates/gymnast_display_level.html similarity index 100% rename from src/templates/gymnast_display_level.html rename to templates/gymnast_display_level.html diff --git a/src/templates/gymnast_event.html b/templates/gymnast_event.html similarity index 100% rename from src/templates/gymnast_event.html rename to templates/gymnast_event.html diff --git a/src/templates/gymnast_event_accident.html b/templates/gymnast_event_accident.html similarity index 100% rename from src/templates/gymnast_event_accident.html rename to templates/gymnast_event_accident.html diff --git a/src/templates/gymnast_list.html b/templates/gymnast_list.html similarity index 100% rename from src/templates/gymnast_list.html rename to templates/gymnast_list.html diff --git a/src/templates/gymnast_objective.html b/templates/gymnast_objective.html similarity index 100% rename from src/templates/gymnast_objective.html rename to templates/gymnast_objective.html diff --git a/src/templates/gymnast_program.html b/templates/gymnast_program.html similarity index 100% rename from src/templates/gymnast_program.html rename to templates/gymnast_program.html diff --git a/src/templates/gymnast_rounds.html b/templates/gymnast_rounds.html similarity index 100% rename from src/templates/gymnast_rounds.html rename to templates/gymnast_rounds.html diff --git a/src/templates/gymnast_routine.html b/templates/gymnast_routine.html similarity index 100% rename from src/templates/gymnast_routine.html rename to templates/gymnast_routine.html diff --git a/src/templates/gymnast_scores.html b/templates/gymnast_scores.html similarity index 100% rename from src/templates/gymnast_scores.html rename to templates/gymnast_scores.html diff --git a/src/templates/gymnast_scores_chrono.html b/templates/gymnast_scores_chrono.html similarity index 100% rename from src/templates/gymnast_scores_chrono.html rename to templates/gymnast_scores_chrono.html diff --git a/src/templates/gymnast_statistics.html b/templates/gymnast_statistics.html similarity index 100% rename from src/templates/gymnast_statistics.html rename to templates/gymnast_statistics.html diff --git a/src/templates/gymnast_to_routine.html b/templates/gymnast_to_routine.html similarity index 100% rename from src/templates/gymnast_to_routine.html rename to templates/gymnast_to_routine.html diff --git a/src/templates/index.html b/templates/index.html similarity index 100% rename from src/templates/index.html rename to templates/index.html diff --git a/src/templates/judge_training_diff.html b/templates/judge_training_diff.html similarity index 100% rename from src/templates/judge_training_diff.html rename to templates/judge_training_diff.html diff --git a/src/templates/listing.html b/templates/listing.html similarity index 100% rename from src/templates/listing.html rename to templates/listing.html diff --git a/src/templates/login.html b/templates/login.html similarity index 100% rename from src/templates/login.html rename to templates/login.html diff --git a/src/templates/message_create.html b/templates/message_create.html similarity index 100% rename from src/templates/message_create.html rename to templates/message_create.html diff --git a/src/templates/message_details.html b/templates/message_details.html similarity index 100% rename from src/templates/message_details.html rename to templates/message_details.html diff --git a/src/templates/message_list.html b/templates/message_list.html similarity index 100% rename from src/templates/message_list.html rename to templates/message_list.html diff --git a/src/templates/place_create.html b/templates/place_create.html similarity index 100% rename from src/templates/place_create.html rename to templates/place_create.html diff --git a/src/templates/place_details.html b/templates/place_details.html similarity index 100% rename from src/templates/place_details.html rename to templates/place_details.html diff --git a/src/templates/place_list.html b/templates/place_list.html similarity index 100% rename from src/templates/place_list.html rename to templates/place_list.html diff --git a/src/templates/planningline_create.html b/templates/planningline_create.html similarity index 100% rename from src/templates/planningline_create.html rename to templates/planningline_create.html diff --git a/src/templates/profile_create.html b/templates/profile_create.html similarity index 100% rename from src/templates/profile_create.html rename to templates/profile_create.html diff --git a/src/templates/program.html b/templates/program.html similarity index 100% rename from src/templates/program.html rename to templates/program.html diff --git a/src/templates/program_day.html b/templates/program_day.html similarity index 100% rename from src/templates/program_day.html rename to templates/program_day.html diff --git a/src/templates/program_list.html b/templates/program_list.html similarity index 100% rename from src/templates/program_list.html rename to templates/program_list.html diff --git a/src/templates/program_old.html b/templates/program_old.html similarity index 100% rename from src/templates/program_old.html rename to templates/program_old.html diff --git a/src/templates/results.html b/templates/results.html similarity index 100% rename from src/templates/results.html rename to templates/results.html diff --git a/src/templates/routine_compose.html b/templates/routine_compose.html similarity index 100% rename from src/templates/routine_compose.html rename to templates/routine_compose.html diff --git a/src/templates/routine_create.html b/templates/routine_create.html similarity index 100% rename from src/templates/routine_create.html rename to templates/routine_create.html diff --git a/src/templates/routine_details.html b/templates/routine_details.html similarity index 100% rename from src/templates/routine_details.html rename to templates/routine_details.html diff --git a/src/templates/routine_list.html b/templates/routine_list.html similarity index 100% rename from src/templates/routine_list.html rename to templates/routine_list.html diff --git a/src/templates/score_create.html b/templates/score_create.html similarity index 100% rename from src/templates/score_create.html rename to templates/score_create.html diff --git a/src/templates/score_list.html b/templates/score_list.html similarity index 100% rename from src/templates/score_list.html rename to templates/score_list.html diff --git a/src/templates/skill_details.html b/templates/skill_details.html similarity index 100% rename from src/templates/skill_details.html rename to templates/skill_details.html diff --git a/src/templates/skill_list.html b/templates/skill_list.html similarity index 100% rename from src/templates/skill_list.html rename to templates/skill_list.html diff --git a/src/templates/training_detail.html b/templates/training_detail.html similarity index 100% rename from src/templates/training_detail.html rename to templates/training_detail.html diff --git a/src/templates/unavailability_create.html b/templates/unavailability_create.html similarity index 100% rename from src/templates/unavailability_create.html rename to templates/unavailability_create.html diff --git a/src/templates/unavailability_details.html b/templates/unavailability_details.html similarity index 100% rename from src/templates/unavailability_details.html rename to templates/unavailability_details.html diff --git a/src/templates/unavailability_list.html b/templates/unavailability_list.html similarity index 100% rename from src/templates/unavailability_list.html rename to templates/unavailability_list.html