# -*- mode: python -*- # -*- coding: utf-8 -*- """ Tests unitaires vérifiant le bon fonctionnement du modèle Assiduité et de 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, get_assiduites_count_in_interval, ) from app.scodoc import sco_abs_views from tools import migrate_abs_to_assiduites, downgrade_module class BiInt(int, scu.BiDirectionalEnum): """Classe pour tester la classe BiDirectionalEnum""" A = 1 B = 2 def test_bi_directional_enum(test_client): """Test le bon fonctionnement de la classe BiDirectionalEnum""" assert BiInt.get("A") == BiInt.get("a") == BiInt.A == 1 assert BiInt.get("B") == BiInt.get("b") == BiInt.B == 2 assert BiInt.get("blabla") is None assert BiInt.get("blabla", -1) == -1 assert isinstance(BiInt.inverse(), dict) assert BiInt.inverse()[1] == BiInt.A and BiInt.inverse()[2] == BiInt.B def test_general(test_client): """tests général du modèle assiduite""" g_fake = sco_fake_gen.ScoFake(verbose=False) # Création d'une formation (1) formation_id = g_fake.create_formation() ue_id = g_fake.create_ue( formation_id=formation_id, acronyme="T1", titre="UE TEST 1" ) matiere_id = g_fake.create_matiere(ue_id=ue_id, titre="test matière") module_id_1 = g_fake.create_module( matiere_id=matiere_id, code="Mo1", coefficient=1.0, titre="test module" ) module_id_2 = g_fake.create_module( matiere_id=matiere_id, code="Mo2", coefficient=1.0, titre="test module2" ) # Création semestre (2) formsemestre_id_1 = g_fake.create_formsemestre( formation_id=formation_id, semestre_id=1, date_debut="01/09/2022", date_fin="31/12/2022", ) formsemestre_id_2 = g_fake.create_formsemestre( formation_id=formation_id, semestre_id=2, date_debut="01/01/2023", date_fin="31/07/2023", ) formsemestre_id_3 = g_fake.create_formsemestre( formation_id=formation_id, semestre_id=3, date_debut="01/01/2024", date_fin="31/07/2024", ) formsemestre_1 = sco_formsemestre.get_formsemestre(formsemestre_id_1) formsemestre_2 = sco_formsemestre.get_formsemestre(formsemestre_id_2) formsemestre_3 = sco_formsemestre.get_formsemestre(formsemestre_id_3) # Création des modulesimpls (4, 2 par semestre) moduleimpl_1_1 = g_fake.create_moduleimpl( module_id=module_id_1, formsemestre_id=formsemestre_id_1, ) moduleimpl_1_2 = g_fake.create_moduleimpl( module_id=module_id_2, formsemestre_id=formsemestre_id_1, ) moduleimpl_2_1 = g_fake.create_moduleimpl( module_id=module_id_1, formsemestre_id=formsemestre_id_2, ) moduleimpl_2_2 = g_fake.create_moduleimpl( module_id=module_id_2, formsemestre_id=formsemestre_id_2, ) moduleimpls = [ moduleimpl_1_1, moduleimpl_1_2, moduleimpl_2_1, moduleimpl_2_2, ] moduleimpls = [ ModuleImpl.query.filter_by(id=mi_id).first() for mi_id in moduleimpls ] # Création des étudiants (3) etuds_dict = [ g_fake.create_etud(code_nip=None, prenom=f"etud{i}") for i in range(3) ] etuds = [] for etud in etuds_dict: g_fake.inscrit_etudiant(formsemestre_id=formsemestre_id_1, etud=etud) g_fake.inscrit_etudiant(formsemestre_id=formsemestre_id_2, etud=etud) etuds.append(Identite.query.filter_by(id=etud["id"]).first()) assert None not in etuds, "Problème avec la conversion en Identite" # Etudiant faux etud_faux_dict = g_fake.create_etud(code_nip=None, prenom="etudfaux") etud_faux = Identite.query.filter_by(id=etud_faux_dict["id"]).first() verif_migration_abs_assiduites() ajouter_assiduites(etuds, moduleimpls, etud_faux) justificatifs: list[Justificatif] = ajouter_justificatifs(etuds[0]) verifier_comptage_et_filtrage_assiduites( etuds, moduleimpls, (formsemestre_1, formsemestre_2, formsemestre_3) ) verifier_filtrage_justificatifs(etuds[0], justificatifs) editer_supprimer_assiduites(etuds, moduleimpls) editer_supprimer_justificatif(etuds[0]) def verif_migration_abs_assiduites(): """Vérification que le script de migration fonctionne correctement""" downgrade_module(assiduites=True, justificatifs=True) etudid: int = 1 for debut, fin, demijournee in [ ( "02/01/2023", "10/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), ]: sco_abs_views.doSignaleAbsence( datedebut=debut, datefin=fin, demijournee=demijournee, etudid=etudid, ) # --- Justification de certaines absences for debut, fin, demijournee in [ ( "02/01/2023", "10/01/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", 2, ), # 1 justificatif 18/01: 08h -> 18/01: 18h | 2dj ]: sco_abs_views.doJustifAbsence( datedebut=debut, datefin=fin, demijournee=demijournee, etudid=etudid, ) migrate_abs_to_assiduites() assert Assiduite.query.count() == 6, "Erreur migration assiduites" assert Justificatif.query.count() == 4, "Erreur migration justificatifs" essais_cache(etudid) downgrade_module(assiduites=True, justificatifs=True) 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 = get_assiduites_count_in_interval( etudid, date_deb, date_fin ) assiduites_count_cache = get_assiduites_count_in_interval( etudid, date_deb, date_fin ) assert ( abs_count_cache == abs_count_no_cache == assiduites_count_cache == assiduites_count_no_cache == (21, 17) ), "Erreur cache" def ajouter_justificatifs(etud): """test de l'ajout des justificatifs""" obj_justificatifs = [ { "etat": scu.EtatJustificatif.ATTENTE, "deb": "2022-09-03T08:00+01:00", "fin": "2022-09-03T09:59:59+01:00", "raison": None, }, { "etat": scu.EtatJustificatif.VALIDE, "deb": "2023-01-03T07:00+01:00", "fin": "2023-01-03T11:00+01:00", "raison": None, }, { "etat": scu.EtatJustificatif.VALIDE, "deb": "2022-09-03T10:00:00+01:00", "fin": "2022-09-03T12:00+01:00", "raison": None, }, { "etat": scu.EtatJustificatif.NON_VALIDE, "deb": "2022-09-03T14:00:00+01:00", "fin": "2022-09-03T15:00+01:00", "raison": "Description", }, { "etat": scu.EtatJustificatif.MODIFIE, "deb": "2023-01-03T11:30+01:00", "fin": "2023-01-03T12:00+01:00", "raison": None, }, ] justificatifs = [ Justificatif.create_justificatif( etud, scu.is_iso_formated(just["deb"], True), scu.is_iso_formated(just["fin"], True), just["etat"], just["raison"], ) for just in obj_justificatifs ] # Vérification de la création des justificatifs assert [ justi for justi in justificatifs if not isinstance(justi, Justificatif) ] == [], "La création des justificatifs de base n'est pas OK" # Vérification de la gestion des erreurs test_assiduite = { "etat": scu.EtatJustificatif.ATTENTE, "deb": "2023-01-03T11:00:01+01:00", "fin": "2023-01-03T12:00+01:00", "raison": "Description", } return justificatifs def verifier_filtrage_justificatifs(etud: Identite, justificatifs: list[Justificatif]): """ - vérifier le filtrage des justificatifs (etat, debut, fin) """ # Vérification du filtrage classique # Etat assert ( scass.filter_justificatifs_by_etat(etud.justificatifs, "valide").count() == 2 ), "Filtrage de l'état 'valide' mauvais" assert ( scass.filter_justificatifs_by_etat(etud.justificatifs, "attente").count() == 1 ), "Filtrage de l'état 'attente' mauvais" assert ( scass.filter_justificatifs_by_etat(etud.justificatifs, "modifie").count() == 1 ), "Filtrage de l'état 'modifie' mauvais" assert ( scass.filter_justificatifs_by_etat(etud.justificatifs, "non_valide").count() == 1 ), "Filtrage de l'état 'non_valide' mauvais" assert ( scass.filter_justificatifs_by_etat(etud.justificatifs, "valide,modifie").count() == 3 ), "Filtrage de l'état 'valide,modifie' mauvais" assert ( scass.filter_justificatifs_by_etat( etud.justificatifs, "valide,modifie,attente" ).count() == 4 ), "Filtrage de l'état 'valide,modifie,attente' mauvais" assert ( scass.filter_justificatifs_by_etat( etud.justificatifs, "valide,modifie,attente,non_valide" ).count() == 5 ), "Filtrage de l'état 'valide,modifie,attente,_non_valide' mauvais" assert ( scass.filter_justificatifs_by_etat(etud.justificatifs, "autre").count() == 0 ), "Filtrage de l'état 'autre' mauvais" # Dates assert ( scass.filter_by_date(etud.justificatifs, Justificatif).count() == 5 ), "Filtrage 'Toute Date' mauvais 1" date = scu.localize_datetime("2022-09-01T10:00+01:00") assert ( scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count() == 5 ), "Filtrage 'Toute Date' mauvais 2" date = scu.localize_datetime("2022-09-03T08:00+01:00") assert ( scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count() == 5 ), "Filtrage 'date début' mauvais 3" date = scu.localize_datetime("2022-09-03T08:00:01+01:00") assert ( scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count() == 5 ), "Filtrage 'date début' mauvais 4" date = scu.localize_datetime("2022-09-03T10:00+01:00") assert ( scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count() == 4 ), "Filtrage 'date début' mauvais 5" date = scu.localize_datetime("2022-09-01T10:00+01:00") assert ( scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count() == 0 ), "Filtrage 'Toute Date' mauvais 6" date = scu.localize_datetime("2022-09-03T08:00+01:00") assert ( scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count() == 1 ), "Filtrage 'date début' mauvais 7" date = scu.localize_datetime("2022-09-03T10:00:01+01:00") assert ( scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count() == 2 ), "Filtrage 'date début' mauvais 8" date = scu.localize_datetime("2023-01-03T12:00+01:00") assert ( scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count() == 5 ), "Filtrage 'date début' mauvais 9" # Justifications des assiduites assert len(scass.justifies(justificatifs[2])) == 2, "Justifications mauvais" assert len(scass.justifies(justificatifs[0])) == 0, "Justifications mauvais" def editer_supprimer_justificatif(etud: Identite): """ Troisième Partie: - Vérification de l'édition des justificatifs - Vérification de la suppression des justificatifs """ justi: Justificatif = etud.justificatifs.first() # Modification de l'état justi.etat = scu.EtatJustificatif.MODIFIE # Modification du moduleimpl justi.date_debut = scu.localize_datetime("2023-02-03T11:00:01+01:00") justi.date_fin = scu.localize_datetime("2023-02-03T12:00:01+01:00") db.session.add(justi) db.session.commit() # Vérification du changement assert ( scass.filter_justificatifs_by_etat(etud.justificatifs, "modifie").count() == 2 ), "Edition de justificatif mauvais" assert ( scass.filter_by_date( etud.justificatifs, Justificatif, date_deb=scu.localize_datetime("2023-02-01T11:00:00+01:00"), ).count() == 1 ), "Edition de justificatif mauvais 2" # Supression d'une assiduité db.session.delete(justi) db.session.commit() assert etud.justificatifs.count() == 4, "Supression de justificatif mauvais" def editer_supprimer_assiduites(etuds: list[Identite], moduleimpls: list[int]): """ Troisième Partie: - Vérification de l'édition des assiduitées - Vérification de la suppression des assiduitées """ ass1: Assiduite = etuds[0].assiduites.first() ass2: Assiduite = etuds[1].assiduites.first() ass3: Assiduite = etuds[2].assiduites.first() # Modification de l'état ass1.etat = scu.EtatAssiduite.RETARD db.session.add(ass1) # Modification du moduleimpl ass2.moduleimpl_id = moduleimpls[0].id db.session.add(ass2) db.session.commit() # Vérification du changement assert ( scass.filter_assiduites_by_etat(etuds[0].assiduites, "retard").count() == 4 ), "Edition d'assiduité mauvais" assert ( scass.filter_by_module_impl(etuds[1].assiduites, moduleimpls[0].id).count() == 2 ), "Edition d'assiduité mauvais" # Supression d'une assiduité db.session.delete(ass3) db.session.commit() assert etuds[2].assiduites.count() == 6, "Supression d'assiduité mauvais" def ajouter_assiduites( etuds: list[Identite], moduleimpls: list[ModuleImpl], etud_faux: Identite ): """ Première partie: - Ajoute 6 assiduités à chaque étudiant - 2 présence (semestre 1 et 2) - 2 retard (semestre 2) - 2 absence (semestre 1) - Vérifie la création des assiduités """ for etud in etuds: obj_assiduites = [ { "etat": scu.EtatAssiduite.PRESENT, "deb": "2022-09-03T08:00+01:00", "fin": "2022-09-03T10:00+01:00", "moduleimpl": None, "desc": None, }, { "etat": scu.EtatAssiduite.PRESENT, "deb": "2023-01-03T08:00+01:00", "fin": "2023-01-03T10:00+01:00", "moduleimpl": moduleimpls[2], "desc": None, }, { "etat": scu.EtatAssiduite.ABSENT, "deb": "2022-09-03T10:00:01+01:00", "fin": "2022-09-03T11:00+01:00", "moduleimpl": moduleimpls[0], "desc": None, }, { "etat": scu.EtatAssiduite.ABSENT, "deb": "2022-09-03T14:00:00+01:00", "fin": "2022-09-03T15:00+01:00", "moduleimpl": moduleimpls[1], "desc": "Description", }, { "etat": scu.EtatAssiduite.RETARD, "deb": "2023-01-03T11:00:01+01:00", "fin": "2023-01-03T12:00+01:00", "moduleimpl": moduleimpls[3], "desc": None, }, { "etat": scu.EtatAssiduite.RETARD, "deb": "2023-01-04T11:00:01+01:00", "fin": "2023-01-04T12:00+01:00", "moduleimpl": moduleimpls[3], "desc": "Description", }, { "etat": scu.EtatAssiduite.RETARD, "deb": "2022-11-04T11:00:01+01:00", "fin": "2022-12-05T12:00+01:00", "moduleimpl": None, "desc": "Description", }, ] assiduites = [ Assiduite.create_assiduite( etud, scu.is_iso_formated(ass["deb"], True), scu.is_iso_formated(ass["fin"], True), ass["etat"], ass["moduleimpl"], ass["desc"], ) for ass in obj_assiduites ] # Vérification de la création des assiduités assert [ ass for ass in assiduites if not isinstance(ass, Assiduite) ] == [], "La création des assiduités de base n'est pas OK" # Vérification de la gestion des erreurs test_assiduite = { "etat": scu.EtatAssiduite.RETARD, "deb": "2023-01-04T11:00:01+01:00", "fin": "2023-01-04T12:00+01:00", "moduleimpl": moduleimpls[3], "desc": "Description", } try: Assiduite.create_assiduite( etuds[0], scu.is_iso_formated(test_assiduite["deb"], True), scu.is_iso_formated(test_assiduite["fin"], True), test_assiduite["etat"], test_assiduite["moduleimpl"], test_assiduite["desc"], ) except ScoValueError as excp: assert ( excp.args[0] == "Duplication des assiduités (la période rentrée rentre en conflit avec une assiduité enregistrée)" ) try: Assiduite.create_assiduite( etud_faux, scu.is_iso_formated(test_assiduite["deb"], True), scu.is_iso_formated(test_assiduite["fin"], True), test_assiduite["etat"], test_assiduite["moduleimpl"], test_assiduite["desc"], ) except ScoValueError as excp: assert excp.args[0] == "L'étudiant n'est pas inscrit au moduleimpl" def verifier_comptage_et_filtrage_assiduites( etuds: list[Identite], moduleimpls: list[int], formsemestres: tuple[int] ): """ Deuxième partie: - vérifier les valeurs du comptage (compte, heure, journée, demi-journée) - vérifier le filtrage des assiduites (etat, debut, fin, module, formsemestre) """ etu1, etu2, etu3 = etuds mod11, mod12, mod21, mod22 = moduleimpls # Vérification du comptage classique comptage = scass.get_assiduites_stats(etu1.assiduites) assert comptage["compte"] == 6 + 1, "la métrique 'Comptage' n'est pas bien calculée" assert ( comptage["journee"] == 3 + 22 ), "la métrique 'Journée' n'est pas bien calculée" assert ( comptage["demi"] == 4 + 43 ), "la métrique 'Demi-Journée' n'est pas bien calculée" assert comptage["heure"] == float( 8 + 169 ), "la métrique 'Heure' n'est pas bien calculée" # Vérification du filtrage classique # Etat assert ( scass.filter_assiduites_by_etat(etu2.assiduites, "present").count() == 2 ), "Filtrage de l'état 'présent' mauvais" assert ( scass.filter_assiduites_by_etat(etu2.assiduites, "retard").count() == 3 ), "Filtrage de l'état 'retard' mauvais" assert ( scass.filter_assiduites_by_etat(etu2.assiduites, "absent").count() == 2 ), "Filtrage de l'état 'absent' mauvais" assert ( scass.filter_assiduites_by_etat(etu2.assiduites, "absent,retard").count() == 5 ), "Filtrage de l'état 'absent,retard' mauvais" assert ( scass.filter_assiduites_by_etat( etu2.assiduites, "absent,retard,present" ).count() == 7 ), "Filtrage de l'état 'absent,retard,present' mauvais" assert ( scass.filter_assiduites_by_etat(etu2.assiduites, "autre").count() == 0 ), "Filtrage de l'état 'autre' mauvais" # Module assert ( scass.filter_by_module_impl(etu3.assiduites, mod11.id).count() == 1 ), "Filtrage par 'Moduleimpl' mauvais" assert ( scass.filter_by_module_impl(etu3.assiduites, mod12.id).count() == 1 ), "Filtrage par 'Moduleimpl' mauvais" assert ( scass.filter_by_module_impl(etu3.assiduites, mod21.id).count() == 1 ), "Filtrage par 'Moduleimpl' mauvais" assert ( scass.filter_by_module_impl(etu3.assiduites, mod22.id).count() == 2 ), "Filtrage par 'Moduleimpl' mauvais" assert ( scass.filter_by_module_impl(etu3.assiduites, None).count() == 2 ), "Filtrage par 'Moduleimpl' mauvais" assert ( scass.filter_by_module_impl(etu3.assiduites, 152).count() == 0 ), "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, formsemestres[0]).count() == 4 ), "Filtrage 'Formsemestre' mauvais" assert ( scass.filter_by_formsemestre(etu1.assiduites, formsemestres[1]).count() == 3 ), "Filtrage 'Formsemestre' mauvais" assert ( scass.filter_by_formsemestre(etu1.assiduites, formsemestres[2]).count() == 0 ), "Filtrage 'Formsemestre' mauvais" # Date début assert ( scass.filter_by_date(etu2.assiduites, Assiduite).count() == 7 ), "Filtrage 'Date début' mauvais 1" date = scu.localize_datetime("2022-09-01T10:00+01:00") assert ( scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 7 ), "Filtrage 'Date début' mauvais 2" date = scu.localize_datetime("2022-09-03T10:00+01:00") assert ( scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 7 ), "Filtrage 'Date début' mauvais 3" date = scu.localize_datetime("2022-09-03T16:00+01:00") assert ( scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 4 ), "Filtrage 'Date début' mauvais 4" # Date Fin date = scu.localize_datetime("2022-09-01T10:00+01:00") assert ( scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 0 ), "Filtrage 'Date fin' mauvais 1" date = scu.localize_datetime("2022-09-03T10:00+01:00") assert ( scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 1 ), "Filtrage 'Date fin' mauvais 2" date = scu.localize_datetime("2022-09-03T10:00:01+01:00") assert ( scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 2 ), "Filtrage 'Date fin' mauvais 3" date = scu.localize_datetime("2022-09-03T16:00+01:00") assert ( scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 3 ), "Filtrage 'Date fin' mauvais 4" date = scu.localize_datetime("2023-01-04T16:00+01:00") assert ( scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 7 ), "Filtrage 'Date fin' mauvais 5"