diff --git a/tests/unit/test_assiduites.py b/tests/unit/test_assiduites.py index efd8d4d052..79f7d430b1 100644 --- a/tests/unit/test_assiduites.py +++ b/tests/unit/test_assiduites.py @@ -8,18 +8,14 @@ ses fonctions liées Ecrit par HARTMANN Matthias (en s'inspirant de tests.unit.test_abs_count.py par Fares Amer ) """ -from tests.unit import sco_fake_gen - -from app import db - -from app.scodoc import sco_formsemestre import app.scodoc.sco_assiduites as scass -from app.models import Assiduite, Justificatif, Identite, FormSemestre, ModuleImpl -from app.scodoc.sco_exceptions import ScoValueError import app.scodoc.sco_utils as scu -from app.scodoc.sco_abs import get_abs_count_in_interval -from app.scodoc import sco_abs_views -from tools import migrate_abs_to_assiduites, downgrade_module +from app import db +from app.models import Assiduite, FormSemestre, Identite, Justificatif, ModuleImpl +from app.scodoc import sco_abs_views, sco_formsemestre +from app.scodoc.sco_exceptions import ScoValueError +from tests.unit import sco_fake_gen +from tools import downgrade_module, migrate_abs_to_assiduites class BiInt(int, scu.BiDirectionalEnum): @@ -153,43 +149,179 @@ def verif_migration_abs_assiduites(): etudid: int = 1 - for debut, fin, demijournee in [ + for debut, fin, demijournee, justifiee in [ ( "02/01/2023", - "10/01/2023", + "02/01/2023", + 1, + False, + ), # 1 assi 02/01/2023 8h > 13h (1dj) + ( + "03/01/2023", + "03/01/2023", + 0, + False, + ), # 1 assi 03/01/2023 13h > 18h (1dj) + ( + "05/01/2023", + "05/01/2023", 2, - ), # 2 assiduités 02/01: 08h -> 06/01: 18h & assiduités 09/01: 08h -> 10/01: 18h | 14dj - ("16/01/2023", "16/01/2023", 1), # 1 assiduité 16/01: 08h -> 16/01: 12h | 1dj - ("19/01/2023", "19/01/2023", 0), # 1 assiduité 19/01: 12h -> 19/01: 18h | 1dj - ("18/01/2023", "18/01/2023", 2), # 1 assiduité 18/01: 08h -> 18/01: 18h | 2dj - ("23/01/2023", "23/01/2023", 0), # 1 assiduité 23/01: 12h -> 24/01: 18h | 3dj - ("24/01/2023", "24/01/2023", 2), + False, + ), # 1 assi 05/01/2023 8h > 18h (2dj) + ( + "09/01/2023", + "09/01/2023", + 1, + False, + ), + ( + "09/01/2023", + "09/01/2023", + 0, + False, + ), # 1 assi 09/01/2023 8h > 18h (2dj) + ( + "10/01/2023", + "10/01/2023", + 0, + False, + ), + ( + "11/01/2023", + "11/01/2023", + 1, + False, + ), # 1 assi 10/01/2023 - 11/01/2023 13h > 13h (2dj) + ( + "12/01/2023", + "12/01/2023", + 1, + False, + ), # 1 assi 12/01/2023 8h > 13h (1dj) + ( + "13/01/2023", + "13/01/2023", + 1, + False, + ), # 1 assi 13/01/2023 8h > 13h (1dj) + ( + "16/01/2023", + "16/01/2023", + 1, + False, + ), + ( + "16/01/2023", + "16/01/2023", + 1, + False, + ), # 1 assi 16/01/2023 8h > 13h (1dj) + ( + "19/01/2023", + "24/01/2023", + 2, + False, + ), # 2 assi 19/01/2023 - 20/01/2023 8h > 18h (4dj) + 23/01/23 - 24/01/2023 8h>18h (4dj) + ( + "01/02/2023", + "01/02/2023", + 1, + True, + ), # 1 assi 01/02/2023 8h > 13h (1dj) JUSTI + ( + "02/02/2023", + "02/02/2023", + 0, + True, + ), # 1 assi 02/02/2023 13h > 18h (1dj) JUSTI + ( + "06/02/2023", + "06/02/2023", + 2, + True, + ), # 1 assi 06/02/2023 8h > 18h (2dj) JUSTI + ( + "07/02/2023", + "07/02/2023", + 0, + True, + ), + ( + "07/02/2023", + "07/02/2023", + 0, + False, + ), # 1 assi 07/02/2023 13h > 18h (1dj) JUSTI + ( + "08/02/2023", + "08/02/2023", + 0, + False, + ), + ( + "08/02/2023", + "08/02/2023", + 0, + True, + ), # 1 assi 08/02/2023 13h > 18h (1dj) JUSTI + ( + "10/02/2023", + "10/02/2023", + 1, + True, + ), + ( + "10/02/2023", + "10/02/2023", + 0, + False, + ), # 1 assi 10/02/2023 08h > 18h (2dj) JUSTI + ( + "13/02/2023", + "13/02/2023", + 1, + False, + ), + ( + "13/02/2023", + "13/02/2023", + 0, + True, + ), # 1 assi 13/02/2023 08h > 18h (2dj) JUSTI + ( + "15/02/2023", + "15/02/2023", + 2, + False, + ), # 1 assi 13/02/2023 08h > 18h (2dj) JUSTI(ext) + ( + "22/02/2023", + "24/02/2023", + 1, + False, + ), # 3 assi 22-23-24/02/2023 08h > 13h (3dj) JUSTI(ext) ]: sco_abs_views.doSignaleAbsence( datedebut=debut, datefin=fin, demijournee=demijournee, etudid=etudid, + estjust=justifiee, ) # --- Justification de certaines absences for debut, fin, demijournee in [ ( - "02/01/2023", - "10/01/2023", + "15/02/2023", + "15/02/2023", 2, - ), # 2 justificatif 02/01: 08h -> 06/01: 18h & justificatif 09/01: 08h -> 10/01: 18h | 14dj + ), ( - "19/01/2023", - "19/01/2023", - 0, - ), # 1 justificatif 19/01: 12h -> 19/01: 18h | 1dj - ( - "18/01/2023", - "18/01/2023", + "21/02/2023", + "24/02/2023", 2, - ), # 1 justificatif 18/01: 08h -> 18/01: 18h | 2dj + ), ]: sco_abs_views.doJustifAbsence( datedebut=debut, @@ -200,22 +332,147 @@ def verif_migration_abs_assiduites(): migrate_abs_to_assiduites() - assert Assiduite.query.count() == 6, "Erreur migration assiduites" - assert Justificatif.query.count() == 4, "Erreur migration justificatifs" + assert Assiduite.query.count() == 21, "Migration : Nb Assiduités FAUX" + assert Justificatif.query.count() == 9, "Migration : Nb Justificatif FAUX" + + # Cas classiques sans justification + + assert ( + _get_assi("2023-01-02T08:00", "2023-01-02T13:00") is not None + ), "Migration : Abs n°1 mal migrée" + assert ( + _get_assi("2023-01-03T13:00", "2023-01-03T18:00") is not None + ), "Migration : Abs n°2 mal migrée" + assert ( + _get_assi("2023-01-05T08:00", "2023-01-05T18:00") is not None + ), "Migration : Abs n°3 mal migrée" + assert ( + _get_assi("2023-01-09T08:00", "2023-01-09T18:00") is not None + ), "Migration : Abs n°4&5 mal migrée" + assert ( + _get_assi("2023-01-10T13:00", "2023-01-11T13:00") is not None + ), "Migration : Abs n°6&7 mal migrée" + assert ( + _get_assi("2023-01-12T08:00", "2023-01-12T13:00") is not None + ), "Migration : Abs n°8 mal migrée" + assert ( + _get_assi("2023-01-13T08:00", "2023-01-13T13:00") is not None + ), "Migration : Abs n°9 mal migrée" + assert ( + _get_assi("2023-01-16T08:00", "2023-01-16T13:00") is not None + ), "Migration : Abs n°10&11 mal migrée" + assert ( + _get_assi("2023-01-19T08:00", "2023-01-20T18:00") is not None + ), "Migration : Abs n°12 mal migrée" + assert ( + _get_assi("2023-01-23T08:00", "2023-01-24T18:00") is not None + ), "Migration : Abs n°12 mal migrée" + + # Cas d'absences justifiées + + assert ( + _get_assi("2023-02-01T08:00", "2023-02-01T13:00", True) is not None + ), "Migration : Abs n°13 mal migrée" + assert ( + _get_assi("2023-02-02T13:00", "2023-02-02T18:00", True) is not None + ), "Migration : Abs n°14 mal migrée" + assert ( + _get_assi("2023-02-06T08:00", "2023-02-06T18:00", True) is not None + ), "Migration : Abs n°15 mal migrée" + assert ( + _get_assi("2023-02-07T13:00", "2023-02-07T18:00", True) is not None + ), "Migration : Abs n°16&17 mal migrée" + assert ( + _get_assi("2023-02-08T13:00", "2023-02-08T18:00", True) is not None + ), "Migration : Abs n°18&19 mal migrée" + assert ( + _get_assi("2023-02-10T08:00", "2023-02-10T18:00", True) is not None + ), "Migration : Abs n°20&21 mal migrée" + assert ( + _get_assi("2023-02-13T08:00", "2023-02-13T18:00", True) is not None + ), "Migration : Abs n°22&23 mal migrée" + + # Cas Justificatifs + + assert ( + _get_justi("2023-02-01T08:00", "2023-02-01T13:00") is not None + ), "Migration : Abs n°13 mal migrée" + assert ( + _get_justi("2023-02-02T13:00", "2023-02-02T18:00") is not None + ), "Migration : Abs n°14 mal migrée" + assert ( + _get_justi("2023-02-06T08:00", "2023-02-06T18:00") is not None + ), "Migration : Abs n°15 mal migrée" + assert ( + _get_justi("2023-02-07T13:00", "2023-02-07T18:00") is not None + ), "Migration : Abs n°16&17 mal migrée" + assert ( + _get_justi("2023-02-08T13:00", "2023-02-08T18:00") is not None + ), "Migration : Abs n°18&19 mal migrée" + assert ( + _get_justi("2023-02-10T08:00", "2023-02-10T18:00") is not None + ), "Migration : Abs n°20&21 mal migrée" + assert ( + _get_justi("2023-02-13T08:00", "2023-02-13T18:00") is not None + ), "Migration : Abs n°22&23 mal migrée" + assert ( + _get_justi("2023-02-15T08:00", "2023-02-15T18:00") is not None + ), "Migration : Justi n°1 mal migré" + assert ( + _get_assi("2023-02-15T08:00", "2023-02-15T18:00", True) is not None + ), "Migration : Abs n°24 mal migrée" + assert ( + _get_justi("2023-02-21T08:00", "2023-02-24T18:00") is not None + ), "Migration : Justi n°2 mal migré" + assert ( + _get_assi("2023-02-22T08:00", "2023-02-22T13:00", True) is not None + ), "Migration : Abs n°25 mal migrée" + assert ( + _get_assi("2023-02-23T08:00", "2023-02-23T13:00", True) is not None + ), "Migration : Abs n°26 mal migrée" + assert ( + _get_assi("2023-02-24T08:00", "2023-02-24T13:00", True) is not None + ), "Migration : Abs n°27 mal migrée" essais_cache(etudid) downgrade_module(assiduites=True, justificatifs=True) +def _get_assi( + deb: str, + fin: str, + est_just: bool = False, +): + deb = scu.localize_datetime(scu.is_iso_formated(deb, True)) + fin = scu.localize_datetime(scu.is_iso_formated(fin, True)) + + return Assiduite.query.filter( + Assiduite.date_debut >= deb, + Assiduite.date_fin <= fin, + Assiduite.est_just == est_just, + ).first() + + +def _get_justi( + deb: str, + fin: str, +): + deb = scu.localize_datetime(scu.is_iso_formated(deb, True)) + fin = scu.localize_datetime(scu.is_iso_formated(fin, True)) + + return Justificatif.query.filter( + Justificatif.date_debut >= deb, + Justificatif.date_fin <= fin, + ).first() + + def essais_cache(etudid): """Vérification des fonctionnalités du cache TODO:WIP""" date_deb: str = "2023-01-01T07:00" date_fin: str = "2023-03-31T19:00" - abs_count_no_cache: int = get_abs_count_in_interval(etudid, date_deb, date_fin) - abs_count_cache = get_abs_count_in_interval(etudid, date_deb, date_fin) assiduites_count_no_cache = scass.get_assiduites_count_in_interval( etudid, date_deb, date_fin ) @@ -224,11 +481,7 @@ def essais_cache(etudid): ) assert ( - abs_count_cache - == abs_count_no_cache - == assiduites_count_cache - == assiduites_count_no_cache - == (21, 17) + assiduites_count_cache == assiduites_count_no_cache == (34, 15) ), "Erreur cache" diff --git a/tools/migrate_abs_to_assiduites.py b/tools/migrate_abs_to_assiduites.py index 8843f4bced..75f5ac02f9 100644 --- a/tools/migrate_abs_to_assiduites.py +++ b/tools/migrate_abs_to_assiduites.py @@ -46,6 +46,8 @@ class _glob: MERGER_ASSI: "_Merger" = None MERGER_JUST: "_Merger" = None + JUSTIFS: dict[int, list[tuple[datetime, datetime]]] = {} + MORNING: time = None NOON: time = None EVENING: time = None @@ -60,6 +62,7 @@ class _Merger: self.est_abs = est_abs self.raison = abs_.description self.entry_date = abs_.entry_date + self.est_just = abs_.estjust def merge(self, abs_: Absence) -> bool: """Fusionne les absences. @@ -78,10 +81,13 @@ class _Merger: return False else: day_after: date = abs_.jour - timedelta(days=1) == self.fin[0] - if not (day_after and abs_.matin): + if not (day_after and abs_.matin and self.est_just == abs_.estjust): return False + self.est_just = self.est_just or abs_.estjust + self.fin = (abs_.jour, abs_.matin) + return True @staticmethod @@ -99,6 +105,8 @@ class _Merger: date_deb = _Merger._tuple_to_date(self.deb) date_fin = _Merger._tuple_to_date(self.fin, end=True) + _glob.JUSTIFS[self.etudid].append((date_deb, date_fin)) + _glob.cursor.execute( """INSERT INTO justificatifs (etudid,date_debut,date_fin,etat,raison,entry_date) @@ -127,10 +135,19 @@ class _Merger: date_deb = _Merger._tuple_to_date(self.deb) date_fin = _Merger._tuple_to_date(self.fin, end=True) + self.est_just = ( + _assi_in_justifs(date_deb, date_fin, self.etudid) or self.est_just + ) + if _glob.MERGER_JUST is not None and not self.est_just: + justi_date_deb = _Merger._tuple_to_date(_glob.MERGER_JUST.deb) + justi_date_fin = _Merger._tuple_to_date(_glob.MERGER_JUST.fin, end=True) + justifiee = date_deb >= justi_date_deb and date_fin <= justi_date_fin + self.est_just = justifiee + _glob.cursor.execute( """INSERT INTO assiduites - (etudid,date_debut,date_fin,etat,moduleimpl_id,"description",entry_date) - VALUES (%(etudid)s,%(date_debut)s,%(date_fin)s,%(etat)s,%(moduleimpl_id)s,%(description)s,%(entry_date)s) + (etudid,date_debut,date_fin,etat,moduleimpl_id,description,entry_date,est_just) + VALUES (%(etudid)s,%(date_debut)s,%(date_fin)s,%(etat)s,%(moduleimpl_id)s,%(description)s,%(entry_date)s, %(est_just)s) """, { "etudid": self.etudid, @@ -140,6 +157,7 @@ class _Merger: "moduleimpl_id": self.moduleimpl, "description": self.raison, "entry_date": self.entry_date, + "est_just": self.est_just, }, ) @@ -165,6 +183,10 @@ class _Merger: self._to_justif() +def _assi_in_justifs(deb, fin, etudid): + return any(deb >= j[0] and fin <= j[1] for j in _glob.JUSTIFS[etudid]) + + class _Statistics: def __init__(self) -> None: self.object: dict[str, dict or int] = {"total": 0} @@ -307,6 +329,8 @@ def migrate_dept(dept_name: str, stats: _Statistics, time_elapsed: Profiler): raise ValueError(f"Département inexistant: {dept_name}") etuds_id: list[int] = [etud.id for etud in dept.etudiants] + for etudid in etuds_id: + _glob.JUSTIFS[etudid] = [] absences_query = absences_query.filter(Absence.etudid.in_(etuds_id)) absences: Absence = absences_query.order_by( Absence.etudid, Absence.jour, not_(Absence.matin) @@ -380,14 +404,14 @@ def migrate_dept(dept_name: str, stats: _Statistics, time_elapsed: Profiler): autosize=True, ) - print( - TerminalColor.RED - + f"Justification des absences du département {dept_name}, veuillez patienter, ceci peut prendre un certain temps." - + TerminalColor.RESET - ) + # print( + # TerminalColor.RED + # + f"Justification des absences du département {dept_name}, veuillez patienter, ceci peut prendre un certain temps." + # + TerminalColor.RESET + # ) - justifs: Justificatif = Justificatif.query.join(Identite).filter_by(dept_id=dept.id) - compute_assiduites_justified(justifs, reset=True) + # justifs: Justificatif = Justificatif.query.join(Identite).filter_by(dept_id=dept.id) + # compute_assiduites_justified(justifs, reset=True) time_elapsed.stop() @@ -432,20 +456,16 @@ def _from_abs_to_assiduite_justificatif(_abs: Absence, etud_modimpl_ids: set[int if _glob.MERGER_ASSI is None: _glob.MERGER_ASSI = _Merger(_abs, True) - return True elif _glob.MERGER_ASSI.merge(_abs): - return True + pass else: _glob.MERGER_ASSI.export() _glob.MERGER_ASSI = _Merger(_abs, True) - return False - - if _glob.MERGER_JUST is None: - _glob.MERGER_JUST = _Merger(_abs, False) - return True - elif _glob.MERGER_JUST.merge(_abs): - return True - else: - _glob.MERGER_JUST.export() - _glob.MERGER_JUST = _Merger(_abs, False) - return False + if _abs.estjust: + if _glob.MERGER_JUST is None: + _glob.MERGER_JUST = _Merger(_abs, False) + elif _glob.MERGER_JUST.merge(_abs): + pass + else: + _glob.MERGER_JUST.export() + _glob.MERGER_JUST = _Merger(_abs, False)