Ultron/planning/models.py

229 lines
6.7 KiB
Python
Raw Normal View History

2021-11-17 10:39:16 +01:00
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)