From 5158bd0c8fd137cb476895a3d8c87f0e439be1b7 Mon Sep 17 00:00:00 2001 From: Iziram Date: Thu, 29 Feb 2024 14:20:39 +0100 Subject: [PATCH] =?UTF-8?q?Assiduit=C3=A9=20:=20optimisation=20justificati?= =?UTF-8?q?on=20assiduit=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/justificatifs.py | 21 ++---- app/models/assiduites.py | 130 +++++++++++++++------------------- app/scodoc/sco_assiduites.py | 10 +-- app/views/assiduites.py | 13 ++-- tests/unit/test_assiduites.py | 9 ++- tests/unit/test_sco_basic.py | 6 +- 6 files changed, 81 insertions(+), 108 deletions(-) diff --git a/app/api/justificatifs.py b/app/api/justificatifs.py index f048d1a67..0a5f5350a 100644 --- a/app/api/justificatifs.py +++ b/app/api/justificatifs.py @@ -22,7 +22,6 @@ from app.api import get_model_api_object, tools from app.decorators import permission_required, scodoc from app.models import Identite, Justificatif, Departement, FormSemestre, Scolog from app.models.assiduites import ( - compute_assiduites_justified, get_formsemestre_from_data, ) 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] = [] success: list[dict] = [] - justifs: list[Justificatif] = [] # énumération des justificatifs 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}) else: success.append({"indice": i, "message": obj}) - justifs.append(justi) + justi.justifier_assiduites() 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} @@ -495,6 +491,7 @@ def justif_edit(justif_id: int): return json_error(404, err) # Mise à jour du justificatif + justificatif_unique.dejustifier_assiduites() db.session.add(justificatif_unique) db.session.commit() @@ -511,11 +508,7 @@ def justif_edit(justif_id: int): retour = { "couverture": { "avant": avant_ids, - "apres": compute_assiduites_justified( - justificatif_unique.etudid, - [justificatif_unique], - True, - ), + "apres": justificatif_unique.justifier_assiduites(), } } # Invalide le cache @@ -592,14 +585,10 @@ def _delete_one(justif_id: int) -> tuple[int, str]: # On invalide le cache 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 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") diff --git a/app/models/assiduites.py b/app/models/assiduites.py index 8580179bd..db7a25872 100644 --- a/app/models/assiduites.py +++ b/app/models/assiduites.py @@ -574,11 +574,7 @@ class Justificatif(ScoDocModel): db.session.delete(self) db.session.commit() # On actualise les assiduités justifiées de l'étudiant concerné - compute_assiduites_justified( - self.etudid, - Justificatif.query.filter_by(etudid=self.etudid).all(), - True, - ) + self.dejustifier_assiduites() def get_fichiers(self) -> tuple[list[str], int]: """Renvoie la liste des noms de fichiers justicatifs @@ -600,6 +596,64 @@ class Justificatif(ScoDocModel): accessible_filenames.append(filename[0]) 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( date_debut: datetime, @@ -623,72 +677,6 @@ def is_period_conflicting( 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]: """ get_assiduites_justif Récupération des justificatifs d'une assiduité diff --git a/app/scodoc/sco_assiduites.py b/app/scodoc/sco_assiduites.py index 3b9917406..597accb98 100644 --- a/app/scodoc/sco_assiduites.py +++ b/app/scodoc/sco_assiduites.py @@ -17,7 +17,7 @@ from app.models import ( ModuleImplInscription, 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_preferences from app.scodoc import sco_cache @@ -111,9 +111,9 @@ class CountCalculator: evening if evening else ScoDocSiteConfig.get("assi_afternoon_time", "18:00") ) - self.non_work_days: list[scu.NonWorkDays] = ( - scu.NonWorkDays.get_all_non_work_days(dept_id=g.scodoc_dept_id) - ) + self.non_work_days: list[ + 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) self.nb_heures_par_jour = ( @@ -661,7 +661,7 @@ def create_absence_billet( db.session.add(justi) db.session.commit() - compute_assiduites_justified(etud.id, [justi]) + justi.justifier_assiduites() calculator: CountCalculator = CountCalculator() calculator.compute_assiduites([assiduite_unique]) diff --git a/app/views/assiduites.py b/app/views/assiduites.py index 16d5c9a06..6b2e931a2 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -64,7 +64,7 @@ from app.models import ( ) from app.scodoc.codes_cursus import UE_STANDARD 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 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 - compute_assiduites_justified(etud.id, [justi]) + justi.justifier_assiduites() # Invalider cache 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_fin.data = dt_fin_tz_server form.entry_date.data = dt_entry_date_tz_server + justif.dejustifier_assiduites() if justif.edit_from_form(form): message = "Justificatif modifié" @@ -792,7 +793,6 @@ def _record_justificatif_etud( ) else: message = "Pas de modification" - else: justif = Justificatif.create_justificatif( etud, @@ -816,7 +816,7 @@ def _record_justificatif_etud( # pour utiliser le "reset" (remise en "non_just") des assiduités # (à terme, il faudrait ne recalculer que les assiduités impactées) # 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) flash(message) return True @@ -1595,7 +1595,7 @@ def tableau_assiduite_actions(): user_id=current_user.id, ) - compute_assiduites_justified(objet.etudiant.id, [justificatif_correspondant]) + justificatif_correspondant.justifier_assiduites() scass.simple_invalidate_cache( justificatif_correspondant.to_dict(), objet.etudiant.id ) @@ -1707,9 +1707,10 @@ def _action_modifier_justificatif(justi: Justificatif): justi.fichier = archive_name + justi.dejustifier_assiduites() db.session.add(justi) db.session.commit() - scass.compute_assiduites_justified(justi.etudid, reset=True) + justi.justifier_assiduites() scass.simple_invalidate_cache(justi.to_dict(True), justi.etudid) diff --git a/tests/unit/test_assiduites.py b/tests/unit/test_assiduites.py index 0cc7ce594..a07eb732a 100644 --- a/tests/unit/test_assiduites.py +++ b/tests/unit/test_assiduites.py @@ -20,7 +20,6 @@ from app.models import ( ModuleImpl, Absence, ) -from app.models.assiduites import compute_assiduites_justified from app.scodoc.sco_exceptions import ScoValueError from tests.unit import sco_fake_gen @@ -498,10 +497,9 @@ def ajouter_justificatifs(etud): ) db.session.add(just_obj) db.session.commit() + just_obj.justifier_assiduites() justificatifs.append(just_obj) - compute_assiduites_justified(etud.etudid, justificatifs) - # Vérification de la création des justificatifs assert [ justi for justi in justificatifs if not isinstance(justi, Justificatif) @@ -1462,7 +1460,7 @@ def test_cas_justificatifs(test_client): 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)" @@ -1496,7 +1494,8 @@ def test_cas_justificatifs(test_client): ) # Mise à jour de l'assiduité - compute_assiduites_justified(etud_1.etudid, [justif_3, justif_4]) + justif_3.justifier_assiduites() + justif_4.justifier_assiduites() assert ( len(scass.justifies(justif_3)) == 1 diff --git a/tests/unit/test_sco_basic.py b/tests/unit/test_sco_basic.py index d4cb7a729..e139316d7 100644 --- a/tests/unit/test_sco_basic.py +++ b/tests/unit/test_sco_basic.py @@ -33,7 +33,6 @@ from app.scodoc import sco_formsemestre_validation from app.scodoc import sco_cursus_dut from app.scodoc import sco_saisie_notes from app.scodoc.sco_utils import EtatAssiduite, EtatJustificatif, localize_datetime -from app.models.assiduites import compute_assiduites_justified DEPT = TestConfig.DEPT_TEST @@ -267,8 +266,5 @@ def _signal_absences_justificatifs(etudid: int): etat=EtatJustificatif.VALIDE, ) db.session.add(justif) - compute_assiduites_justified( - etud.etudid, - [justif], - ) db.session.commit() + justif.justifier_assiduites()