# -*- 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 )
"""
import pytest

import app.scodoc.sco_assiduites as scass
import app.scodoc.sco_utils as scu
from app import db, log
from app.models import (
    Assiduite,
    FormSemestre,
    Identite,
    Justificatif,
    ModuleImpl,
    Absence,
)
from app.models.assiduites import compute_assiduites_justified

from app.scodoc.sco_exceptions import ScoValueError
from tests.unit import sco_fake_gen
from tools import downgrade_module, migrate_abs_to_assiduites

import datetime as dt


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 = FormSemestre.get_formsemestre(formsemestre_id_1)
    formsemestre_2 = FormSemestre.get_formsemestre(formsemestre_id_2)
    formsemestre_3 = 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 de 3 étudiants
    etud_0 = g_fake.create_etud(prenom="etud0")
    etud_1 = g_fake.create_etud(prenom="etud1")
    etud_2 = g_fake.create_etud(prenom="etud2")
    etuds_dict = [etud_0, etud_1, etud_2]
    # etuds_dict = [g_fake.create_etud(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["etudid"]).first())

    assert None not in etuds, "Problème avec la conversion en Identite"

    # Etudiant faux

    etud_faux_dict = g_fake.create_etud(prenom="etudfaux")
    etud_faux = Identite.query.filter_by(id=etud_faux_dict["etudid"]).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)

    essais_cache(etuds[0].etudid, (formsemestre_1, formsemestre_2), moduleimpls)

    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, justifiee in [
        (
            "02/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,
            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)
    ]:
        _create_abs(
            date_debut=debut,
            date_fin=fin,
            demijournee=demijournee,
            etudid=etudid,
            estjust=justifiee,
        )

    # --- Justification de certaines absences

    for debut, fin, demijournee in [
        (
            "15/02/2023",
            "15/02/2023",
            2,
        ),
        (
            "21/02/2023",
            "24/02/2023",
            2,
        ),
    ]:
        _create_abs(
            date_debut=debut,
            date_fin=fin,
            demijournee=demijournee,
            etudid=etudid,
            estjust=True,
            estabs=False,
        )

    migrate_abs_to_assiduites()

    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"

    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, sems: tuple[FormSemestre], moduleimpls: list[ModuleImpl]):
    """Vérification des fonctionnalités du cache"""

    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
    )
    assiduites_count_cache = scass.get_assiduites_count_in_interval(
        etudid, date_deb, date_fin
    )

    assert (
        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):
    """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 = []
    for just in obj_justificatifs:
        just_obj = Justificatif.create_justificatif(
            etud,
            scu.is_iso_formated(just["deb"], True),
            scu.is_iso_formated(just["fin"], True),
            just["etat"],
            just["raison"],
        )
        db.session.add(just_obj)
        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)
    ] == [], "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])) == 1, "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 = []
        for ass in obj_assiduites:
            ass_obj = Assiduite.create_assiduite(
                etud,
                scu.is_iso_formated(ass["deb"], True),
                scu.is_iso_formated(ass["fin"], True),
                ass["etat"],
                ass["moduleimpl"],
                ass["desc"],
            )
            assiduites.append(ass_obj)
            db.session.add(ass_obj)
            db.session.commit()

        # 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: la période rentre en conflit avec une plage 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 module"


def verifier_comptage_et_filtrage_assiduites(
    etuds: list[Identite], moduleimpls: list[int], formsemestres: tuple[FormSemestre]
):
    """
    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
    assert (
        scass.filter_by_formsemestre(
            etu1.assiduites, Assiduite, formsemestres[0]
        ).count()
        == 4
    ), "Filtrage 'Formsemestre' mauvais"
    assert (
        scass.filter_by_formsemestre(
            etu1.assiduites, Assiduite, formsemestres[1]
        ).count()
        == 3
    ), "Filtrage 'Formsemestre' mauvais"
    assert (
        scass.filter_by_formsemestre(
            etu1.assiduites, Assiduite, 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"


def _create_abs(
    date_debut, date_fin, demijournee, estjust=False, etudid=False, estabs=True
):
    etud = Identite.from_request(etudid)
    deb: dt.date = dt.datetime.strptime(date_debut, "%d/%m/%Y").date()
    fin: dt.date = dt.datetime.strptime(date_fin, "%d/%m/%Y").date()
    abs_list: list[Absence] = []
    while deb < fin:
        if deb.weekday() in [5, 6]:
            deb += dt.timedelta(days=1)
            continue
        if demijournee == 2:
            abs_list.append(
                Absence(
                    etudid=etud.id,
                    jour=deb.isoformat(),
                    estabs=estabs,
                    estjust=estjust,
                    matin=True,
                )
            )
            abs_list.append(
                Absence(
                    etudid=etud.id,
                    jour=deb.isoformat(),
                    estabs=estabs,
                    estjust=estjust,
                    matin=False,
                )
            )
        else:
            abs_list.append(
                Absence(
                    etudid=etud.id,
                    jour=deb.isoformat(),
                    estabs=estabs,
                    estjust=estjust,
                    matin=demijournee,
                )
            )
        log(
            f"create_abs [{etudid}, {deb.isoformat()}, {estabs}, {estjust}, {['aprem', 'matin', 'journee'][demijournee]}]"
        )
        deb += dt.timedelta(days=1)

    if deb == fin and deb.weekday() not in [5, 6]:
        if demijournee == 2:
            abs_list.append(
                Absence(
                    etudid=etud.id,
                    jour=deb.isoformat(),
                    estabs=estabs,
                    estjust=estjust,
                    matin=True,
                )
            )
            abs_list.append(
                Absence(
                    etudid=etud.id,
                    jour=deb.isoformat(),
                    estabs=estabs,
                    estjust=estjust,
                    matin=False,
                )
            )
        else:
            abs_list.append(
                Absence(
                    etudid=etud.id,
                    jour=deb.isoformat(),
                    estabs=estabs,
                    estjust=estjust,
                    matin=demijournee,
                )
            )
        log(
            f"create_abs [{etudid}, {deb.isoformat()}, {estabs}, {estjust}, {['aprem', 'matin', 'journee'][demijournee]}]"
        )

    db.session.add_all(abs_list)
    db.session.commit()