229 lines
6.7 KiB
Python
229 lines
6.7 KiB
Python
|
from datetime import datetime, date, time, timedelta
|
||
|
|
||
|
from django.contrib.auth import get_user_model
|
||
|
from django.db import models
|
||
|
from django.utils import timezone
|
||
|
from location.models import Place
|
||
|
|
||
|
import pendulum
|
||
|
|
||
|
from tools.models import Markdownizable
|
||
|
from people.models import Gymnast
|
||
|
from location.models import Place
|
||
|
|
||
|
|
||
|
# User = get_user_model()
|
||
|
|
||
|
|
||
|
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 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=15, 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).
|
||
|
"""
|
||
|
|
||
|
class Meta:
|
||
|
verbose_name = "Event"
|
||
|
verbose_name_plural = "Event"
|
||
|
|
||
|
place = models.ForeignKey(
|
||
|
Place, 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")
|
||
|
gymnasts = models.ManyToManyField(
|
||
|
Gymnast,
|
||
|
through="Event_Participation",
|
||
|
related_name="participate_to",
|
||
|
verbose_name="Participants",
|
||
|
)
|
||
|
|
||
|
def __str__(self):
|
||
|
return "%s (à %s)" % (self.name, self.place)
|
||
|
|
||
|
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):
|
||
|
"""
|
||
|
"""
|
||
|
class Meta:
|
||
|
verbose_name = "Event Participation"
|
||
|
|
||
|
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||
|
gymnast = models.ForeignKey(Gymnast, on_delete=models.CASCADE)
|
||
|
rank = models.PositiveSmallIntegerField(default=0)
|