forked from ScoDoc/ScoDoc
Assiduité : optimisation justification assiduités
This commit is contained in:
parent
21b2e0f582
commit
5158bd0c8f
@ -22,7 +22,6 @@ from app.api import get_model_api_object, tools
|
|||||||
from app.decorators import permission_required, scodoc
|
from app.decorators import permission_required, scodoc
|
||||||
from app.models import Identite, Justificatif, Departement, FormSemestre, Scolog
|
from app.models import Identite, Justificatif, Departement, FormSemestre, Scolog
|
||||||
from app.models.assiduites import (
|
from app.models.assiduites import (
|
||||||
compute_assiduites_justified,
|
|
||||||
get_formsemestre_from_data,
|
get_formsemestre_from_data,
|
||||||
)
|
)
|
||||||
from app.scodoc.sco_archives_justificatifs import JustificatifArchiver
|
from app.scodoc.sco_archives_justificatifs import JustificatifArchiver
|
||||||
@ -310,7 +309,6 @@ def justif_create(etudid: int = None, nip=None, ine=None):
|
|||||||
|
|
||||||
errors: list[dict] = []
|
errors: list[dict] = []
|
||||||
success: list[dict] = []
|
success: list[dict] = []
|
||||||
justifs: list[Justificatif] = []
|
|
||||||
|
|
||||||
# énumération des justificatifs
|
# énumération des justificatifs
|
||||||
for i, data in enumerate(create_list):
|
for i, data in enumerate(create_list):
|
||||||
@ -322,11 +320,9 @@ def justif_create(etudid: int = None, nip=None, ine=None):
|
|||||||
errors.append({"indice": i, "message": obj})
|
errors.append({"indice": i, "message": obj})
|
||||||
else:
|
else:
|
||||||
success.append({"indice": i, "message": obj})
|
success.append({"indice": i, "message": obj})
|
||||||
justifs.append(justi)
|
justi.justifier_assiduites()
|
||||||
scass.simple_invalidate_cache(data, etud.id)
|
scass.simple_invalidate_cache(data, etud.id)
|
||||||
|
|
||||||
# Actualisation des assiduités justifiées en fonction de tous les nouveaux justificatifs
|
|
||||||
compute_assiduites_justified(etud.etudid, justifs)
|
|
||||||
return {"errors": errors, "success": success}
|
return {"errors": errors, "success": success}
|
||||||
|
|
||||||
|
|
||||||
@ -495,6 +491,7 @@ def justif_edit(justif_id: int):
|
|||||||
return json_error(404, err)
|
return json_error(404, err)
|
||||||
|
|
||||||
# Mise à jour du justificatif
|
# Mise à jour du justificatif
|
||||||
|
justificatif_unique.dejustifier_assiduites()
|
||||||
db.session.add(justificatif_unique)
|
db.session.add(justificatif_unique)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -511,11 +508,7 @@ def justif_edit(justif_id: int):
|
|||||||
retour = {
|
retour = {
|
||||||
"couverture": {
|
"couverture": {
|
||||||
"avant": avant_ids,
|
"avant": avant_ids,
|
||||||
"apres": compute_assiduites_justified(
|
"apres": justificatif_unique.justifier_assiduites(),
|
||||||
justificatif_unique.etudid,
|
|
||||||
[justificatif_unique],
|
|
||||||
True,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Invalide le cache
|
# Invalide le cache
|
||||||
@ -592,14 +585,10 @@ def _delete_one(justif_id: int) -> tuple[int, str]:
|
|||||||
|
|
||||||
# On invalide le cache
|
# On invalide le cache
|
||||||
scass.simple_invalidate_cache(justificatif_unique.to_dict())
|
scass.simple_invalidate_cache(justificatif_unique.to_dict())
|
||||||
|
# On actualise les assiduités justifiées de l'étudiant concerné
|
||||||
|
justificatif_unique.dejustifier_assiduites()
|
||||||
# On supprime le justificatif
|
# On supprime le justificatif
|
||||||
db.session.delete(justificatif_unique)
|
db.session.delete(justificatif_unique)
|
||||||
# On actualise les assiduités justifiées de l'étudiant concerné
|
|
||||||
compute_assiduites_justified(
|
|
||||||
justificatif_unique.etudid,
|
|
||||||
Justificatif.query.filter_by(etudid=justificatif_unique.etudid).all(),
|
|
||||||
True,
|
|
||||||
)
|
|
||||||
|
|
||||||
return (200, "OK")
|
return (200, "OK")
|
||||||
|
|
||||||
|
@ -574,11 +574,7 @@ class Justificatif(ScoDocModel):
|
|||||||
db.session.delete(self)
|
db.session.delete(self)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
# On actualise les assiduités justifiées de l'étudiant concerné
|
# On actualise les assiduités justifiées de l'étudiant concerné
|
||||||
compute_assiduites_justified(
|
self.dejustifier_assiduites()
|
||||||
self.etudid,
|
|
||||||
Justificatif.query.filter_by(etudid=self.etudid).all(),
|
|
||||||
True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_fichiers(self) -> tuple[list[str], int]:
|
def get_fichiers(self) -> tuple[list[str], int]:
|
||||||
"""Renvoie la liste des noms de fichiers justicatifs
|
"""Renvoie la liste des noms de fichiers justicatifs
|
||||||
@ -600,6 +596,64 @@ class Justificatif(ScoDocModel):
|
|||||||
accessible_filenames.append(filename[0])
|
accessible_filenames.append(filename[0])
|
||||||
return accessible_filenames, len(filenames)
|
return accessible_filenames, len(filenames)
|
||||||
|
|
||||||
|
def justifier_assiduites(
|
||||||
|
self,
|
||||||
|
) -> list[int]:
|
||||||
|
"""Justifie les assiduités sur la période de validité du justificatif"""
|
||||||
|
log(f"justifier_assiduites: {self}")
|
||||||
|
assiduites_justifiees: list[int] = []
|
||||||
|
if self.etat != EtatJustificatif.VALIDE:
|
||||||
|
return []
|
||||||
|
# On récupère les assiduités de l'étudiant sur la période donnée
|
||||||
|
assiduites: Query = self.etudiant.assiduites.filter(
|
||||||
|
Assiduite.date_debut >= self.date_debut,
|
||||||
|
Assiduite.date_fin <= self.date_fin,
|
||||||
|
Assiduite.etat != EtatAssiduite.PRESENT,
|
||||||
|
)
|
||||||
|
# Pour chaque assiduité, on la justifie
|
||||||
|
for assi in assiduites:
|
||||||
|
assi.est_just = True
|
||||||
|
assiduites_justifiees.append(assi.assiduite_id)
|
||||||
|
db.session.add(assi)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return assiduites_justifiees
|
||||||
|
|
||||||
|
def dejustifier_assiduites(self) -> list[int]:
|
||||||
|
"""
|
||||||
|
Déjustifie les assiduités sur la période du justificatif
|
||||||
|
"""
|
||||||
|
assiduites_dejustifiees: list[int] = []
|
||||||
|
|
||||||
|
# On récupère les assiduités de l'étudiant sur la période donnée
|
||||||
|
assiduites: Query = self.etudiant.assiduites.filter(
|
||||||
|
Assiduite.date_debut >= self.date_debut,
|
||||||
|
Assiduite.date_fin <= self.date_fin,
|
||||||
|
Assiduite.etat != EtatAssiduite.PRESENT,
|
||||||
|
)
|
||||||
|
assi: Assiduite
|
||||||
|
for assi in assiduites:
|
||||||
|
# On récupère les justificatifs qui justifient l'assiduité `assi`
|
||||||
|
assi_justifs: list[int] = get_justifs_from_date(
|
||||||
|
self.etudiant.etudid,
|
||||||
|
assi.date_debut,
|
||||||
|
assi.date_fin,
|
||||||
|
long=False,
|
||||||
|
valid=True,
|
||||||
|
)
|
||||||
|
# Si il n'y a pas d'autre justificatif valide, on déjustifie l'assiduité
|
||||||
|
if len(assi_justifs) == 0 or (
|
||||||
|
len(assi_justifs) == 1 and assi_justifs[0] == self.justif_id
|
||||||
|
):
|
||||||
|
assi.est_just = False
|
||||||
|
assiduites_dejustifiees.append(assi.assiduite_id)
|
||||||
|
db.session.add(assi)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return assiduites_dejustifiees
|
||||||
|
|
||||||
|
|
||||||
def is_period_conflicting(
|
def is_period_conflicting(
|
||||||
date_debut: datetime,
|
date_debut: datetime,
|
||||||
@ -623,72 +677,6 @@ def is_period_conflicting(
|
|||||||
return count > 0
|
return count > 0
|
||||||
|
|
||||||
|
|
||||||
def compute_assiduites_justified(
|
|
||||||
etudid: int, justificatifs: list[Justificatif] = None, reset: bool = False
|
|
||||||
) -> list[int]:
|
|
||||||
"""
|
|
||||||
Args:
|
|
||||||
etudid (int): l'identifiant de l'étudiant
|
|
||||||
justificatifs (list[Justificatif]): La liste des justificatifs qui seront utilisés
|
|
||||||
reset (bool, optional): remet à false les assiduites non justifiés. Defaults to False.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[int]: la liste des assiduités qui ont été justifiées.
|
|
||||||
"""
|
|
||||||
# TODO à optimiser (car très long avec 40000 assiduités)
|
|
||||||
# On devrait :
|
|
||||||
# - récupérer uniquement les assiduités qui sont sur la période des justificatifs donnés
|
|
||||||
# - Pour chaque assiduité trouvée, il faut récupérer les justificatifs qui la justifie
|
|
||||||
# - Si au moins un justificatif valide couvre la période de l'assiduité alors on la justifie
|
|
||||||
|
|
||||||
# Si on ne donne pas de justificatifs on prendra par défaut tous les justificatifs de l'étudiant
|
|
||||||
if justificatifs is None:
|
|
||||||
justificatifs: list[Justificatif] = Justificatif.query.filter_by(
|
|
||||||
etudid=etudid
|
|
||||||
).all()
|
|
||||||
|
|
||||||
# On ne prend que les justificatifs valides
|
|
||||||
justificatifs = [j for j in justificatifs if j.etat == EtatJustificatif.VALIDE]
|
|
||||||
|
|
||||||
# On récupère les assiduités de l'étudiant
|
|
||||||
assiduites: Assiduite = Assiduite.query.filter_by(etudid=etudid)
|
|
||||||
|
|
||||||
assiduites_justifiees: list[int] = []
|
|
||||||
|
|
||||||
for assi in assiduites:
|
|
||||||
# On ne justifie pas les Présences
|
|
||||||
if assi.etat == EtatAssiduite.PRESENT:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# On récupère les justificatifs qui justifient l'assiduité `assi`
|
|
||||||
assi_justificatifs = Justificatif.query.filter(
|
|
||||||
Justificatif.etudid == assi.etudid,
|
|
||||||
Justificatif.date_debut <= assi.date_debut,
|
|
||||||
Justificatif.date_fin >= assi.date_fin,
|
|
||||||
Justificatif.etat == EtatJustificatif.VALIDE,
|
|
||||||
).all()
|
|
||||||
|
|
||||||
# Si au moins un justificatif possède une période qui couvre l'assiduité
|
|
||||||
if any(
|
|
||||||
assi.date_debut >= j.date_debut and assi.date_fin <= j.date_fin
|
|
||||||
for j in justificatifs + assi_justificatifs
|
|
||||||
):
|
|
||||||
# On justifie l'assiduité
|
|
||||||
# On ajoute l'id de l'assiduité à la liste des assiduités justifiées
|
|
||||||
assi.est_just = True
|
|
||||||
assiduites_justifiees.append(assi.assiduite_id)
|
|
||||||
db.session.add(assi)
|
|
||||||
elif reset:
|
|
||||||
# Si le paramètre reset est Vrai alors les assiduités non justifiées
|
|
||||||
# sont remise en "non justifiée"
|
|
||||||
assi.est_just = False
|
|
||||||
db.session.add(assi)
|
|
||||||
# On valide la session
|
|
||||||
db.session.commit()
|
|
||||||
# On renvoie la liste des assiduite_id des assiduités justifiées
|
|
||||||
return assiduites_justifiees
|
|
||||||
|
|
||||||
|
|
||||||
def get_assiduites_justif(assiduite_id: int, long: bool) -> list[int | dict]:
|
def get_assiduites_justif(assiduite_id: int, long: bool) -> list[int | dict]:
|
||||||
"""
|
"""
|
||||||
get_assiduites_justif Récupération des justificatifs d'une assiduité
|
get_assiduites_justif Récupération des justificatifs d'une assiduité
|
||||||
|
@ -17,7 +17,7 @@ from app.models import (
|
|||||||
ModuleImplInscription,
|
ModuleImplInscription,
|
||||||
ScoDocSiteConfig,
|
ScoDocSiteConfig,
|
||||||
)
|
)
|
||||||
from app.models.assiduites import Assiduite, Justificatif, compute_assiduites_justified
|
from app.models.assiduites import Assiduite, Justificatif
|
||||||
from app.scodoc import sco_formsemestre_inscriptions
|
from app.scodoc import sco_formsemestre_inscriptions
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
@ -111,9 +111,9 @@ class CountCalculator:
|
|||||||
evening if evening else ScoDocSiteConfig.get("assi_afternoon_time", "18:00")
|
evening if evening else ScoDocSiteConfig.get("assi_afternoon_time", "18:00")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.non_work_days: list[scu.NonWorkDays] = (
|
self.non_work_days: list[
|
||||||
scu.NonWorkDays.get_all_non_work_days(dept_id=g.scodoc_dept_id)
|
scu.NonWorkDays
|
||||||
)
|
] = scu.NonWorkDays.get_all_non_work_days(dept_id=g.scodoc_dept_id)
|
||||||
|
|
||||||
# Sera utilisé pour les assiduités longues (> 1 journée)
|
# Sera utilisé pour les assiduités longues (> 1 journée)
|
||||||
self.nb_heures_par_jour = (
|
self.nb_heures_par_jour = (
|
||||||
@ -661,7 +661,7 @@ def create_absence_billet(
|
|||||||
db.session.add(justi)
|
db.session.add(justi)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
compute_assiduites_justified(etud.id, [justi])
|
justi.justifier_assiduites()
|
||||||
|
|
||||||
calculator: CountCalculator = CountCalculator()
|
calculator: CountCalculator = CountCalculator()
|
||||||
calculator.compute_assiduites([assiduite_unique])
|
calculator.compute_assiduites([assiduite_unique])
|
||||||
|
@ -64,7 +64,7 @@ from app.models import (
|
|||||||
)
|
)
|
||||||
from app.scodoc.codes_cursus import UE_STANDARD
|
from app.scodoc.codes_cursus import UE_STANDARD
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.models.assiduites import get_assiduites_justif, compute_assiduites_justified
|
from app.models.assiduites import get_assiduites_justif
|
||||||
from app.tables.list_etuds import RowEtud, TableEtud
|
from app.tables.list_etuds import RowEtud, TableEtud
|
||||||
import app.tables.liste_assiduites as liste_assi
|
import app.tables.liste_assiduites as liste_assi
|
||||||
|
|
||||||
@ -468,7 +468,7 @@ def _record_assiduite_etud(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# On met à jour les assiduités en fonction du nouveau justificatif
|
# On met à jour les assiduités en fonction du nouveau justificatif
|
||||||
compute_assiduites_justified(etud.id, [justi])
|
justi.justifier_assiduites()
|
||||||
|
|
||||||
# Invalider cache
|
# Invalider cache
|
||||||
scass.simple_invalidate_cache(ass.to_dict(), etud.id)
|
scass.simple_invalidate_cache(ass.to_dict(), etud.id)
|
||||||
@ -778,6 +778,7 @@ def _record_justificatif_etud(
|
|||||||
form.date_debut.data = dt_debut_tz_server
|
form.date_debut.data = dt_debut_tz_server
|
||||||
form.date_fin.data = dt_fin_tz_server
|
form.date_fin.data = dt_fin_tz_server
|
||||||
form.entry_date.data = dt_entry_date_tz_server
|
form.entry_date.data = dt_entry_date_tz_server
|
||||||
|
justif.dejustifier_assiduites()
|
||||||
if justif.edit_from_form(form):
|
if justif.edit_from_form(form):
|
||||||
message = "Justificatif modifié"
|
message = "Justificatif modifié"
|
||||||
|
|
||||||
@ -792,7 +793,6 @@ def _record_justificatif_etud(
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
message = "Pas de modification"
|
message = "Pas de modification"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
justif = Justificatif.create_justificatif(
|
justif = Justificatif.create_justificatif(
|
||||||
etud,
|
etud,
|
||||||
@ -816,7 +816,7 @@ def _record_justificatif_etud(
|
|||||||
# pour utiliser le "reset" (remise en "non_just") des assiduités
|
# pour utiliser le "reset" (remise en "non_just") des assiduités
|
||||||
# (à terme, il faudrait ne recalculer que les assiduités impactées)
|
# (à terme, il faudrait ne recalculer que les assiduités impactées)
|
||||||
# VOIR TODO dans compute_assiduites_justified
|
# VOIR TODO dans compute_assiduites_justified
|
||||||
compute_assiduites_justified(etud.id, reset=True)
|
justif.justifier_assiduites()
|
||||||
scass.simple_invalidate_cache(justif.to_dict(), etud.id)
|
scass.simple_invalidate_cache(justif.to_dict(), etud.id)
|
||||||
flash(message)
|
flash(message)
|
||||||
return True
|
return True
|
||||||
@ -1595,7 +1595,7 @@ def tableau_assiduite_actions():
|
|||||||
user_id=current_user.id,
|
user_id=current_user.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
compute_assiduites_justified(objet.etudiant.id, [justificatif_correspondant])
|
justificatif_correspondant.justifier_assiduites()
|
||||||
scass.simple_invalidate_cache(
|
scass.simple_invalidate_cache(
|
||||||
justificatif_correspondant.to_dict(), objet.etudiant.id
|
justificatif_correspondant.to_dict(), objet.etudiant.id
|
||||||
)
|
)
|
||||||
@ -1707,9 +1707,10 @@ def _action_modifier_justificatif(justi: Justificatif):
|
|||||||
|
|
||||||
justi.fichier = archive_name
|
justi.fichier = archive_name
|
||||||
|
|
||||||
|
justi.dejustifier_assiduites()
|
||||||
db.session.add(justi)
|
db.session.add(justi)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
scass.compute_assiduites_justified(justi.etudid, reset=True)
|
justi.justifier_assiduites()
|
||||||
scass.simple_invalidate_cache(justi.to_dict(True), justi.etudid)
|
scass.simple_invalidate_cache(justi.to_dict(True), justi.etudid)
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ from app.models import (
|
|||||||
ModuleImpl,
|
ModuleImpl,
|
||||||
Absence,
|
Absence,
|
||||||
)
|
)
|
||||||
from app.models.assiduites import compute_assiduites_justified
|
|
||||||
|
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
from tests.unit import sco_fake_gen
|
from tests.unit import sco_fake_gen
|
||||||
@ -498,10 +497,9 @@ def ajouter_justificatifs(etud):
|
|||||||
)
|
)
|
||||||
db.session.add(just_obj)
|
db.session.add(just_obj)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
just_obj.justifier_assiduites()
|
||||||
justificatifs.append(just_obj)
|
justificatifs.append(just_obj)
|
||||||
|
|
||||||
compute_assiduites_justified(etud.etudid, justificatifs)
|
|
||||||
|
|
||||||
# Vérification de la création des justificatifs
|
# Vérification de la création des justificatifs
|
||||||
assert [
|
assert [
|
||||||
justi for justi in justificatifs if not isinstance(justi, Justificatif)
|
justi for justi in justificatifs if not isinstance(justi, Justificatif)
|
||||||
@ -1462,7 +1460,7 @@ def test_cas_justificatifs(test_client):
|
|||||||
etat=scu.EtatJustificatif.VALIDE,
|
etat=scu.EtatJustificatif.VALIDE,
|
||||||
)
|
)
|
||||||
|
|
||||||
compute_assiduites_justified(etud_1.etudid, [justif_2])
|
justif_2.justifier_assiduites()
|
||||||
|
|
||||||
assert len(scass.justifies(justif_2)) == 1, "Justification non prise en compte (b1)"
|
assert len(scass.justifies(justif_2)) == 1, "Justification non prise en compte (b1)"
|
||||||
|
|
||||||
@ -1496,7 +1494,8 @@ def test_cas_justificatifs(test_client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Mise à jour de l'assiduité
|
# Mise à jour de l'assiduité
|
||||||
compute_assiduites_justified(etud_1.etudid, [justif_3, justif_4])
|
justif_3.justifier_assiduites()
|
||||||
|
justif_4.justifier_assiduites()
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
len(scass.justifies(justif_3)) == 1
|
len(scass.justifies(justif_3)) == 1
|
||||||
|
@ -33,7 +33,6 @@ from app.scodoc import sco_formsemestre_validation
|
|||||||
from app.scodoc import sco_cursus_dut
|
from app.scodoc import sco_cursus_dut
|
||||||
from app.scodoc import sco_saisie_notes
|
from app.scodoc import sco_saisie_notes
|
||||||
from app.scodoc.sco_utils import EtatAssiduite, EtatJustificatif, localize_datetime
|
from app.scodoc.sco_utils import EtatAssiduite, EtatJustificatif, localize_datetime
|
||||||
from app.models.assiduites import compute_assiduites_justified
|
|
||||||
|
|
||||||
DEPT = TestConfig.DEPT_TEST
|
DEPT = TestConfig.DEPT_TEST
|
||||||
|
|
||||||
@ -267,8 +266,5 @@ def _signal_absences_justificatifs(etudid: int):
|
|||||||
etat=EtatJustificatif.VALIDE,
|
etat=EtatJustificatif.VALIDE,
|
||||||
)
|
)
|
||||||
db.session.add(justif)
|
db.session.add(justif)
|
||||||
compute_assiduites_justified(
|
|
||||||
etud.etudid,
|
|
||||||
[justif],
|
|
||||||
)
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
justif.justifier_assiduites()
|
||||||
|
Loading…
Reference in New Issue
Block a user