From 37e7667eebb94bb04fb57d71747817a2099107fd Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet <emmanuel.viennet@gmail.com>
Date: Sun, 1 Aug 2021 00:05:53 +0300
Subject: [PATCH] doc et tests unitaires des absences

---
 app/scodoc/sco_abs.py                      |  68 +++-
 app/scodoc/sco_abs_views.py                |  21 +-
 app/views/absences.py                      |   2 +-
 scotests/test_AnnuleAbsencesDatesNoJust.py |  13 +-
 scotests/test_absence2.py                  |   2 +-
 tests/unit/test_abs_counts.py              | 248 ++++++------
 tests/unit/test_abs_demijournee.py         | 437 ++++++++++++++++-----
 tests/unit/test_caches.py                  |   4 +-
 8 files changed, 547 insertions(+), 248 deletions(-)

diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py
index ee3455e4..8e866285 100644
--- a/app/scodoc/sco_abs.py
+++ b/app/scodoc/sco_abs.py
@@ -306,11 +306,14 @@ def list_abs_in_range(etudid, debut, fin, matin=None, moduleimpl_id=None, cursor
     """Liste des absences entre deux dates.
 
     Args:
-        etudid
-        debut   string iso date ("2020-03-12")
-        end     string iso date ("2020-03-12")
-        matin   None, True, False
-        moduleimpl_id
+        etudid:
+        debut:   string iso date ("2020-03-12")
+        end:     string iso date ("2020-03-12")
+        matin:   None, True, False
+        moduleimpl_id: restreint le comptage aux absences dans ce module
+
+    Returns:
+        List of absences
     """
     if matin != None:
         matin = _toboolean(matin)
@@ -347,9 +350,15 @@ WHERE A.ETUDID = %(etudid)s
     return res
 
 
-def count_abs(etudid, debut, fin, matin=None, moduleimpl_id=None):
-    """CountAbs
-    matin= 1 ou 0.
+def count_abs(etudid, debut, fin, matin=None, moduleimpl_id=None) -> int:
+    """compte le nombre d'absences
+
+    Args:
+        etudid: l'étudiant considéré
+        debut: date, chaîne iso, eg "2021-06-15"
+        fin: date de fin, incluse
+        matin: True (compte les matinées), False (les après-midi), None (les deux)
+        moduleimpl_id: restreint le comptage aux absences dans ce module.
 
     Returns:
         An integer.
@@ -359,8 +368,19 @@ def count_abs(etudid, debut, fin, matin=None, moduleimpl_id=None):
     )
 
 
-def count_abs_just(etudid, debut, fin, matin=None, moduleimpl_id=None):
-    "Count just. abs"
+def count_abs_just(etudid, debut, fin, matin=None, moduleimpl_id=None) -> int:
+    """compte le nombre d'absences justifiées
+
+    Args:
+        etudid: l'étudiant considéré
+        debut: date, chaîne iso, eg "2021-06-15"
+        fin: date de fin, incluse
+        matin: True (compte les matinées), False (les après-midi), None (les deux)
+        moduleimpl_id: restreint le comptage aux absences dans ce module.
+
+    Returns:
+        An integer.
+    """
     if matin != None:
         matin = _toboolean(matin)
         ismatin = " AND A.MATIN = %(matin)s "
@@ -569,9 +589,20 @@ ORDER BY A.JOUR
 
 
 def list_abs_justifs(etudid, datedebut, datefin=None, only_no_abs=False):
-    """Liste des justificatifs (sans absence relevée) à partir d'une date,
+    """Liste des justificatifs (avec ou sans absence relevée) à partir d'une date,
     ou, si datefin spécifié, entre deux dates.
-    Si only_no_abs: seulement les justificatifs correspondant aux jours sans absences relevées.
+
+    Args:
+        etudid:
+        datedebut: date de début, iso, eg "2002-03-15"
+        datefin: date de fin, incluse, eg "2002-03-15"
+        only_no_abs: si vrai, seulement les justificatifs correspondant
+        aux jours sans absences relevées.
+    Returns:
+        Liste de dict absences
+        {'etudid': 'EID214', 'jour': datetime.date(2021, 1, 15),
+         'matin': True, 'description': ''
+        }
     """
     cnx = ndb.GetDBConnexion()
     cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
@@ -664,9 +695,16 @@ def _add_abslist(context, abslist, REQUEST, moduleimpl_id=None):
             add_absence(context, etudid, jour, matin, 0, REQUEST, "", moduleimpl_id)
 
 
-def annule_absence(context, etudid, jour, matin, moduleimpl_id=None, REQUEST=None):
-    """Annule une absence ds base
-    Si moduleimpl_id, n'annule que pour ce module
+def annule_absence(context, etudid, jour, matin, moduleimpl_id=None):
+    """Annule une absence dans la base. N'efface pas l'éventuel justificatif.
+    Args:
+        etudid:
+        jour: date, chaîne iso, eg "1999-12-31"
+        matin:
+        moduleimpl_id: si spécifié, n'annule que pour ce module.
+
+    Returns:
+        None
     """
     # unpublished
     matin = _toboolean(matin)
