ScoDoc/app/models/assiduites.py

278 lines
7.9 KiB
Python

# -*- 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_exceptions import ScoValueError
from app.scodoc.sco_utils import (
EtatAssiduite,
EtatJustificatif,
is_period_overlapping,
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)
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())
def to_dict(self, format_api=True) -> dict:
etat = self.etat
if format_api:
etat = EtatAssiduite.inverse().get(self.etat).name
data = {
"assiduite_id": self.assiduite_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,
}
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,
) -> 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,
)
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,
)
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,
) -> 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,
)
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())
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,
}
return data
@classmethod
def create_justificatif(
cls,
etud: Identite,
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"""
# Vérification de non duplication des périodes
justificatifs: list[Justificatif] = etud.justificatifs
if is_period_conflicting(date_debut, date_fin, justificatifs, Justificatif):
raise ScoValueError(
"Duplication des justificatifs (la période rentrée rentre en conflit avec un justificatif enregistré)"
)
nouv_justificatif = Justificatif(
date_debut=date_debut,
date_fin=date_fin,
etat=etat,
etudiant=etud,
raison=raison,
entry_date=entry_date,
)
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