219 lines
6.6 KiB
Python
219 lines
6.6 KiB
Python
|
"""Ensemble des classes d'utilité publique :-)"""
|
||
|
|
||
|
from django.db import models
|
||
|
from django.utils import timezone
|
||
|
|
||
|
from datetime import date
|
||
|
from .date_week_transition import (
|
||
|
get_number_of_weeks_between,
|
||
|
from_date_to_week_number,
|
||
|
)
|
||
|
import markdown
|
||
|
import pendulum
|
||
|
import re
|
||
|
|
||
|
|
||
|
class Season:
|
||
|
"""Class pour représenter une saison"""
|
||
|
|
||
|
def __init__(self, label=None):
|
||
|
self.label = label
|
||
|
if self.label is None or not self.is_valid():
|
||
|
the_date = pendulum.today().date()
|
||
|
if the_date.month >= 9: # nouvelle saison
|
||
|
self.label = str(the_date.year) + "-" + str(the_date.year + 1)
|
||
|
else:
|
||
|
self.label = str(the_date.year - 1) + "-" + str(the_date.year)
|
||
|
|
||
|
def is_valid(self):
|
||
|
"""Test si une chaine de caractère correspond bien à une saison
|
||
|
|
||
|
Args:
|
||
|
season string saison sous la forme "xxxx-xxxy"
|
||
|
|
||
|
Returns:
|
||
|
bool vrai si la chaine de caractère correspond à une saison.
|
||
|
|
||
|
Examples:
|
||
|
>>> from jarvis.tools.date_week_transition import is_valid_season
|
||
|
>>> is_valid_season("2022-2023")
|
||
|
>>> True
|
||
|
|
||
|
>>> is_valid_season("2022-2024")
|
||
|
>>> False
|
||
|
|
||
|
>>> is_valid_season("2024-2023")
|
||
|
>>> False
|
||
|
|
||
|
>>> is_valid_season("1358-5682")
|
||
|
>>> False
|
||
|
|
||
|
>>> is_valid_season("fgkrs-gkml")
|
||
|
>>> False
|
||
|
|
||
|
>>> is_valid_season("drgnldsjgklfdtngl")
|
||
|
>>> False
|
||
|
"""
|
||
|
dash_position = self.label.find("-")
|
||
|
if dash_position < 0:
|
||
|
return False
|
||
|
|
||
|
pattern = "^[0-9]{4}-[0-9]{4}$"
|
||
|
if not re.search(pattern, self.label):
|
||
|
return False
|
||
|
|
||
|
first_year = int(self.label[:dash_position])
|
||
|
second_year = int(self.label[dash_position + 1 :])
|
||
|
|
||
|
if first_year != second_year - 1:
|
||
|
return False
|
||
|
|
||
|
return True
|
||
|
|
||
|
def __str__(self):
|
||
|
return "%s" % (self.label)
|
||
|
|
||
|
|
||
|
def get_default_date():
|
||
|
return pendulum.now().date()
|
||
|
|
||
|
|
||
|
class Seasonisable(models.Model):
|
||
|
""" """
|
||
|
|
||
|
class Meta:
|
||
|
abstract = True
|
||
|
|
||
|
date = models.DateField(default=get_default_date, verbose_name="Date")
|
||
|
season = models.CharField(max_length=9, editable=False)
|
||
|
week_number = models.PositiveSmallIntegerField(editable=False)
|
||
|
|
||
|
def save(self, *args, **kwargs):
|
||
|
"""Calcule les valeurs `season` et `week_number` sur base d'une date lors de l'
|
||
|
enregistrement d'un object enfant.
|
||
|
"""
|
||
|
if self.date is None:
|
||
|
self.date = get_default_date()
|
||
|
self.season, self.week_number = from_date_to_week_number(self.date)
|
||
|
super().save(*args, **kwargs)
|
||
|
|
||
|
@property
|
||
|
def is_past(self):
|
||
|
return pendulum.now().date() > self.date
|
||
|
|
||
|
|
||
|
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(date_begin__gte=timezone.now()).order_by("date_begin")[
|
||
|
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(date_end__lte=timezone.now()).order_by("-date_end")[0:limit]
|
||
|
|
||
|
# def get(self, date_string):
|
||
|
# """
|
||
|
# """
|
||
|
# try:
|
||
|
# selected_object = self.get(date_begin__lte=date_string, date_end__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
|
||
|
|
||
|
date_begin = models.DateTimeField(verbose_name="Begin", default=timezone.now)
|
||
|
date_end = models.DateTimeField(verbose_name="End", default=timezone.now)
|
||
|
|
||
|
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.date_begin.date(), self.date_end.date())
|
||
|
|
||
|
def get_number_of_occurence_to_event(self, the_date):
|
||
|
"""
|
||
|
Renvoie le nombre semaines entre une date choisie et le début
|
||
|
(date_begin) 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.date_begin.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.date_end.date())
|
||
|
|
||
|
return get_number_of_weeks_between(self.date_begin.date(), the_date)
|
||
|
|
||
|
|
||
|
class Markdownizable(models.Model):
|
||
|
"""
|
||
|
Classe abstraite ajoutant un champ `informations`, convertible de .md -> .html.
|
||
|
"""
|
||
|
|
||
|
class Meta:
|
||
|
abstract = True
|
||
|
|
||
|
informations = models.TextField(
|
||
|
null=True,
|
||
|
blank=True,
|
||
|
verbose_name="Comments",
|
||
|
help_text="Only MarkDown is authorized",
|
||
|
)
|
||
|
|
||
|
def to_markdown(self):
|
||
|
"""Convertit le champ `informations` en (Github-flavored) Markdown."""
|
||
|
|
||
|
return markdown.markdown(self.informations)
|