diff --git a/app/scodoc/sco_abs_views.py b/app/scodoc/sco_abs_views.py
index 06ea8e9d..efd0e2c5 100644
--- a/app/scodoc/sco_abs_views.py
+++ b/app/scodoc/sco_abs_views.py
@@ -433,11 +433,11 @@ def doAnnuleAbsence(
     demijournee = int(demijournee)
     for jour in dates:
         if demijournee == 2:
-            sco_abs.annule_absence(context, etudid, jour, False, REQUEST=REQUEST)
-            sco_abs.annule_absence(context, etudid, jour, True, REQUEST=REQUEST)
+            sco_abs.annule_absence(context, etudid, jour, False)
+            sco_abs.annule_absence(context, etudid, jour, True)
             nbadded += 2
         else:
-            sco_abs.annule_absence(context, etudid, jour, demijournee, REQUEST=REQUEST)
+            sco_abs.annule_absence(context, etudid, jour, demijournee)
             nbadded += 1
     #
     H = [
@@ -608,9 +608,16 @@ autre absence pour <b>%(nomprenom)s</b></a></li>
     return "\n".join(H)
 
 
-def AnnuleAbsencesDatesNoJust(context, etudid, dates, moduleimpl_id=None, REQUEST=None):
-    """Supprime les absences aux dates indiquées
-    mais ne supprime pas les justificatifs.
+def AnnuleAbsencesDatesNoJust(context, etudid, dates, moduleimpl_id=None):
+    """Supprime les absences non justifiées aux dates indiquées
+    Ne supprime pas les justificatifs éventuels.
+    Args:
+        etudid: l'étudiant
+        dates: liste de dates iso, eg [ "2000-01-15", "2000-01-16" ]
+        moduleimpl_id: si spécifié, n'affecte que les absences de ce module
+
+    Returns:
+        None
     """
     # log('AnnuleAbsencesDatesNoJust: moduleimpl_id=%s' % moduleimpl_id)
     if not dates:
@@ -626,7 +633,7 @@ def AnnuleAbsencesDatesNoJust(context, etudid, dates, moduleimpl_id=None, REQUES
                 matin = 0
             else:
                 raise ValueError("invalid ampm !")
-            sco_abs.annule_absence(context, etudid, jour, matin, moduleimpl_id, REQUEST)
+            sco_abs.annule_absence(context, etudid, jour, matin, moduleimpl_id)
         return
     cnx = ndb.GetDBConnexion()
     cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
diff --git a/app/views/absences.py b/app/views/absences.py
index de3ce5ec..509f4989 100644
--- a/app/views/absences.py
+++ b/app/views/absences.py
@@ -295,7 +295,7 @@ def doSignaleAbsenceGrSemestre(
     if dates:
         for etudid in etudids:
             sco_abs_views.AnnuleAbsencesDatesNoJust(
-                context, etudid, dates, moduleimpl_id, REQUEST
+                context, etudid, dates, moduleimpl_id
             )
         return "Absences effacées"
 
diff --git a/scotests/test_AnnuleAbsencesDatesNoJust.py b/scotests/test_AnnuleAbsencesDatesNoJust.py
index dce5c1b5..7fa93365 100644
--- a/scotests/test_AnnuleAbsencesDatesNoJust.py
+++ b/scotests/test_AnnuleAbsencesDatesNoJust.py
@@ -45,13 +45,20 @@ mi = G.create_moduleimpl(
 # --- Création d'un étudiant
 etud = G.create_etud(code_nip=None)
 G.inscrit_etudiant(sem, etud)
-etudid=etud["etudid"]
+etudid = etud["etudid"]
 
 # --- Création d'une absence
-sco_abs_views.doSignaleAbsence(context.Absences, datedebut="22/01/2021", datefin="22/01/2021", demijournee=2, etudid=etudid, REQUEST=REQUEST)
+sco_abs_views.doSignaleAbsence(
+    context.Absences,
+    datedebut="22/01/2021",
+    datefin="22/01/2021",
+    demijournee=2,
+    etudid=etudid,
+    REQUEST=REQUEST,
+)
 
 # --- Annulation d'absence
-context.Absences.AnnuleAbsencesDatesNoJust(etudid, dates="22/01/2021", REQUEST=REQUEST)
+context.Absences.AnnuleAbsencesDatesNoJust(etudid, dates="22/01/2021")
 
 """ Erreur :   File "/opt/scodoc/Products/ScoDoc/ZAbsences.py", line 323, in AnnuleAbsencesDatesNoJust
     vars(),
diff --git a/scotests/test_absence2.py b/scotests/test_absence2.py
index 0fbeb5fd..e8f91922 100644
--- a/scotests/test_absence2.py
+++ b/scotests/test_absence2.py
@@ -236,7 +236,7 @@ assert len(li_range) == 5
 
 # --- Annulation d'absence
 
-# context.Absences.AnnuleAbsencesDatesNoJust(etudid, dates="22/01/2021", REQUEST=REQUEST)
+# context.Absences.AnnuleAbsencesDatesNoJust(etudid, dates="22/01/2021")
 
 
 # --- Fonction renvoyant du code HTML
diff --git a/tests/unit/test_abs_counts.py b/tests/unit/test_abs_counts.py
index 05949395..3a442675 100644
--- a/tests/unit/test_abs_counts.py
+++ b/tests/unit/test_abs_counts.py
@@ -1,126 +1,130 @@
-import random
-
-# La variable context est définie par le script de lancement
-# l'affecte ainsi pour évietr les warnins pylint:
-context = context  # pylint: disable=undefined-variable
-REQUEST = REQUEST  # pylint: disable=undefined-variable
-import scotests.sco_fake_gen as sco_fake_gen  # pylint: disable=import-error
-import sco_abs
-import sco_abs_views
-import sco_abs_notification
-import ZAbsences
-
-G = sco_fake_gen.ScoFake(context.Notes)
-G.verbose = False
-
-# --- Création d'étudiants
-etud = G.create_etud(code_nip=None)
-
-# --- Création d'une formation
-f = G.create_formation(acronyme="")
-ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
-mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
-mod = G.create_module(
-    matiere_id=mat["matiere_id"],
-    code="TSM1",
-    coefficient=1.0,
-    titre="module test",
-    ue_id=ue["ue_id"],  # faiblesse de l'API
-    formation_id=f["formation_id"],  # faiblesse de l'API
-)
-
-# --- Mise place d'un semestre
-sem = G.create_formsemestre(
-    formation_id=f["formation_id"],
-    semestre_id=1,
-    date_debut="01/01/2021",
-    date_fin="30/06/2021",
-)
-
-mi = G.create_moduleimpl(
-    module_id=mod["module_id"],
-    formsemestre_id=sem["formsemestre_id"],
-    responsable_id="bach",
-)
-
-# --- Inscription des étudiants
-G.inscrit_etudiant(sem, etud)
-
-# --- Saisie absences
-etudid = etud["etudid"]
-
-for debut, fin, demijournee in [
-    ("15/01/2021", "15/01/2021", 1),
-    ("18/01/2021", "18/01/2021", 0),
-    ("19/01/2021", "19/01/2021", 2),
-    ("22/01/2021", "22/01/2021", 1),
-]:
-    sco_abs_views.doSignaleAbsence(
-        context.Absences,
-        datedebut=debut,
-        datefin=fin,
-        demijournee=demijournee,
-        etudid=etudid,
-        REQUEST=REQUEST,
-    )
-
-
-# --- Justification de certaines absences
-
-for debut, fin, demijournee in [
-    ("15/01/2021", "15/01/2021", 1),
-    ("18/01/2021", "18/01/2021", 0),
-    ("19/01/2021", "19/01/2021", 2),
-]:
-    sco_abs_views.doJustifAbsence(
-        context.Absences,
-        datedebut=debut,
-        datefin=fin,
-        demijournee=demijournee,
-        etudid=etudid,
-        REQUEST=REQUEST,
-    )
-
-
-# --- Utilisation de get_abs_count() de sco_abs
-
-nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
-
-
-# --- Utilisation de CountAbs() de ZAbsences
-
-nb_abs2 = context.Absences.CountAbs(etudid=etudid, debut="01/01/2021", fin="06/30/2021")
-nb_absj2 = context.Absences.CountAbsJust(
-    etudid=etudid, debut="01/01/2021", fin="06/30/2021"
-)
-
-assert nbabs == nb_abs2 == 5
-assert nbabsjust == nb_absj2 == 4
-
-# --- Suppression d'absence
-
-_ = sco_abs_views.doAnnuleAbsence(
-    context.Absences, "19/01/2021", "19/01/2021", 2, etudid=etudid, REQUEST=REQUEST
-)
-
-# --- Vérification
-
-new_nbabs, _ = sco_abs.get_abs_count(etudid, sem)
-new_nbabs2 = context.Absences.CountAbs(
-    etudid=etudid, debut="01/01/2021", fin="06/30/2021"
-)
-
-print(new_nbabs)
-print(new_nbabs2)
-
-assert new_nbabs == new_nbabs2
-
+# -*- mode: python -*-
+# -*- coding: utf-8 -*-
 
 """
-Commentaire : 
+Comptage des absences
+"""
+# test écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en juillet 2021
 
-CountAbs de ZAbsence ----> OK
-CountAbs de sco_abs_view ----> bug
+import json
 
-Peut être du au fait que la fonction getAbsSemEtud ne se met pas à jour par rapport aux absences supprimés.
-"""
\ No newline at end of file
+from tests.unit import sco_fake_gen
+
+from app.scodoc import sco_abs
+from app.scodoc import sco_abs_views
+from app.scodoc import sco_groups
+from app.views import absences
+
+context = None  # #context
+
+
+def test_abs_counts(test_client):
+    """Comptage des absences"""
+    G = sco_fake_gen.ScoFake(verbose=False)
+
+    # --- Création d'étudiants
+    etud = G.create_etud(code_nip=None)
+
+    # --- Création d'une formation
+    f = G.create_formation(acronyme="")
+    ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
+    mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
+    mod = G.create_module(
+        matiere_id=mat["matiere_id"],
+        code="TSM1",
+        coefficient=1.0,
+        titre="module test",
+        ue_id=ue["ue_id"],  # faiblesse de l'API
+        formation_id=f["formation_id"],  # faiblesse de l'API
+    )
+
+    # --- Mise place d'un semestre
+    sem = G.create_formsemestre(
+        formation_id=f["formation_id"],
+        semestre_id=1,
+        date_debut="01/01/2021",
+        date_fin="30/06/2021",
+    )
+
+    mi = G.create_moduleimpl(
+        module_id=mod["module_id"],
+        formsemestre_id=sem["formsemestre_id"],
+        responsable_id="bach",
+    )
+
+    # --- Inscription des étudiants
+    G.inscrit_etudiant(sem, etud)
+
+    # --- Saisie absences
+    etudid = etud["etudid"]
+
+    for debut, fin, demijournee in [
+        ("01/01/2020", "31/01/2020", 2),  # hors semestre
+        ("15/01/2021", "15/01/2021", 1),
+        ("18/01/2021", "18/01/2021", 0),
+        ("19/01/2021", "19/01/2021", 2),
+        ("22/01/2021", "22/01/2021", 1),
+        ("30/06/2021", "30/06/2021", 2),  # dernier jour
+    ]:
+        sco_abs_views.doSignaleAbsence(
+            context,
+            datedebut=debut,
+            datefin=fin,
+            demijournee=demijournee,
+            etudid=etudid,
+        )
+
+    # --- Justification de certaines absences
+
+    for debut, fin, demijournee in [
+        ("15/01/2021", "15/01/2021", 1),
+        ("18/01/2021", "18/01/2021", 0),
+        ("19/01/2021", "19/01/2021", 2),
+    ]:
+        sco_abs_views.doJustifAbsence(
+            context,
+            datedebut=debut,
+            datefin=fin,
+            demijournee=demijournee,
+            etudid=etudid,
+        )
+
+    # --- Utilisation de get_abs_count() de sco_abs
+
+    nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
+
+    # --- Utilisation de sco_abs.count_abs()
+
+    nb_abs2 = sco_abs.count_abs(etudid=etudid, debut="2021-01-01", fin="2021-06-30")
+    nb_absj2 = sco_abs.count_abs_just(
+        etudid=etudid, debut="2021-01-01", fin="2021-06-30"
+    )
+
+    assert nbabs == nb_abs2 == 7
+    assert nbabsjust == nb_absj2 == 4
+
+    # --- Nombre de justificatifs:
+    justifs = sco_abs.list_abs_justifs(etudid, "2021-01-01", datefin="2021-06-30")
+    assert len(justifs) == 4
+
+    # --- Suppression d'absence
+    _ = sco_abs_views.doAnnuleAbsence(
+        context, "19/01/2021", "19/01/2021", 2, etudid=etudid
+    )
+
+    # --- Vérification
+    justifs_2 = sco_abs.list_abs_justifs(etudid, "2021-01-01", datefin="2021-06-30")
+    assert len(justifs_2) == len(justifs)
+    new_nbabs, _ = sco_abs.get_abs_count(etudid, sem)  # version cachée
+    new_nbabs2 = sco_abs.count_abs(etudid=etudid, debut="2021-01-01", fin="2021-06-30")
+
+    assert new_nbabs == new_nbabs2
+    assert new_nbabs == (nbabs - 2)  # on a supprimé deux absences
+
+    # --- annulation absence sans supprimer le justificatif
+    sco_abs_views.AnnuleAbsencesDatesNoJust(context, etudid, ["2021-01-15"])
+    nbabs_3, nbjust_3 = sco_abs.get_abs_count(etudid, sem)
+    assert nbabs_3 == new_nbabs
+    justifs_3 = sco_abs.list_abs_justifs(etudid, "2021-01-01", datefin="2021-06-30")
+    assert len(justifs_3) == len(justifs_2)
+    # XXX à continuer
\ No newline at end of file
diff --git a/tests/unit/test_abs_demijournee.py b/tests/unit/test_abs_demijournee.py
index a66a11be..db3a1550 100644
--- a/tests/unit/test_abs_demijournee.py
+++ b/tests/unit/test_abs_demijournee.py
@@ -1,119 +1,360 @@
-""" Parametre demijournee ne fonctionne pas lorsque demijournee = 2
+# -*- mode: python -*-
+# -*- coding: utf-8 -*-
+
+"""
 Créer et justifier des absences en utilisant le parametre demijournee
 """
-import random
+# test écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en juillet 2021
 
-# La variable context est définie par le script de lancement
-# l'affecte ainsi pour évietr les warnins pylint:
-context = context  # pylint: disable=undefined-variable
-REQUEST = REQUEST  # pylint: disable=undefined-variable
-import scotests.sco_fake_gen as sco_fake_gen  # pylint: disable=import-error
-import sco_abs
-import sco_abs_views
-import sco_abs_notification
+import json
 
-G = sco_fake_gen.ScoFake(context.Notes)
-G.verbose = False
+from tests.unit import sco_fake_gen
 
-# --- Création d'étudiants
-etud = G.create_etud(code_nip=None)
+from app.scodoc import sco_abs
+from app.scodoc import sco_abs_views
+from app.scodoc import sco_groups
+from app.views import absences
 
-# --- Création d'une formation
-f = G.create_formation(acronyme="")
-ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
-mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
-mod = G.create_module(
-    matiere_id=mat["matiere_id"],
-    code="TSM1",
-    coefficient=1.0,
-    titre="module test",
-    ue_id=ue["ue_id"],  # faiblesse de l'API
-    formation_id=f["formation_id"],  # faiblesse de l'API
-)
-
-# --- Mise place d'un semestre
-sem = G.create_formsemestre(
-    formation_id=f["formation_id"],
-    semestre_id=1,
-    date_debut="01/01/2021",
-    date_fin="30/06/2021",
-)
-
-mi = G.create_moduleimpl(
-    module_id=mod["module_id"],
-    formsemestre_id=sem["formsemestre_id"],
-    responsable_id="bach",
-)
-
-# --- Inscription des étudiants
-G.inscrit_etudiant(sem, etud)
+context = None  # #context
 
 
-# --- Saisie absences
-etudid = etud["etudid"]
+def test_abs_demijournee(test_client):
+    """Opération élémentaires sur les absences, tests demi-journées
+    Travaille dans base TEST00 (defaut)
+    """
+    G = sco_fake_gen.ScoFake(verbose=False)
 
-_ = sco_abs_views.doSignaleAbsence(
-    context.Absences,
-    "15/01/2021",
-    "15/01/2021",
-    demijournee=2,
-    etudid=etudid,
-    REQUEST=REQUEST,
-)
+    # --- Création d'étudiants
+    etud = G.create_etud(code_nip=None)
+
+    # --- Création d'une formation
+    f = G.create_formation(acronyme="")
+    ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
+    mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
+    mod = G.create_module(
+        matiere_id=mat["matiere_id"],
+        code="TSM1",
+        coefficient=1.0,
+        titre="module test",
+        ue_id=ue["ue_id"],  # faiblesse de l'API
+        formation_id=f["formation_id"],  # faiblesse de l'API
+    )
+
+    # --- Mise place d'un semestre
+    sem = G.create_formsemestre(
+        formation_id=f["formation_id"],
+        semestre_id=1,
+        date_debut="01/01/2021",
+        date_fin="30/06/2021",
+    )
+
+    mi = G.create_moduleimpl(
+        module_id=mod["module_id"],
+        formsemestre_id=sem["formsemestre_id"],
+        responsable_id="bach",
+    )
+
+    # --- Inscription des étudiants
+    G.inscrit_etudiant(sem, etud)
+
+    # --- Saisie absences
+    etudid = etud["etudid"]
+
+    _ = sco_abs_views.doSignaleAbsence(
+        context,
+        "15/01/2021",
+        "15/01/2021",
+        demijournee=2,
+        etudid=etudid,
+    )
+
+    _ = sco_abs_views.doSignaleAbsence(
+        context,
+        "18/01/2021",
+        "18/01/2021",
+        demijournee=1,
+        etudid=etudid,
+    )
+
+    _ = sco_abs_views.doSignaleAbsence(
+        context,
+        "19/01/2021",
+        "19/01/2021",
+        demijournee=0,
+        etudid=etudid,
+    )
+
+    # --- Justification de certaines absences
+
+    _ = sco_abs_views.doJustifAbsence(
+        context,
+        "18/01/2021",
+        "18/01/2021",
+        demijournee=1,
+        etudid=etudid,
+    )
+
+    _ = sco_abs_views.doJustifAbsence(
+        context,
+        "19/01/2021",
+        "19/01/2021",
+        demijournee=2,
+        etudid=etudid,
+    )
+
+    # NE JUSTIFIE QUE LE MATIN MALGRES LE PARAMETRE demijournee = 2
+
+    # --- Test
+
+    nbabs, nbabs_just = sco_abs.get_abs_count(etudid, sem)
+    assert (
+        nbabs == 4
+    )  # l'étudiant a été absent le 15 journée compléte (2 abs : 1 matin, 1 apres midi) et le 18 (1 matin), et le 19 (1 apres midi).
+    assert nbabs_just == 2  # Justifie abs du matin + abs après midi
 
 
-_ = sco_abs_views.doSignaleAbsence(
-    context.Absences,
-    "18/01/2021",
-    "18/01/2021",
-    demijournee=1,
-    etudid=etudid,
-    REQUEST=REQUEST,
-)
+def test_abs_basic(test_client):
+    """creation de 10 étudiants, formation, semestre, ue, module, absences le matin, l'apres midi, la journée compléte
+    et justification d'absences, supression d'absences, création d'une liste etat absences, creation d'un groupe afin
+    de tester la fonction EtatAbsencesGroupes
 
-_ = sco_abs_views.doSignaleAbsence(
-    context.Absences,
-    "19/01/2021",
-    "19/01/2021",
-    demijournee=0,
-    etudid=etudid,
-    REQUEST=REQUEST,
-)
+    Fonctions de l'API utilisé :
+     - doSignaleAbsence
+     - doAnnuleAbsence
+     - doJustifAbsence
+     - get_partition_groups
+     - get_partitions_list
+     - sco_abs.get_abs_count(etudid, sem)
+     - ListeAbsEtud
+     - partition_create
+     - createGroup
+     - set_group
+     - EtatAbsenceGr
+     - AddBilletAbsence
+     - listeBilletsEtud
+    """
+    G = sco_fake_gen.ScoFake(verbose=False)
 
-# --- Justification de certaines absences
+    # --- Création d'étudiants
+    etuds = [G.create_etud(code_nip=None) for _ in range(10)]
 
-_ = sco_abs_views.doJustifAbsence(
-    context.Absences,
-    "18/01/2021",
-    "18/01/2021",
-    demijournee=1,
-    etudid=etudid,
-    REQUEST=REQUEST,
-)
+    # --- Création d'une formation
+    f = G.create_formation(acronyme="")
+    ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
+    mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
+    mod = G.create_module(
+        matiere_id=mat["matiere_id"],
+        code="TSM1",
+        coefficient=1.0,
+        titre="module test",
+        ue_id=ue["ue_id"],  # faiblesse de l'API
+        formation_id=f["formation_id"],  # faiblesse de l'API
+    )
 
-_ = sco_abs_views.doJustifAbsence(
-    context.Absences,
-    "19/01/2021",
-    "19/01/2021",
-    demijournee=2,
-    etudid=etudid,
-    REQUEST=REQUEST,
-)
+    # --- Mise place d'un semestre
+    sem = G.create_formsemestre(
+        formation_id=f["formation_id"],
+        semestre_id=1,
+        date_debut="01/01/2021",
+        date_fin="30/06/2021",
+    )
 
-# NE JUSTIFIE QUE LE MATIN MALGRES LE PARAMETRE demijournee = 2
+    mi = G.create_moduleimpl(
+        module_id=mod["module_id"],
+        formsemestre_id=sem["formsemestre_id"],
+        responsable_id="bach",
+    )
 
-# --- Test
+    # --- Inscription des étudiants
+    for etud in etuds:
+        G.inscrit_etudiant(sem, etud)
 
-nbabs, nbabs_just = sco_abs.get_abs_count(etudid, sem)
-assert (
-    nbabs == 4
-)  # l'étudiant a été absent le 15 journée compléte (2 abs : 1 matin, 1 apres midi) et le 18 (1 matin), et le 19 (1 apres midi).
-assert nbabs_just == 2  # Justifie abs du matin + abs après midi
+    # --- Création d'une évaluation
+    e = G.create_evaluation(
+        moduleimpl_id=mi["moduleimpl_id"],
+        jour="22/01/2021",
+        description="evaluation test",
+        coefficient=1.0,
+    )
 
-""" 
-Commentaire :
+    # --- Saisie absences
+    etudid = etuds[0]["etudid"]
 
-Pb : le 2 ne peut pas être pris en tant que int car string dans la fonction
------> Pb reglé
+    _ = sco_abs_views.doSignaleAbsence(
+        context,
+        "15/01/2021",
+        "15/01/2021",
+        demijournee=1,
+        etudid=etudid,
+    )
 
-"""
\ No newline at end of file
+    _ = sco_abs_views.doSignaleAbsence(
+        context,
+        "18/01/2021",
+        "18/01/2021",
+        demijournee=0,
+        etudid=etudid,
+    )
+
+    _ = sco_abs_views.doSignaleAbsence(
+        context,
+        "19/01/2021",
+        "19/01/2021",
+        demijournee=2,
+        etudid=etudid,
+    )
+
+    _ = sco_abs_views.doSignaleAbsence(
+        context,
+        "22/01/2021",
+        "22/01/2021",
+        demijournee=1,
+        etudid=etudid,
+    )
+
+    # --- Justification de certaines absences
+
+    _ = sco_abs_views.doJustifAbsence(
+        context,
+        "15/01/2021",
+        "15/01/2021",
+        demijournee=1,
+        etudid=etudid,
+    )
+
+    _ = sco_abs_views.doJustifAbsence(
+        context,
+        "18/01/2021",
+        "18/01/2021",
+        demijournee=0,
+        etudid=etudid,
+    )
+
+    _ = sco_abs_views.doJustifAbsence(
+        context,
+        "19/01/2021",
+        "19/01/2021",
+        demijournee=2,
+        etudid=etudid,
+    )
+
+    # --- Test
+
+    b = sco_abs.is_work_saturday(context)
+    assert b == 0  # samedi ne sont pas compris
+    nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
+    assert (
+        nbabs == 5
+    )  # l'étudiant a été absent le 15 (apres midi) , (16 et 17 we), 18 (matin) et 19 janvier (matin et apres midi), et 22 (matin)
+    assert nbabsjust == 4  # l'étudiant justifie ses abs du 15, 18 et 19
+
+    # + vérification à l'aide de ScoDoc WEB : ok!
+
+    # --- Supression d'une absence et d'une justification
+
+    _ = sco_abs_views.doAnnuleAbsence(
+        context, "19/01/2021", "19/01/2021", 2, etudid=etudid
+    )
+    nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
+    print(nbabs)  # lors du print cela affiche 5 or cela devrait afficher 3
+
+    # assert nbabs ==  3
+    # + vérification à l'aide de ScoDoc WEB : il y a bien plus que 3 abs, 2 justifiés et 1 non justifié.
+
+    # --- supression d'une justification pas encore disponible à l'aide de python.
+
+    # --- Création d'une liste d'abs
+
+    liste_abs = sco_abs_views.ListeAbsEtud(
+        context, etudid, format="json", absjust_only=1
+    )
+    liste_abs2 = sco_abs_views.ListeAbsEtud(context, etudid, format="json")
+
+    load_liste_abs = json.loads(liste_abs)
+    load_liste_abs2 = json.loads(liste_abs2)
+
+    assert len(load_liste_abs2) == 1
+    assert len(load_liste_abs) == 2
+    assert load_liste_abs2[0]["ampm"] == "1"
+    assert load_liste_abs2[0]["datedmy"] == "22/01/2021"
+    assert load_liste_abs2[0]["exams"] == mod["code"]
+    # absjust_only -> seulement les abs justifiés
+
+    # --- Création d'un groupe
+
+    _ = sco_groups.partition_create(
+        context,
+        formsemestre_id=sem["formsemestre_id"],
+        partition_name="Eleve",
+    )
+    li1 = sco_groups.get_partitions_list(context, sem["formsemestre_id"])
+    _ = sco_groups.createGroup(context, li1[0]["partition_id"], "Groupe 1")
+
+    # --- Affectation des élèves dans des groupes
+
+    li_grp1 = sco_groups.get_partition_groups(context, li1[0])
+    for etud in etuds:
+        sco_groups.set_group(context, etud["etudid"], li_grp1[0]["group_id"])
+
+    # --- Test de EtatAbsencesGroupes
+
+    grp1_abs = absences.EtatAbsencesGr(
+        context,
+        group_ids=[li_grp1[0]["group_id"]],
+        debut="01/01/2021",
+        fin="30/06/2021",
+        format="json",
+    )
+    # grp1_abs est une Response car on a appelé une vue (1er appel)
+    load_grp1_abs = json.loads(grp1_abs.get_data().decode("utf-8"))
+
+    assert len(load_grp1_abs) == 10
+
+    tab_id = []  # tab des id present dans load_grp1_abs
+    for un_etud in load_grp1_abs:
+        tab_id.append(un_etud["etudid"])
+
+    for (
+        etud
+    ) in (
+        etuds
+    ):  # verification si tous les etudiants sont present dans la liste du groupe d'absence
+        assert etud["etudid"] in tab_id
+
+    for un_etud in load_grp1_abs:
+        if un_etud["etudid"] == etudid:
+            assert un_etud["nbabs"] == "3"
+            assert un_etud["nbjustifs_noabs"] == "2"
+            assert un_etud["nbabsjust"] == "2"
+            assert un_etud["nbabsnonjust"] == "1"
+            assert un_etud["nomprenom"] == etuds[0]["nomprenom"]
+
+    # --- Création de billets
+
+    b1 = absences.AddBilletAbsence(
+        context,
+        begin="2021-01-22 00:00",
+        end="2021-01-22 23:59",
+        etudid=etudid,
+        description="abs du 22",
+        justified=False,
+        code_nip=etuds[0]["code_nip"],
+        code_ine=etuds[0]["code_ine"],
+    )
+
+    b2 = absences.AddBilletAbsence(
+        context,
+        begin="2021-01-15 00:00",
+        end="2021-01-15 23:59",
+        etudid=etudid,
+        description="abs du 15",
+        code_nip=etuds[0]["code_nip"],
+        code_ine=etuds[0]["code_ine"],
+    )
+
+    li_bi = absences.listeBilletsEtud(context, etudid=etudid, format="json")
+    assert isinstance(li_bi, str)
+    load_li_bi = json.loads(li_bi)
+
+    assert len(load_li_bi) == 2
+    assert load_li_bi[1]["description"] == "abs du 22"
diff --git a/tests/unit/test_caches.py b/tests/unit/test_caches.py
index d416ee3d..a0598b5a 100644
--- a/tests/unit/test_caches.py
+++ b/tests/unit/test_caches.py
@@ -22,7 +22,9 @@ context = None  # #context
 
 
 def test_notes_table(test_client):
-    """Test construction et cache de NotesTable"""
+    """Test construction et cache de NotesTable.
+    Attention: utilise une base (departement) existante.
+    """
     ndb.set_sco_dept(DEPT)
     assert g.scodoc_dept == DEPT
     sems = sco_formsemestre.do_formsemestre_list(context)