forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -81,3 +81,5 @@ from app.models.but_refcomp import (
|
|||||||
from app.models.but_validations import ApcValidationAnnee, ApcValidationRCUE
|
from app.models.but_validations import ApcValidationAnnee, ApcValidationRCUE
|
||||||
|
|
||||||
from app.models.config import ScoDocSiteConfig
|
from app.models.config import ScoDocSiteConfig
|
||||||
|
|
||||||
|
from app.models.assiduites import Assiduite, Justificatif
|
||||||
|
343
app/models/assiduites.py
Normal file
343
app/models/assiduites.py
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
# -*- coding: UTF-8 -*
|
||||||
|
"""Gestion de l'assiduité (assiduités + justificatifs)
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from app import db
|
||||||
|
from app.models import ModuleImpl
|
||||||
|
from app.models.etudiants import Identite
|
||||||
|
from app.scodoc.sco_utils import (
|
||||||
|
EtatAssiduite,
|
||||||
|
EtatJustificatif,
|
||||||
|
localize_datetime,
|
||||||
|
is_period_overlapping,
|
||||||
|
)
|
||||||
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
|
from app.scodoc.sco_utils import (
|
||||||
|
EtatAssiduite,
|
||||||
|
EtatJustificatif,
|
||||||
|
localize_datetime,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Assiduite(db.Model):
|
||||||
|
"""
|
||||||
|
Représente une assiduité:
|
||||||
|
- une plage horaire lié à un état et un étudiant
|
||||||
|
- un module si spécifiée
|
||||||
|
- une description si spécifiée
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "assiduites"
|
||||||
|
|
||||||
|
id = db.Column(db.Integer, primary_key=True, nullable=False)
|
||||||
|
assiduite_id = db.synonym("id")
|
||||||
|
|
||||||
|
date_debut = db.Column(
|
||||||
|
db.DateTime(timezone=True), server_default=db.func.now(), nullable=False
|
||||||
|
)
|
||||||
|
date_fin = db.Column(
|
||||||
|
db.DateTime(timezone=True), server_default=db.func.now(), nullable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
moduleimpl_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("notes_moduleimpl.id", ondelete="SET NULL"),
|
||||||
|
)
|
||||||
|
etudid = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("identite.id", ondelete="CASCADE"),
|
||||||
|
index=True,
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
etat = db.Column(db.Integer, nullable=False)
|
||||||
|
|
||||||
|
desc = db.Column(db.Text)
|
||||||
|
|
||||||
|
entry_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||||
|
|
||||||
|
user_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("user.id", ondelete="SET NULL"),
|
||||||
|
nullable=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
est_just = db.Column(db.Boolean, server_default="false", nullable=False)
|
||||||
|
|
||||||
|
def to_dict(self, format_api=True) -> dict:
|
||||||
|
"""Retourne la représentation json de l'assiduité"""
|
||||||
|
etat = self.etat
|
||||||
|
|
||||||
|
if format_api:
|
||||||
|
etat = EtatAssiduite.inverse().get(self.etat).name
|
||||||
|
data = {
|
||||||
|
"assiduite_id": self.id,
|
||||||
|
"etudid": self.etudid,
|
||||||
|
"moduleimpl_id": self.moduleimpl_id,
|
||||||
|
"date_debut": self.date_debut,
|
||||||
|
"date_fin": self.date_fin,
|
||||||
|
"etat": etat,
|
||||||
|
"desc": self.desc,
|
||||||
|
"entry_date": self.entry_date,
|
||||||
|
"user_id": self.user_id,
|
||||||
|
"est_just": self.est_just,
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_assiduite(
|
||||||
|
cls,
|
||||||
|
etud: Identite,
|
||||||
|
date_debut: datetime,
|
||||||
|
date_fin: datetime,
|
||||||
|
etat: EtatAssiduite,
|
||||||
|
moduleimpl: ModuleImpl = None,
|
||||||
|
description: str = None,
|
||||||
|
entry_date: datetime = None,
|
||||||
|
user_id: int = None,
|
||||||
|
est_just: bool = False,
|
||||||
|
) -> object or int:
|
||||||
|
"""Créer une nouvelle assiduité pour l'étudiant"""
|
||||||
|
# Vérification de non duplication des périodes
|
||||||
|
assiduites: list[Assiduite] = etud.assiduites
|
||||||
|
if is_period_conflicting(date_debut, date_fin, assiduites, Assiduite):
|
||||||
|
raise ScoValueError(
|
||||||
|
"Duplication des assiduités (la période rentrée rentre en conflit avec une assiduité enregistrée)"
|
||||||
|
)
|
||||||
|
if moduleimpl is not None:
|
||||||
|
# Vérification de l'existence du module pour l'étudiant
|
||||||
|
if moduleimpl.est_inscrit(etud):
|
||||||
|
nouv_assiduite = Assiduite(
|
||||||
|
date_debut=date_debut,
|
||||||
|
date_fin=date_fin,
|
||||||
|
etat=etat,
|
||||||
|
etudiant=etud,
|
||||||
|
moduleimpl_id=moduleimpl.id,
|
||||||
|
desc=description,
|
||||||
|
entry_date=entry_date,
|
||||||
|
user_id=user_id,
|
||||||
|
est_just=est_just,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ScoValueError("L'étudiant n'est pas inscrit au moduleimpl")
|
||||||
|
else:
|
||||||
|
nouv_assiduite = Assiduite(
|
||||||
|
date_debut=date_debut,
|
||||||
|
date_fin=date_fin,
|
||||||
|
etat=etat,
|
||||||
|
etudiant=etud,
|
||||||
|
desc=description,
|
||||||
|
entry_date=entry_date,
|
||||||
|
user_id=user_id,
|
||||||
|
est_just=est_just,
|
||||||
|
)
|
||||||
|
|
||||||
|
return nouv_assiduite
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fast_create_assiduite(
|
||||||
|
cls,
|
||||||
|
etudid: int,
|
||||||
|
date_debut: datetime,
|
||||||
|
date_fin: datetime,
|
||||||
|
etat: EtatAssiduite,
|
||||||
|
moduleimpl_id: int = None,
|
||||||
|
description: str = None,
|
||||||
|
entry_date: datetime = None,
|
||||||
|
est_just: bool = False,
|
||||||
|
) -> object or int:
|
||||||
|
"""Créer une nouvelle assiduité pour l'étudiant"""
|
||||||
|
# Vérification de non duplication des périodes
|
||||||
|
|
||||||
|
nouv_assiduite = Assiduite(
|
||||||
|
date_debut=date_debut,
|
||||||
|
date_fin=date_fin,
|
||||||
|
etat=etat,
|
||||||
|
etudid=etudid,
|
||||||
|
moduleimpl_id=moduleimpl_id,
|
||||||
|
desc=description,
|
||||||
|
entry_date=entry_date,
|
||||||
|
est_just=est_just,
|
||||||
|
)
|
||||||
|
|
||||||
|
return nouv_assiduite
|
||||||
|
|
||||||
|
|
||||||
|
class Justificatif(db.Model):
|
||||||
|
"""
|
||||||
|
Représente un justificatif:
|
||||||
|
- une plage horaire lié à un état et un étudiant
|
||||||
|
- une raison si spécifiée
|
||||||
|
- un fichier si spécifié
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "justificatifs"
|
||||||
|
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
justif_id = db.synonym("id")
|
||||||
|
|
||||||
|
date_debut = db.Column(
|
||||||
|
db.DateTime(timezone=True), server_default=db.func.now(), nullable=False
|
||||||
|
)
|
||||||
|
date_fin = db.Column(
|
||||||
|
db.DateTime(timezone=True), server_default=db.func.now(), nullable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
etudid = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("identite.id", ondelete="CASCADE"),
|
||||||
|
index=True,
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
etat = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
entry_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||||
|
|
||||||
|
user_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("user.id", ondelete="SET NULL"),
|
||||||
|
nullable=True,
|
||||||
|
index=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
raison = db.Column(db.Text())
|
||||||
|
|
||||||
|
# Archive_id -> sco_archives_justificatifs.py
|
||||||
|
fichier = db.Column(db.Text())
|
||||||
|
|
||||||
|
def to_dict(self, format_api: bool = False) -> dict:
|
||||||
|
"""transformation de l'objet en dictionnaire sérialisable"""
|
||||||
|
|
||||||
|
etat = self.etat
|
||||||
|
|
||||||
|
if format_api:
|
||||||
|
etat = EtatJustificatif.inverse().get(self.etat).name
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"justif_id": self.justif_id,
|
||||||
|
"etudid": self.etudid,
|
||||||
|
"date_debut": self.date_debut,
|
||||||
|
"date_fin": self.date_fin,
|
||||||
|
"etat": etat,
|
||||||
|
"raison": self.raison,
|
||||||
|
"fichier": self.fichier,
|
||||||
|
"entry_date": self.entry_date,
|
||||||
|
"user_id": self.user_id,
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_justificatif(
|
||||||
|
cls,
|
||||||
|
etud: Identite,
|
||||||
|
date_debut: datetime,
|
||||||
|
date_fin: datetime,
|
||||||
|
etat: EtatJustificatif,
|
||||||
|
raison: str = None,
|
||||||
|
entry_date: datetime = None,
|
||||||
|
user_id: int = None,
|
||||||
|
) -> object or int:
|
||||||
|
"""Créer un nouveau justificatif pour l'étudiant"""
|
||||||
|
nouv_justificatif = Justificatif(
|
||||||
|
date_debut=date_debut,
|
||||||
|
date_fin=date_fin,
|
||||||
|
etat=etat,
|
||||||
|
etudiant=etud,
|
||||||
|
raison=raison,
|
||||||
|
entry_date=entry_date,
|
||||||
|
user_id=user_id,
|
||||||
|
)
|
||||||
|
return nouv_justificatif
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fast_create_justificatif(
|
||||||
|
cls,
|
||||||
|
etudid: int,
|
||||||
|
date_debut: datetime,
|
||||||
|
date_fin: datetime,
|
||||||
|
etat: EtatJustificatif,
|
||||||
|
raison: str = None,
|
||||||
|
entry_date: datetime = None,
|
||||||
|
) -> object or int:
|
||||||
|
"""Créer un nouveau justificatif pour l'étudiant"""
|
||||||
|
|
||||||
|
nouv_justificatif = Justificatif(
|
||||||
|
date_debut=date_debut,
|
||||||
|
date_fin=date_fin,
|
||||||
|
etat=etat,
|
||||||
|
etudid=etudid,
|
||||||
|
raison=raison,
|
||||||
|
entry_date=entry_date,
|
||||||
|
)
|
||||||
|
|
||||||
|
return nouv_justificatif
|
||||||
|
|
||||||
|
|
||||||
|
def is_period_conflicting(
|
||||||
|
date_debut: datetime,
|
||||||
|
date_fin: datetime,
|
||||||
|
collection: list[Assiduite or Justificatif],
|
||||||
|
collection_cls: Assiduite or Justificatif,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Vérifie si une date n'entre pas en collision
|
||||||
|
avec les justificatifs ou assiduites déjà présentes
|
||||||
|
"""
|
||||||
|
|
||||||
|
date_debut = localize_datetime(date_debut)
|
||||||
|
date_fin = localize_datetime(date_fin)
|
||||||
|
|
||||||
|
if (
|
||||||
|
collection.filter_by(date_debut=date_debut, date_fin=date_fin).first()
|
||||||
|
is not None
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
count: int = collection.filter(
|
||||||
|
collection_cls.date_debut < date_fin, collection_cls.date_fin > date_debut
|
||||||
|
).count()
|
||||||
|
|
||||||
|
return count > 0
|
||||||
|
|
||||||
|
|
||||||
|
def compute_assiduites_justified(
|
||||||
|
justificatifs: Justificatif = Justificatif, reset: bool = False
|
||||||
|
) -> list[int]:
|
||||||
|
"""Calcule et modifie les champs "est_just" de chaque assiduité lié à l'étud
|
||||||
|
retourne la liste des assiduite_id justifiées
|
||||||
|
|
||||||
|
Si reset alors : met à false toutes les assiduités non justifiées par les justificatifs donnés
|
||||||
|
"""
|
||||||
|
|
||||||
|
list_assiduites_id: set[int] = set()
|
||||||
|
for justi in justificatifs:
|
||||||
|
assiduites: Assiduite = (
|
||||||
|
Assiduite.query.join(Justificatif, Justificatif.etudid == Assiduite.etudid)
|
||||||
|
.filter(Assiduite.etat != EtatAssiduite.PRESENT)
|
||||||
|
.filter(
|
||||||
|
Assiduite.date_debut <= justi.date_fin,
|
||||||
|
Assiduite.date_fin >= justi.date_debut,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for assi in assiduites:
|
||||||
|
assi.est_just = True
|
||||||
|
list_assiduites_id.add(assi.id)
|
||||||
|
db.session.add(assi)
|
||||||
|
|
||||||
|
if reset:
|
||||||
|
un_justified: Assiduite = (
|
||||||
|
Assiduite.query.filter(Assiduite.id.not_in(list_assiduites_id))
|
||||||
|
.filter(Assiduite.etat != EtatAssiduite.PRESENT)
|
||||||
|
.join(Justificatif, Justificatif.etudid == Assiduite.etudid)
|
||||||
|
)
|
||||||
|
|
||||||
|
for assi in un_justified:
|
||||||
|
assi.est_just = False
|
||||||
|
db.session.add(assi)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
return list(list_assiduites_id)
|
@ -73,6 +73,10 @@ class Identite(db.Model):
|
|||||||
passive_deletes=True,
|
passive_deletes=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Relations avec les assiduites et les justificatifs
|
||||||
|
assiduites = db.relationship("Assiduite", backref="etudiant", lazy="dynamic")
|
||||||
|
justificatifs = db.relationship("Justificatif", backref="etudiant", lazy="dynamic")
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return (
|
return (
|
||||||
f"<Etud {self.id}/{self.departement.acronym} {self.nom!r} {self.prenom!r}>"
|
f"<Etud {self.id}/{self.departement.acronym} {self.nom!r} {self.prenom!r}>"
|
||||||
|
@ -122,6 +122,22 @@ class ModuleImpl(db.Model):
|
|||||||
raise AccessDenied(f"Modification impossible pour {user}")
|
raise AccessDenied(f"Modification impossible pour {user}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def est_inscrit(self, etud: Identite) -> bool:
|
||||||
|
"""
|
||||||
|
Vérifie si l'étudiant est bien inscrit au moduleimpl
|
||||||
|
|
||||||
|
Retourne Vrai si c'est le cas, faux sinon
|
||||||
|
"""
|
||||||
|
|
||||||
|
is_module: int = (
|
||||||
|
ModuleImplInscription.query.filter_by(
|
||||||
|
etudid=etud.id, moduleimpl_id=self.id
|
||||||
|
).count()
|
||||||
|
> 0
|
||||||
|
)
|
||||||
|
|
||||||
|
return is_module
|
||||||
|
|
||||||
|
|
||||||
# Enseignants (chargés de TD ou TP) d'un moduleimpl
|
# Enseignants (chargés de TD ou TP) d'un moduleimpl
|
||||||
notes_modules_enseignants = db.Table(
|
notes_modules_enseignants = db.Table(
|
||||||
|
@ -32,13 +32,14 @@ import base64
|
|||||||
import bisect
|
import bisect
|
||||||
import collections
|
import collections
|
||||||
import datetime
|
import datetime
|
||||||
from enum import IntEnum
|
from enum import IntEnum, Enum
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
import numbers
|
import numbers
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
from shutil import get_terminal_size
|
||||||
import _thread
|
import _thread
|
||||||
import time
|
import time
|
||||||
import unicodedata
|
import unicodedata
|
||||||
@ -50,6 +51,10 @@ from PIL import Image as PILImage
|
|||||||
import pydot
|
import pydot
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from pytz import timezone
|
||||||
|
|
||||||
|
import dateutil.parser as dtparser
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask import g, request, Response
|
from flask import g, request, Response
|
||||||
from flask import flash, url_for, make_response
|
from flask import flash, url_for, make_response
|
||||||
@ -91,6 +96,161 @@ ETATS_INSCRIPTION = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def print_progress_bar(
|
||||||
|
iteration,
|
||||||
|
total,
|
||||||
|
prefix="",
|
||||||
|
suffix="",
|
||||||
|
finish_msg="",
|
||||||
|
decimals=1,
|
||||||
|
length=100,
|
||||||
|
fill="█",
|
||||||
|
autosize=False,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Affiche une progress bar à un point donné (mettre dans une boucle pour rendre dynamique)
|
||||||
|
@params:
|
||||||
|
iteration - Required : index du point donné (Int)
|
||||||
|
total - Required : nombre total avant complétion (eg: len(List))
|
||||||
|
prefix - Optional : Préfix -> écrit à gauche de la barre (Str)
|
||||||
|
suffix - Optional : Suffix -> écrit à droite de la barre (Str)
|
||||||
|
decimals - Optional : nombres de chiffres après la virgule (Int)
|
||||||
|
length - Optional : taille de la barre en nombre de caractères (Int)
|
||||||
|
fill - Optional : charactère de remplissange de la barre (Str)
|
||||||
|
autosize - Optional : Choisir automatiquement la taille de la barre en fonction du terminal (Bool)
|
||||||
|
"""
|
||||||
|
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
||||||
|
color = TerminalColor.RED
|
||||||
|
if 50 >= float(percent) > 25:
|
||||||
|
color = TerminalColor.MAGENTA
|
||||||
|
if 75 >= float(percent) > 50:
|
||||||
|
color = TerminalColor.BLUE
|
||||||
|
if 90 >= float(percent) > 75:
|
||||||
|
color = TerminalColor.CYAN
|
||||||
|
if 100 >= float(percent) > 90:
|
||||||
|
color = TerminalColor.GREEN
|
||||||
|
styling = f"{prefix} |{fill}| {percent}% {suffix}"
|
||||||
|
if autosize:
|
||||||
|
cols, _ = get_terminal_size(fallback=(length, 1))
|
||||||
|
length = cols - len(styling)
|
||||||
|
filled_length = int(length * iteration // total)
|
||||||
|
pg_bar = fill * filled_length + "-" * (length - filled_length)
|
||||||
|
print(f"\r{color}{styling.replace(fill, pg_bar)}{TerminalColor.RESET}", end="\r")
|
||||||
|
# Affiche une nouvelle ligne vide
|
||||||
|
if iteration == total:
|
||||||
|
print(f"\n{finish_msg}")
|
||||||
|
|
||||||
|
|
||||||
|
class TerminalColor:
|
||||||
|
"""Ensemble de couleur pour terminaux"""
|
||||||
|
|
||||||
|
BLUE = "\033[94m"
|
||||||
|
CYAN = "\033[96m"
|
||||||
|
GREEN = "\033[92m"
|
||||||
|
MAGENTA = "\033[95m"
|
||||||
|
RED = "\033[91m"
|
||||||
|
RESET = "\033[0m"
|
||||||
|
|
||||||
|
|
||||||
|
class BiDirectionalEnum(Enum):
|
||||||
|
"""Permet la recherche inverse d'un enum
|
||||||
|
Condition : les clés et les valeurs doivent être uniques
|
||||||
|
les clés doivent être en MAJUSCULES
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def contains(cls, attr: str):
|
||||||
|
"""Vérifie sur un attribut existe dans l'enum"""
|
||||||
|
return attr.upper() in cls._member_names_
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, attr: str, default: any = None):
|
||||||
|
"""Récupère une valeur à partir de son attribut"""
|
||||||
|
val = None
|
||||||
|
try:
|
||||||
|
val = cls[attr.upper()]
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
val = default
|
||||||
|
return val
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def inverse(cls):
|
||||||
|
"""Retourne un dictionnaire représentant la map inverse de l'Enum"""
|
||||||
|
return cls._value2member_map_
|
||||||
|
|
||||||
|
|
||||||
|
class EtatAssiduite(int, BiDirectionalEnum):
|
||||||
|
"""Code des états d'assiduité"""
|
||||||
|
|
||||||
|
# Stockés en BD ne pas modifier
|
||||||
|
|
||||||
|
PRESENT = 0
|
||||||
|
RETARD = 1
|
||||||
|
ABSENT = 2
|
||||||
|
|
||||||
|
|
||||||
|
class EtatJustificatif(int, BiDirectionalEnum):
|
||||||
|
"""Code des états des justificatifs"""
|
||||||
|
|
||||||
|
# Stockés en BD ne pas modifier
|
||||||
|
|
||||||
|
VALIDE = 0
|
||||||
|
NON_VALIDE = 1
|
||||||
|
ATTENTE = 2
|
||||||
|
MODIFIE = 3
|
||||||
|
|
||||||
|
|
||||||
|
def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or None:
|
||||||
|
"""
|
||||||
|
Vérifie si une date est au format iso
|
||||||
|
|
||||||
|
Retourne un booléen Vrai (ou un objet Datetime si convert = True)
|
||||||
|
si l'objet est au format iso
|
||||||
|
|
||||||
|
Retourne Faux si l'objet n'est pas au format et convert = False
|
||||||
|
|
||||||
|
Retourne None sinon
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
date: datetime.datetime = dtparser.isoparse(date)
|
||||||
|
return date if convert else True
|
||||||
|
except (dtparser.ParserError, ValueError, TypeError):
|
||||||
|
return None if convert else False
|
||||||
|
|
||||||
|
|
||||||
|
def localize_datetime(date: datetime.datetime or str) -> datetime.datetime:
|
||||||
|
"""Ajoute un timecode UTC à la date donnée."""
|
||||||
|
if isinstance(date, str):
|
||||||
|
date = is_iso_formated(date, convert=True)
|
||||||
|
|
||||||
|
new_date: datetime.datetime = date
|
||||||
|
if new_date.tzinfo is None:
|
||||||
|
try:
|
||||||
|
new_date = timezone("Europe/Paris").localize(date)
|
||||||
|
except OverflowError:
|
||||||
|
new_date = timezone("UTC").localize(date)
|
||||||
|
return new_date
|
||||||
|
|
||||||
|
|
||||||
|
def is_period_overlapping(
|
||||||
|
periode: tuple[datetime.datetime, datetime.datetime],
|
||||||
|
interval: tuple[datetime.datetime, datetime.datetime],
|
||||||
|
bornes: bool = True,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Vérifie si la période et l'interval s'intersectent
|
||||||
|
si strict == True : les extrémitées ne comptes pas
|
||||||
|
Retourne Vrai si c'est le cas, faux sinon
|
||||||
|
"""
|
||||||
|
p_deb, p_fin = periode
|
||||||
|
i_deb, i_fin = interval
|
||||||
|
|
||||||
|
if bornes:
|
||||||
|
return p_deb <= i_fin and p_fin >= i_deb
|
||||||
|
return p_deb < i_fin and p_fin > i_deb
|
||||||
|
|
||||||
|
|
||||||
# Types de modules
|
# Types de modules
|
||||||
class ModuleType(IntEnum):
|
class ModuleType(IntEnum):
|
||||||
"""Code des types de module."""
|
"""Code des types de module."""
|
||||||
|
Loading…
Reference in New Issue
Block a user