diff --git a/app/models/assiduites.py b/app/models/assiduites.py index 7f5520df74..0f8933e734 100644 --- a/app/models/assiduites.py +++ b/app/models/assiduites.py @@ -350,11 +350,15 @@ def compute_assiduites_justified( if justificatifs is None: justificatifs: Justificatif = Justificatif.query.filter_by(etudid=etudid).all() + justificatifs = [j for j in justificatifs if j.etat == EtatJustificatif.VALIDE] + assiduites: Assiduite = Assiduite.query.filter_by(etudid=etudid) assiduites_justifiees: list[int] = [] for assi in assiduites: + if assi.etat == EtatAssiduite.PRESENT: + continue if any( assi.date_debut >= j.date_debut and assi.date_fin <= j.date_fin for j in justificatifs diff --git a/app/scodoc/sco_assiduites.py b/app/scodoc/sco_assiduites.py index 2a478843b6..c7f5fac8a2 100644 --- a/app/scodoc/sco_assiduites.py +++ b/app/scodoc/sco_assiduites.py @@ -212,7 +212,7 @@ def get_assiduites_stats( output: dict = {} calculator: CountCalculator = CountCalculator() - if "split" not in filtered: + if filtered is None or "split" not in filtered: calculator.compute_assiduites(assiduites) count: dict = calculator.to_dict() @@ -382,7 +382,10 @@ def justifies(justi: Justificatif, obj: bool = False) -> list[int] or Query: def get_all_justified( - etudid: int, date_deb: datetime = None, date_fin: datetime = None + etudid: int, + date_deb: datetime = None, + date_fin: datetime = None, + moduleimpl_id: int = None, ) -> Query: """Retourne toutes les assiduités justifiées sur une période""" @@ -393,7 +396,9 @@ def get_all_justified( date_deb = scu.localize_datetime(date_deb) date_fin = scu.localize_datetime(date_fin) - justified = Assiduite.query.filter_by(est_just=True, etudid=etudid) + justified: Query = Assiduite.query.filter_by(est_just=True, etudid=etudid) + if moduleimpl_id is not None: + justified = justified.filter_by(moduleimpl_id=moduleimpl_id) after = filter_by_date( justified, Assiduite, @@ -419,7 +424,7 @@ def get_assiduites_count(etudid: int, sem: dict) -> tuple[int, int]: def formsemestre_get_assiduites_count( - etudid: int, formsemestre: FormSemestre + etudid: int, formsemestre: FormSemestre, moduleimpl_id: int = None ) -> tuple[int, int]: """Les comptes d'absences de cet étudiant dans ce semestre: tuple (nb abs non justifiées, nb abs justifiées) @@ -428,9 +433,14 @@ def formsemestre_get_assiduites_count( metrique = sco_preferences.get_preference("assi_metrique", formsemestre.id) return get_assiduites_count_in_interval( etudid, - date_debut=formsemestre.date_debut, - date_fin=formsemestre.date_fin, + date_debut=scu.localize_datetime( + datetime.combine(formsemestre.date_debut, time(8, 0)) + ), + date_fin=scu.localize_datetime( + datetime.combine(formsemestre.date_fin, time(18, 0)) + ), metrique=scu.translate_assiduites_metric(metrique), + moduleimpl_id=moduleimpl_id, ) @@ -441,6 +451,7 @@ def get_assiduites_count_in_interval( metrique="demi", date_debut: datetime = None, date_fin: datetime = None, + moduleimpl_id: int = None, ): """Les comptes d'absences de cet étudiant entre ces deux dates, incluses: tuple (nb abs, nb abs justifiées) @@ -452,33 +463,44 @@ def get_assiduites_count_in_interval( key = f"{etudid}_{date_debut_iso}_{date_fin_iso}{metrique}_assiduites" r = sco_cache.AbsSemEtudCache.get(key) - if not r: + if not r or moduleimpl_id is not None: date_debut: datetime = date_debut or datetime.fromisoformat(date_debut_iso) date_fin: datetime = date_fin or datetime.fromisoformat(date_fin_iso) - assiduites: Assiduite = Assiduite.query.filter_by(etudid=etudid) + log(f"deb : {date_debut.isoformat()} fin: {date_fin.isoformat()}") + + assiduites: Query = Assiduite.query.filter_by(etudid=etudid) assiduites = assiduites.filter(Assiduite.etat == scu.EtatAssiduite.ABSENT) justificatifs: Justificatif = Justificatif.query.filter_by(etudid=etudid) assiduites = filter_by_date(assiduites, Assiduite, date_debut, date_fin) + + if moduleimpl_id is not None: + assiduites = assiduites.filter_by(moduleimpl_id=moduleimpl_id) + justificatifs = filter_by_date( justificatifs, Justificatif, date_debut, date_fin ) - + log(f"---assi---\n{[a.assiduite_id for a in assiduites]}") calculator: CountCalculator = CountCalculator() calculator.compute_assiduites(assiduites) nb_abs: dict = calculator.to_dict()[metrique] - abs_just: list[Assiduite] = get_all_justified(etudid, date_debut, date_fin) + abs_just: list[Assiduite] = get_all_justified( + etudid, date_debut, date_fin, moduleimpl_id + ) + + log(f"---justi---\n{[a.assiduite_id for a in abs_just]}") calculator.reset() calculator.compute_assiduites(abs_just) nb_abs_just: dict = calculator.to_dict()[metrique] r = (nb_abs, nb_abs_just) - ans = sco_cache.AbsSemEtudCache.set(key, r) - if not ans: - log("warning: get_assiduites_count failed to cache") + if moduleimpl_id is None: + ans = sco_cache.AbsSemEtudCache.set(key, r) + if not ans: + log("warning: get_assiduites_count failed to cache") return r diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index ee8417f3b5..00f0604efb 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -237,7 +237,7 @@ def localize_datetime(date: datetime.datetime or str) -> datetime.datetime: new_date: datetime.datetime = date if new_date.tzinfo is None: try: - new_date = timezone("Europe/Paris").localize(date) + new_date = TIME_ZONE.localize(date) except OverflowError: new_date = timezone("UTC").localize(date) return new_date diff --git a/app/views/notes.py b/app/views/notes.py index 9f69cbd97f..f3ad9e4402 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -1174,22 +1174,9 @@ def view_module_abs(moduleimpl_id, fmt="html"): rows = [] for etud in inscrits: - # TODO-ASSIDUITE ne va pas car ne filtre pas sur le moduleimpl - # nb_abs, nb_abs_just = sco_assiduites.formsemestre_get_assiduites_count(etud.id, modimpl.formsemestre) - nb_abs, nb_abs_just = 0, 0 # XXX TODO-ASSIDUITE - # nb_abs = sco_abs.count_abs( - # etudid=etud.id, - # debut=debut_sem, - # fin=fin_sem, - # moduleimpl_id=moduleimpl_id, - # ) - # if nb_abs: - # nb_abs_just = sco_abs.count_abs_just( - # etudid=etud.id, - # debut=debut_sem, - # fin=fin_sem, - # moduleimpl_id=moduleimpl_id, - # ) + nb_abs, nb_abs_just = sco_assiduites.formsemestre_get_assiduites_count( + etud.id, modimpl.formsemestre, moduleimpl_id=modimpl.id + ) rows.append( { "nomprenom": etud.nomprenom, diff --git a/tests/unit/test_assiduites.py b/tests/unit/test_assiduites.py index 725be9491e..7077f7e776 100644 --- a/tests/unit/test_assiduites.py +++ b/tests/unit/test_assiduites.py @@ -13,8 +13,7 @@ import app.scodoc.sco_assiduites as scass import app.scodoc.sco_utils as scu from app import db from app.models import Assiduite, FormSemestre, Identite, Justificatif, ModuleImpl - -# from app.scodoc import sco_abs_views, sco_formsemestre TODO-ASSIDUITE +from app.models.assiduites import compute_assiduites_justified from app.scodoc.sco_exceptions import ScoValueError from tests.unit import sco_fake_gen @@ -39,7 +38,7 @@ def test_bi_directional_enum(test_client): assert BiInt.inverse()[1] == BiInt.A and BiInt.inverse()[2] == BiInt.B -@pytest.mark.skip # XXX TODO-ASSIDUITE (issue #690) +# @pytest.mark.skip # XXX TODO-ASSIDUITE (issue #690) def test_general(test_client): """tests général du modèle assiduite""" @@ -80,11 +79,11 @@ def test_general(test_client): date_fin="31/07/2024", ) - formsemestre_1 = sco_formsemestre.get_formsemestre( + formsemestre_1 = FormSemestre.get_formsemestre( formsemestre_id_1 ) # Utiliser plutot FormSemestre de nos jours TODO-ASSIDUITE - formsemestre_2 = sco_formsemestre.get_formsemestre(formsemestre_id_2) - formsemestre_3 = sco_formsemestre.get_formsemestre(formsemestre_id_3) + formsemestre_2 = FormSemestre.get_formsemestre(formsemestre_id_2) + formsemestre_3 = FormSemestre.get_formsemestre(formsemestre_id_3) # Création des modulesimpls (4, 2 par semestre) @@ -145,11 +144,14 @@ def test_general(test_client): etuds, moduleimpls, (formsemestre_1, formsemestre_2, formsemestre_3) ) verifier_filtrage_justificatifs(etuds[0], justificatifs) + + essais_cache(etuds[0].etudid, (formsemestre_1, formsemestre_2), moduleimpls) + editer_supprimer_assiduites(etuds, moduleimpls) editer_supprimer_justificatif(etuds[0]) -@pytest.mark.skip # XXX TODO-ASSIDUITE (issue #696) +# XXX TODO-ASSIDUITE (issue #696) def verif_migration_abs_assiduites(): """Vérification que le script de migration fonctionne correctement""" downgrade_module(assiduites=True, justificatifs=True) @@ -476,11 +478,11 @@ def _get_justi( ).first() -def essais_cache(etudid): +def essais_cache(etudid, sems: tuple[FormSemestre], moduleimpls: list[ModuleImpl]): """Vérification des fonctionnalités du cache""" - date_deb: str = "2023-01-01T07:00" - date_fin: str = "2023-03-31T19:00" + date_deb: str = "2022-09-01T07:00" + date_fin: str = "2023-01-31T19:00" assiduites_count_no_cache = scass.get_assiduites_count_in_interval( etudid, date_deb, date_fin @@ -490,8 +492,36 @@ def essais_cache(etudid): ) assert ( - assiduites_count_cache == assiduites_count_no_cache == (34, 15) - ), "Erreur cache" + assiduites_count_cache == assiduites_count_no_cache == (2, 1) + ), "Erreur cache classique" + + assert scass.formsemestre_get_assiduites_count(etudid, sems[0]) == ( + 2, + 1, + ), "Erreur formsemestre_get_assiduites_count (sans module) A" + assert scass.formsemestre_get_assiduites_count(etudid, sems[1]) == ( + 0, + 0, + ), "Erreur formsemestre_get_assiduites_count (sans module) B" + + assert scass.formsemestre_get_assiduites_count( + etudid, sems[0], moduleimpl_id=moduleimpls[0].id + ) == ( + 1, + 1, + ), "Erreur formsemestre_get_assiduites_count (avec module) A" + assert scass.formsemestre_get_assiduites_count( + etudid, sems[0], moduleimpl_id=moduleimpls[1].id + ) == ( + 1, + 0, + ), "Erreur formsemestre_get_assiduites_count (avec module) A" + assert scass.formsemestre_get_assiduites_count( + etudid, sems[0], moduleimpl_id=moduleimpls[2].id + ) == ( + 0, + 0, + ), "Erreur formsemestre_get_assiduites_count (avec module) A" def ajouter_justificatifs(etud): @@ -543,6 +573,8 @@ def ajouter_justificatifs(etud): db.session.commit() 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) @@ -858,7 +890,7 @@ def ajouter_assiduites( def verifier_comptage_et_filtrage_assiduites( - etuds: list[Identite], moduleimpls: list[int], formsemestres: tuple[int] + etuds: list[Identite], moduleimpls: list[int], formsemestres: tuple[FormSemestre] ): """ Deuxième partie: @@ -931,9 +963,6 @@ def verifier_comptage_et_filtrage_assiduites( ), "Filtrage par 'Moduleimpl' mauvais" # Formsemestre - formsemestres = [ - FormSemestre.query.filter_by(id=fms["id"]).first() for fms in formsemestres - ] assert ( scass.filter_by_formsemestre( etu1.assiduites, Assiduite, formsemestres[0]