Merge branch 'review/planning' of Sulley/khana into master
This commit is contained in:
commit
176bdfcc9f
|
@ -1,6 +1,9 @@
|
||||||
# coding=UTF-8
|
"""Administration des plannings, évènements et saisons."""
|
||||||
|
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django_extensions.admin import ForeignKeyAutocompleteAdmin
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
EventType,
|
EventType,
|
||||||
Event,
|
Event,
|
||||||
|
@ -14,19 +17,17 @@ from .models import (
|
||||||
Round,
|
Round,
|
||||||
PlanningLine,
|
PlanningLine,
|
||||||
)
|
)
|
||||||
from django_extensions.admin import ForeignKeyAutocompleteAdmin
|
|
||||||
|
|
||||||
|
|
||||||
def duplicate_record(modeladmin, request, queryset):
|
def duplicate_record(modeladmin, request, queryset):
|
||||||
"""
|
"""*Custom action* permettant de dupliquer plusieurs enregistrements.
|
||||||
Duplication de record sélectionner.
|
|
||||||
"""
|
"""
|
||||||
for object in queryset:
|
for object in queryset:
|
||||||
object.id = None
|
object.id = None
|
||||||
object.save()
|
object.save()
|
||||||
|
|
||||||
|
|
||||||
duplicate_record.short_description = "Duplicate selected record"
|
duplicate_record.short_description = "Duplicate selected records"
|
||||||
|
|
||||||
|
|
||||||
class SeasonAdmin(admin.ModelAdmin):
|
class SeasonAdmin(admin.ModelAdmin):
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# coding=UTF-8
|
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from datetime import date
|
|
||||||
from .models import Unavailability, Event, PlanningLine
|
|
||||||
from people.models import Gymnast
|
|
||||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
|
|
||||||
|
from people.models import Gymnast
|
||||||
|
from .models import Unavailability, Event, PlanningLine
|
||||||
|
|
||||||
class UnavailabilityForm(forms.ModelForm):
|
class UnavailabilityForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
# coding=UTF-8
|
|
||||||
|
|
||||||
from django.db import models
|
from datetime import datetime, date, time, timedelta
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from datetime import datetime, date, time
|
from django.db import models
|
||||||
from base.models import Markdownizable
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from people.models import Gymnast
|
|
||||||
from location.models import Club
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import pendulum
|
import pendulum
|
||||||
|
|
||||||
|
from base.models import Markdownizable
|
||||||
|
from location.models import Club
|
||||||
|
from people.models import Gymnast
|
||||||
|
|
||||||
|
|
||||||
def get_week(a_date):
|
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)
|
the_date = pendulum.parse(a_date)
|
||||||
day = the_date.weekday()
|
day = the_date.weekday()
|
||||||
|
@ -33,6 +38,18 @@ def get_number_of_weeks_between(start, stop):
|
||||||
:param stop: date de fin de la période
|
:param stop: date de fin de la période
|
||||||
:type stop: datetime.date
|
:type stop: datetime.date
|
||||||
:return: Le nombre de semaines entre les deux dates.
|
: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
|
tmp = stop - start
|
||||||
|
@ -87,9 +104,8 @@ class TemporizableQuerySet(models.QuerySet):
|
||||||
|
|
||||||
|
|
||||||
class Temporizable(models.Model):
|
class Temporizable(models.Model):
|
||||||
"""
|
"""Classe abstraite définissant une période comprise entre deux dates.
|
||||||
Classe abstraite définissant deux dates (une da te de début, une date de
|
|
||||||
fin) et des méthodes de calculs sur base de ces dates.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -198,12 +214,13 @@ class EventType(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Event(Markdownizable, Temporizable):
|
class Event(Markdownizable, Temporizable):
|
||||||
"""
|
"""Classe représentant les évènements.
|
||||||
Classe représentant les évènements. Un évènement est caractèrisé par :
|
|
||||||
- un nom,
|
Un évènement est caractérisé par :
|
||||||
- un lieu (place),
|
* un nom,
|
||||||
- un type (compétition, démonstration, …),
|
* un lieu (place),
|
||||||
- des gymnastes (participation prévue).
|
* un type (compétition, démonstration, …),
|
||||||
|
* des gymnastes (participation prévue).
|
||||||
Je ne me rapelle plus à quoi sert le club.
|
Je ne me rapelle plus à quoi sert le club.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -257,14 +274,15 @@ class Event_Participation(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Course(Markdownizable, Temporizable):
|
class Course(Markdownizable, Temporizable):
|
||||||
"""
|
"""Classe représentant les cours.
|
||||||
Classe représentant les cours. Un cours est défini par :
|
|
||||||
- une heure de début et une heure de fin,
|
Un cours est défini par :
|
||||||
- une date de début et une date de fin (un cours est considéré comme donné hebdromadairement entre
|
* 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`)
|
ces deux dates) (hérite de la classe `Temporizable`)
|
||||||
- est associé à un ou plusieurs entraineurs,
|
* est associé à un ou plusieurs entraineurs,
|
||||||
- est associé à un club
|
* est associé à un club
|
||||||
- est associé à un jour de la semaine (numéro du jour dans la semaine : 0 = lundi, 6 = dimanche).
|
* est associé à un jour de la semaine (numéro du jour dans la semaine : 0 = lundi, 6 = dimanche).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -319,8 +337,7 @@ class Course(Markdownizable, Temporizable):
|
||||||
|
|
||||||
|
|
||||||
class Group(models.Model):
|
class Group(models.Model):
|
||||||
"""
|
"""Classe représentant les groupes (Loisir, D1, D2, A, B, …).
|
||||||
Classe représentant les groupes (Loisir, D1, D2, A, B, …).
|
|
||||||
|
|
||||||
Un groupe appartient à un club.
|
Un groupe appartient à un club.
|
||||||
"""
|
"""
|
||||||
|
@ -343,10 +360,12 @@ class Group(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Subgroup(models.Model):
|
class Subgroup(models.Model):
|
||||||
"""
|
"""Classe représentant les sous-groupes.
|
||||||
Classe représentant les sous-groupes.
|
|
||||||
|
|
||||||
Un sous-groupe appartient à un groupe (lui-même lié à un club). De la sorte, 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.
|
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:
|
class Meta:
|
||||||
|
@ -367,8 +386,7 @@ class Subgroup(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class UnavailabilityManager(models.Manager):
|
class UnavailabilityManager(models.Manager):
|
||||||
"""
|
"""Classe représentant le manager de la classe `Unavailability`.
|
||||||
Classe représentant le manager de la classe `Unavailability`.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def next(self, count):
|
def next(self, count):
|
||||||
|
@ -379,8 +397,7 @@ class UnavailabilityManager(models.Manager):
|
||||||
|
|
||||||
|
|
||||||
class Unavailability(Markdownizable, Temporizable):
|
class Unavailability(Markdownizable, Temporizable):
|
||||||
"""
|
"""Classe représentant les indisponibilités.
|
||||||
Classe représentant les indisponibilités.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -405,10 +422,11 @@ class Unavailability(Markdownizable, Temporizable):
|
||||||
|
|
||||||
|
|
||||||
class Training(models.Model):
|
class Training(models.Model):
|
||||||
"""
|
"""Classe représentant les entraînements.
|
||||||
Classe représentant les entraînements. Un entraînement est une occurence
|
|
||||||
d'un cours auquel sont présent des gymnastes pour une date donnée. Un objet
|
Un entraînement est une occurence d'un cours pendant lequel des gmnastes sont présents.
|
||||||
de cette classe lie donc un cours et un gymnaste à une date donnée.
|
|
||||||
|
Un objet de cette classe lie donc un cours et un gymnaste à une date donnée.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -441,9 +459,9 @@ class Training(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Round(Markdownizable):
|
class Round(Markdownizable):
|
||||||
"""
|
"""Classe représentant les passages des élèves lors d'un entrainement.
|
||||||
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`.
|
Chaque record représente un passage. Il est donc lié à un record de la classe `Training`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -483,8 +501,7 @@ class Round(Markdownizable):
|
||||||
|
|
||||||
|
|
||||||
class PlanningLine(Markdownizable):
|
class PlanningLine(Markdownizable):
|
||||||
"""
|
"""Classe représentant les passages prévisionnels (incubating idea).
|
||||||
Classe représentant les passages prévisionnels (incubating idea).
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from .models import get_number_of_weeks_between, Season
|
from ..models import get_number_of_weeks_between, Season
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(TestCase):
|
class TestUtils(TestCase):
|
||||||
def test_get_number_of_weeks(self):
|
def test_get_number_of_weeks(self):
|
||||||
|
"""Evalue le nombre de semaines qu'il y a entre deux dates.
|
||||||
|
"""
|
||||||
x = datetime(2016, 1, 1)
|
x = datetime(2016, 1, 1)
|
||||||
y = datetime(2016, 2, 5)
|
y = datetime(2016, 2, 5)
|
||||||
z = datetime(2016, 2, 4)
|
z = datetime(2016, 2, 4)
|
|
@ -1,4 +1,3 @@
|
||||||
# coding=UTF-8
|
|
||||||
|
|
||||||
from django.urls import path, re_path
|
from django.urls import path, re_path
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# coding=UTF-8
|
|
||||||
|
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.http import (
|
from django.http import (
|
||||||
|
@ -65,16 +64,25 @@ def event_lookup(request):
|
||||||
@login_required
|
@login_required
|
||||||
@require_http_methods(["GET", "POST"])
|
@require_http_methods(["GET", "POST"])
|
||||||
def event_create_or_update(request, eventid=None):
|
def event_create_or_update(request, eventid=None):
|
||||||
"""
|
"""Création ou mise à jour d'un évènement.
|
||||||
Création ou mise à jour d'un event.
|
|
||||||
|
Remarks:
|
||||||
|
* Il faut éviter de cibler une URL précise.
|
||||||
|
|
||||||
|
Pour cela, il faut passer par la fonction `reverse`:
|
||||||
|
https://docs.djangoproject.com/fr/3.1/ref/urlresolvers/
|
||||||
|
|
||||||
|
Si jamais l'URL `/event` devait être modifiée, cela ne fonctionnerait plus.
|
||||||
|
|
||||||
|
* J'ai modifié l'ordre des conditions pour un peu plus de clarté.
|
||||||
|
Notamment, tu faisais une première requête pour récupérer le nom du lieu
|
||||||
|
Mais tu n'utilisais cette valeur que dans le cas d'un GET.
|
||||||
|
|
||||||
|
Dans ce cas-ci, une CBV serait intéressante, parce qu'elle découplerait
|
||||||
|
complètement le GET du POST.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if eventid:
|
|
||||||
event = get_object_or_404(Event, pk=eventid)
|
|
||||||
data = {"place_related": event.place.name}
|
|
||||||
else:
|
|
||||||
event = None
|
|
||||||
data = {}
|
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = EventForm(request.POST, instance=event)
|
form = EventForm(request.POST, instance=event)
|
||||||
|
@ -86,6 +94,14 @@ def event_create_or_update(request, eventid=None):
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect("/event/")
|
return HttpResponseRedirect("/event/")
|
||||||
else:
|
else:
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
if eventid:
|
||||||
|
event = get_object_or_404(Event, pk=eventid)
|
||||||
|
data["place_related"] = event.place.name
|
||||||
|
else:
|
||||||
|
event = None
|
||||||
|
|
||||||
form = EventForm(instance=event, initial=data)
|
form = EventForm(instance=event, initial=data)
|
||||||
|
|
||||||
context = {"form": form, "eventid": eventid}
|
context = {"form": form, "eventid": eventid}
|
||||||
|
@ -94,8 +110,16 @@ def event_create_or_update(request, eventid=None):
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def link_gymnast_to_event(request, eventid, gymnastid):
|
def link_gymnast_to_event(request, eventid, gymnastid):
|
||||||
"""
|
"""Crée un lien entre un gymnaste et un évènement.
|
||||||
Crée un lien entre un gymnaste et un évènement.
|
|
||||||
|
Returns:
|
||||||
|
Si tout se passe bien, un code 200 (Success) est retourné.
|
||||||
|
|
||||||
|
Excepts:
|
||||||
|
Si une erreur se produit lors de l'association, un code HTTP 409 (Conflict) est retourné.
|
||||||
|
|
||||||
|
Remarks:
|
||||||
|
Tu ne veux pas retourner le lien qui vient d'être créé ?
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
gymnast = get_object_or_404(Gymnast, pk=gymnastid)
|
gymnast = get_object_or_404(Gymnast, pk=gymnastid)
|
||||||
|
@ -110,8 +134,14 @@ def link_gymnast_to_event(request, eventid, gymnastid):
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def remove_link_between_gymnast_and_event(request, eventid, gymnastid):
|
def remove_link_between_gymnast_and_event(request, eventid, gymnastid):
|
||||||
"""
|
"""Supprime le lien entre un gymnaste et un évènement.
|
||||||
Supprime le lien entre un gymnaste et un évènement.
|
|
||||||
|
Remarks:
|
||||||
|
En fait, tes fonctions `link_gymnast_to_event`
|
||||||
|
et `remove_link_between_gymnast_and_event` sont _très_ similaires.
|
||||||
|
|
||||||
|
Il faudrait sans doute mieux passer par une CBV, voire mieux: DRF ;-)
|
||||||
|
Surtout qu'ici, on gère directement des `link_between_g_and_e`, à ajouter ou supprimer.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
gymnast = get_object_or_404(Gymnast, pk=gymnastid)
|
gymnast = get_object_or_404(Gymnast, pk=gymnastid)
|
||||||
|
@ -125,20 +155,30 @@ def remove_link_between_gymnast_and_event(request, eventid, gymnastid):
|
||||||
|
|
||||||
|
|
||||||
def __get_event_list(request):
|
def __get_event_list(request):
|
||||||
|
"""Récupère une liste d'évènement.
|
||||||
|
|
||||||
|
Par défaut, la liste est triée chronologiquement - le plus ancien étant le premier élément.
|
||||||
|
|
||||||
|
Cette fonction est utilisée pour l'affichage des évènements et au niveau du calendrier.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): La requête en entrée
|
||||||
|
pattern (str?): Optionnel. Permet de spécifier un pattern à appliquer à la recherche.
|
||||||
|
"""
|
||||||
pattern = request.GET.get("pattern", None)
|
pattern = request.GET.get("pattern", None)
|
||||||
|
|
||||||
if pattern:
|
if pattern:
|
||||||
event_list = Event.objects.filter(name__icontains=pattern).order_by("datebegin")
|
event_list = Event.objects.filter(name__icontains=pattern)
|
||||||
else:
|
else:
|
||||||
event_list = Event.objects.all().order_by("datebegin")
|
event_list = Event.objects.all()
|
||||||
return event_list
|
|
||||||
|
return event_list.order_by("datebegin")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def calendar(request):
|
def calendar(request):
|
||||||
"""
|
"""Récupère la liste de tous évènements suivant un pattern si celui-ci est définit.
|
||||||
Récupère la liste de tous évènements suivant un pattern si celui-ci est
|
|
||||||
définit.
|
|
||||||
"""
|
"""
|
||||||
event_list = __get_event_list(request)
|
event_list = __get_event_list(request)
|
||||||
context = {"event_list": event_list}
|
context = {"event_list": event_list}
|
||||||
|
@ -217,7 +257,7 @@ def event_detail(request, eventid):
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def course_detail(request, courseid, date=None):
|
def course_detail(request, courseid, date=None):
|
||||||
"""
|
"""
|
||||||
Récupère toutes les informations d'un course.
|
Récupère toutes les informations d'un cours.
|
||||||
|
|
||||||
:return: une instance de la classe `Course`, une liste de gymnaste associés
|
:return: une instance de la classe `Course`, une liste de gymnaste associés
|
||||||
à ce cours, …
|
à ce cours, …
|
||||||
|
@ -1239,8 +1279,10 @@ def program_date_listing(request, gymnastid=None):
|
||||||
@login_required
|
@login_required
|
||||||
@require_http_methods(["GET", "POST"])
|
@require_http_methods(["GET", "POST"])
|
||||||
def planningline_update(request, planninglineid=None):
|
def planningline_update(request, planninglineid=None):
|
||||||
"""
|
"""Mise à jour d'une ligne de programme.
|
||||||
Mise à jour d'une ligne de programme.
|
|
||||||
|
Remarks:
|
||||||
|
Pourquoi ne pas juste faire un `form.save()` plutôt que de réattribuer chaque valeur ?
|
||||||
"""
|
"""
|
||||||
|
|
||||||
planningline = get_object_or_404(PlanningLine, pk=planninglineid)
|
planningline = get_object_or_404(PlanningLine, pk=planninglineid)
|
||||||
|
|
Loading…
Reference in New Issue