diff --git a/README.md b/README.md
index 115ef229d80deeda244f79c51bd31fed0e9725fb..3cfbf39de69b319c698be3bd7bc8175547a53118 100644
--- a/README.md
+++ b/README.md
@@ -101,7 +101,7 @@ Certains tests ont besoin d'un département déjà créé, qui n'est pas créé
 scripts de tests:
 Lancer au préalable:
 
-    flask delete-dept TEST00 && flask create-dept TEST00
+    flask delete-dept -fy TEST00 && flask create-dept TEST00
 
 Puis dérouler les tests unitaires:
 
diff --git a/app/__init__.py b/app/__init__.py
index 428ba16417a859c0140614d49491421625f8fe6b..4431ff6913def5d0d94b6b23bae01f8868e8c014 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -435,8 +435,6 @@ def initialize_scodoc_database(erase=False, create_all=False):
     SQL tables and functions.
     If erase is True, _erase_ all database content.
     """
-    from app import models
-
     # - ERASE (the truncation sql function has been defined above)
     if erase:
         truncate_database()
diff --git a/app/scodoc/sco_bulletins_json.py b/app/scodoc/sco_bulletins_json.py
index 5caba24a6ed226200e4aecc9e08177bb4db272d5..fb0be065d0ca04fc6918c1c8f8dd5bfdff20c845 100644
--- a/app/scodoc/sco_bulletins_json.py
+++ b/app/scodoc/sco_bulletins_json.py
@@ -43,6 +43,7 @@ import app.scodoc.sco_utils as scu
 import app.scodoc.notesdb as ndb
 from app.scodoc import sco_abs
 from app.scodoc import sco_edit_ue
+from app.scodoc import sco_evaluations
 from app.scodoc import sco_evaluation_db
 from app.scodoc import sco_formsemestre
 from app.scodoc import sco_groups
@@ -84,13 +85,14 @@ def formsemestre_bulletinetud_published_dict(
     xml_nodate=False,
     xml_with_decisions=False,  # inclue les decisions même si non publiées
     version="long",
-):
+) -> dict:
     """Dictionnaire representant les informations _publiees_ du bulletin de notes
     Utilisé pour JSON, devrait l'être aussi pour XML. (todo)
     """
     from app.scodoc import sco_bulletins
 
     formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
+    prefs = sco_preferences.SemPreferences(formsemestre_id)
     etud = Identite.query.get(etudid)
     sem = sco_formsemestre.get_formsemestre(formsemestre_id)
 
@@ -158,11 +160,8 @@ def formsemestre_bulletinetud_published_dict(
     ues = nt.get_ues_stat_dict()
     modimpls = nt.get_modimpls_dict()
     nbetuds = len(nt.etud_moy_gen_ranks)
-    mg = scu.fmt_note(nt.get_etud_moy_gen(etudid))
-    if (
-        nt.get_moduleimpls_attente()
-        or sco_preferences.get_preference("bul_show_rangs", formsemestre_id) == 0
-    ):
+    moy_gen = scu.fmt_note(nt.get_etud_moy_gen(etudid))
+    if nt.get_moduleimpls_attente() or not prefs["bul_show_rangs"]:
         # n'affiche pas le rang sur le bulletin s'il y a des
         # notes en attente dans ce semestre
         rang = ""
@@ -175,7 +174,7 @@ def formsemestre_bulletinetud_published_dict(
         )
 
     d["note"] = dict(
-        value=mg,
+        value=moy_gen,
         min=scu.fmt_note(nt.moy_min),
         max=scu.fmt_note(nt.moy_max),
         moy=scu.fmt_note(nt.moy_moy),
@@ -217,9 +216,7 @@ def formsemestre_bulletinetud_published_dict(
                 value=scu.fmt_note(ue_status["cur_moy_ue"] if ue_status else ""),
                 min=scu.fmt_note(ue["min"]),
                 max=scu.fmt_note(ue["max"]),
-                moy=scu.fmt_note(
-                    ue["moy"]
-                ),  # CM : ajout pour faire apparaitre la moyenne des UE
+                moy=scu.fmt_note(ue["moy"]),
             ),
             rang=rang,
             effectif=effectif,
@@ -259,10 +256,7 @@ def formsemestre_bulletinetud_published_dict(
                 m["note"][k] = scu.fmt_note(m["note"][k])
 
             u["module"].append(m)
-            if (
-                sco_preferences.get_preference("bul_show_mod_rangs", formsemestre_id)
-                and nt.mod_rangs is not None
-            ):
+            if prefs["bul_show_mod_rangs"] and nt.mod_rangs is not None:
                 m["rang"] = dict(
                     value=nt.mod_rangs[modimpl["moduleimpl_id"]][0][etudid]
                 )
@@ -274,33 +268,40 @@ def formsemestre_bulletinetud_published_dict(
             if version != "short":
                 for e in evals:
                     if e["visibulletin"] or version == "long":
-                        val = e["notes"].get(etudid, {"value": "NP"})[
-                            "value"
-                        ]  # NA si etud demissionnaire
+                        val = e["notes"].get(etudid, {"value": "NP"})["value"]
+                        # nb: val est NA si etud démissionnaire
                         val = scu.fmt_note(val, note_max=e["note_max"])
-                        m["evaluation"].append(
-                            dict(
-                                jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
-                                heure_debut=ndb.TimetoISO8601(
-                                    e["heure_debut"], null_is_empty=True
-                                ),
-                                heure_fin=ndb.TimetoISO8601(
-                                    e["heure_fin"], null_is_empty=True
-                                ),
-                                coefficient=e["coefficient"],
-                                evaluation_type=e["evaluation_type"],
-                                evaluation_id=e[
-                                    "evaluation_id"
-                                ],  # CM : ajout pour permettre de faire le lien sur les bulletins en ligne avec l'évaluation
-                                description=quote_xml_attr(e["description"]),
-                                note=val,
-                            )
+                        eval_dict = dict(
+                            jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
+                            heure_debut=ndb.TimetoISO8601(
+                                e["heure_debut"], null_is_empty=True
+                            ),
+                            heure_fin=ndb.TimetoISO8601(
+                                e["heure_fin"], null_is_empty=True
+                            ),
+                            coefficient=e["coefficient"],
+                            evaluation_type=e["evaluation_type"],
+                            # CM : ajout pour permettre de faire le lien sur
+                            # les bulletins en ligne avec l'évaluation:
+                            evaluation_id=e["evaluation_id"],
+                            description=quote_xml_attr(e["description"]),
+                            note=val,
                         )
+                        if prefs["bul_show_minmax_eval"] or prefs["bul_show_moypromo"]:
+                            etat = sco_evaluations.do_evaluation_etat(
+                                e["evaluation_id"]
+                            )
+                            if prefs["bul_show_minmax_eval"]:
+                                eval_dict["min"] = scu.fmt_note(etat["mini"])
+                                eval_dict["max"] = scu.fmt_note(etat["maxi"])
+                            if prefs["bul_show_moypromo"]:
+                                eval_dict["moy"] = scu.fmt_note(etat["moy"])
+
+                        m["evaluation"].append(eval_dict)
+
                 # Evaluations incomplètes ou futures:
                 complete_eval_ids = set([e["evaluation_id"] for e in evals])
-                if sco_preferences.get_preference(
-                    "bul_show_all_evals", formsemestre_id
-                ):
+                if prefs["bul_show_all_evals"]:
                     all_evals = sco_evaluation_db.do_evaluation_list(
                         args={"moduleimpl_id": modimpl["moduleimpl_id"]}
                     )
@@ -344,7 +345,7 @@ def formsemestre_bulletinetud_published_dict(
             )
 
     # --- Absences
-    if sco_preferences.get_preference("bul_show_abs", formsemestre_id):
+    if prefs["bul_show_abs"]:
         nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
         d["absences"] = dict(nbabs=nbabs, nbabsjust=nbabsjust)
 
@@ -403,17 +404,14 @@ def dict_decision_jury(
     """
     from app.scodoc import sco_bulletins
 
+    prefs = sco_preferences.SemPreferences(formsemestre.id)
+
     d = {}
-    if (
-        sco_preferences.get_preference("bul_show_decision", formsemestre.id)
-        or with_decisions
-    ):
+    if prefs["bul_show_decision"] or with_decisions:
         infos, dpv = sco_bulletins.etud_descr_situation_semestre(
             etud.id,
             formsemestre.id,
-            show_uevalid=sco_preferences.get_preference(
-                "bul_show_uevalid", formsemestre.id
-            ),
+            show_uevalid=prefs["bul_show_uevalid"],
         )
         d["situation"] = infos["situation"]
         if dpv:
diff --git a/app/scodoc/sco_bulletins_standard.py b/app/scodoc/sco_bulletins_standard.py
index 968bbdd9d8827434fcbc68868df0a9bc71c459c3..b326ef7b895d179021e669c7c314f93b822dda87 100644
--- a/app/scodoc/sco_bulletins_standard.py
+++ b/app/scodoc/sco_bulletins_standard.py
@@ -46,11 +46,12 @@ de la forme %(XXX)s sont remplacées par la valeur de XXX, pour XXX dans:
 Balises img: actuellement interdites.
 
 """
+from flask import url_for, g
+
 from reportlab.platypus import KeepTogether, Paragraph, Spacer, Table
 from reportlab.lib.units import cm, mm
 from reportlab.lib.colors import Color, blue
 
-
 import app.scodoc.sco_utils as scu
 from app.scodoc.sco_pdf import SU, make_paras
 from app.scodoc import sco_preferences
@@ -434,7 +435,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
                     plusminus = pluslink
                 try:
                     ects_txt = str(int(ue["ects"]))
-                except:
+                except (ValueError, KeyError):
                     ects_txt = "-"
 
                 t = {
@@ -602,12 +603,14 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
                 "rang": mod["mod_rang_txt"],  # vide si pas option rang
                 "note": mod["mod_moy_txt"],
                 "coef": mod["mod_coef_txt"] if prefs["bul_show_coef"] else "",
-                "abs": mod.get(
-                    "mod_abs_txt", ""
-                ),  # absent si pas option show abs module
-                "_css_row_class": "notes_bulletin_row_mod%s" % rowstyle,
-                "_titre_target": "moduleimpl_status?moduleimpl_id=%s"
-                % mod["moduleimpl_id"],
+                # vide si pas option show abs module:
+                "abs": mod.get("mod_abs_txt", ""),
+                "_css_row_class": f"notes_bulletin_row_mod{rowstyle}",
+                "_titre_target": url_for(
+                    "notes.moduleimpl_status",
+                    scodoc_dept=g.scodoc_dept,
+                    moduleimpl_id=mod["moduleimpl_id"],
+                ),
                 "_titre_help": mod["mod_descr_txt"],
                 "_hidden": hidden,
                 "_pdf_style": pdf_style,
diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py
index 0223c7b6389f0b9cc4a5bf492fc18c4d2012644b..92378db76e5bff120be985ccea281d5f4a64b651 100644
--- a/app/scodoc/sco_preferences.py
+++ b/app/scodoc/sco_preferences.py
@@ -2034,7 +2034,10 @@ class BasePreferences(object):
         if modif:
             sco_cache.invalidate_formsemestre()
 
-    def set(self, formsemestre_id, name, value):
+    def set(self, formsemestre_id: int, name: str, value: str):
+        """Set and save a preference value.
+        If formsemestre_id is None, global pref.
+        """
         if not name or name[0] == "_" or name not in self.prefs_name:
             raise ValueError(f"invalid preference name: {name}")
         if formsemestre_id and name in self.prefs_only_global:
diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py
index 5e114677a8ba65825c2915a75b77e11adae46bc7..4e33d5e77e780eb2073487937a47e5ebd5fbc5a8 100644
--- a/app/scodoc/sco_saisie_notes.py
+++ b/app/scodoc/sco_saisie_notes.py
@@ -39,7 +39,7 @@ from flask_login import current_user
 from app.comp import res_sem
 from app.comp.res_compat import NotesTableCompat
 from app.models import Evaluation, FormSemestre
-from app.models import ScolarNews
+from app.models import ModuleImpl, ScolarNews
 from app.models.etudiants import Identite
 import app.scodoc.sco_utils as scu
 from app.scodoc.sco_utils import ModuleType
@@ -216,7 +216,8 @@ def do_evaluation_upload_xls():
             eval_id = None
         if eval_id != evaluation_id:
             diag.append(
-                f"Erreur: fichier invalide: le code d'évaluation de correspond pas ! ('{eval_id_str}' != '{evaluation_id}')"
+                f"""Erreur: fichier invalide: le code d'évaluation de correspond pas ! ('{
+                    eval_id_str}' != '{evaluation_id}')"""
             )
             raise InvalidNoteValue()
         # --- get notes -> list (etudid, value)
@@ -238,15 +239,14 @@ def do_evaluation_upload_xls():
                 ni += 1
         except:
             diag.append(
-                'Erreur: Ligne invalide ! (erreur ligne %d)<br>"%s"'
-                % (ni, str(lines[ni]))
+                f"""Erreur: Ligne invalide ! (erreur ligne {ni})<br>{lines[ni]}"""
             )
             raise InvalidNoteValue()
         # -- check values
         L, invalids, withoutnotes, absents, _ = _check_notes(notes, E, M["module"])
         if len(invalids):
             diag.append(
-                "Erreur: la feuille contient %d notes invalides</p>" % len(invalids)
+                f"Erreur: la feuille contient {len(invalids)} notes invalides</p>"
             )
             if len(invalids) < 25:
                 etudsnames = [
@@ -424,25 +424,34 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
             evaluation_id, by_uid=current_user.id
         )
     else:
-        raise AccessDenied("Modification des notes impossible pour %s" % current_user)
+        raise AccessDenied(f"Modification des notes impossible pour {current_user}")
 
     notes = [(etudid, scu.NOTES_SUPPRESS) for etudid in notes_db.keys()]
 
+    status_url = url_for(
+        "notes.moduleimpl_status",
+        scodoc_dept=g.scodoc_dept,
+        moduleimpl_id=E["moduleimpl_id"],
+    )
+
     if not dialog_confirmed:
         nb_changed, nb_suppress, existing_decisions = notes_add(
             current_user, evaluation_id, notes, do_it=False, check_inscription=False
         )
-        msg = (
-            "<p>Confirmer la suppression des %d notes ? <em>(peut affecter plusieurs groupes)</em></p>"
-            % nb_suppress
-        )
+        msg = f"""<p>Confirmer la suppression des {nb_suppress} notes ?
+        <em>(peut affecter plusieurs groupes)</em>
+        </p>
+        """
+
         if existing_decisions:
-            msg += """<p class="warning">Important: il y a déjà des décisions de jury enregistrées, qui seront potentiellement à revoir suite à cette modification !</p>"""
+            msg += """<p class="warning">Important: il y a déjà des décisions de
+             jury enregistrées, qui seront potentiellement à revoir suite à
+             cette modification !</p>"""
         return scu.confirm_dialog(
             msg,
             dest_url="",
             OK="Supprimer les notes",
-            cancel_url="moduleimpl_status?moduleimpl_id=%s" % E["moduleimpl_id"],
+            cancel_url=status_url,
             parameters={"evaluation_id": evaluation_id},
         )
 
@@ -455,26 +464,28 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
         check_inscription=False,
     )
     assert nb_changed == nb_suppress
-    H = ["<p>%s notes supprimées</p>" % nb_suppress]
+    H = [f"""<p>{nb_suppress} notes supprimées</p>"""]
     if existing_decisions:
         H.append(
-            """<p class="warning">Important: il y avait déjà des décisions de jury enregistrées, qui sont potentiellement à revoir suite à cette modification !</p>"""
+            """<p class="warning">Important: il y avait déjà des décisions
+            de jury enregistrées, qui sont potentiellement à revoir suite
+            à cette modification !
+            </p>"""
         )
     H += [
-        '<p><a class="stdlink" href="moduleimpl_status?moduleimpl_id=%s">continuer</a>'
-        % E["moduleimpl_id"]
+        f"""<p><a class="stdlink" href="{status_url}">continuer</a>
+        """
     ]
     # news
-    M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
-    mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
-    mod["moduleimpl_id"] = M["moduleimpl_id"]
-    mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
+    modimpl = ModuleImpl.query.get(E["moduleimpl_id"])
     ScolarNews.add(
         typ=ScolarNews.NEWS_NOTE,
-        obj=M["moduleimpl_id"],
-        text='Suppression des notes d\'une évaluation dans <a href="%(url)s">%(titre)s</a>'
-        % mod,
-        url=mod["url"],
+        obj=modimpl.id,
+        text=f"""Suppression des notes d'une évaluation dans
+        <a class="stdlink" href="{status_url}"
+        >{modimpl.module.titre or 'module sans titre'}</a>
+        """,
+        url=status_url,
     )
 
     return html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer()
@@ -555,7 +566,7 @@ def notes_add(
                 oldval = notes_db[etudid]["value"]
                 if type(value) != type(oldval):
                     changed = True
-                elif type(value) == type(1.0) and (
+                elif type(value) == float and (
                     abs(value - oldval) > scu.NOTES_PRECISION
                 ):
                     changed = True
@@ -566,10 +577,10 @@ def notes_add(
                     if do_it:
                         cursor.execute(
                             """INSERT INTO notes_notes_log
-                                (etudid,evaluation_id,value,comment,date,uid) 
+                                (etudid,evaluation_id,value,comment,date,uid)
                             SELECT etudid, evaluation_id, value, comment, date, uid
                             FROM notes_notes
-                            WHERE etudid=%(etudid)s 
+                            WHERE etudid=%(etudid)s
                             and evaluation_id=%(evaluation_id)s
                             """,
                             {"etudid": etudid, "evaluation_id": evaluation_id},
@@ -588,7 +599,7 @@ def notes_add(
                             cursor.execute(
                                 """UPDATE notes_notes
                                 SET value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s
-                                WHERE etudid = %(etudid)s 
+                                WHERE etudid = %(etudid)s
                                 and evaluation_id = %(evaluation_id)s
                                 """,
                                 aa,
@@ -609,7 +620,7 @@ def notes_add(
                             # garde trace de la suppression dans l'historique:
                             aa["value"] = scu.NOTES_SUPPRESS
                             cursor.execute(
-                                """INSERT INTO notes_notes_log (etudid,evaluation_id,value,comment,date,uid) 
+                                """INSERT INTO notes_notes_log (etudid,evaluation_id,value,comment,date,uid)
                                 VALUES (%(etudid)s, %(evaluation_id)s, %(value)s, %(comment)s, %(date)s, %(uid)s)
                                 """,
                                 aa,
diff --git a/app/views/notes.py b/app/views/notes.py
index 9b2c5dca0b72ca944d697a1ef45b0352d2e8a247..34067a8df5ce36fb8c39623e7bb2a3659f1263ee 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -651,7 +651,9 @@ def index_html():
     </ul>
     <h3>Référentiels de compétences</h3>
     <ul>
-    <li><a class="stdlink" href="{url_for('notes.refcomp_table', scodoc_dept=g.scodoc_dept)}">Liste des référentiels chargés</a>
+    <li><a class="stdlink" href="{
+        url_for('notes.refcomp_table', scodoc_dept=g.scodoc_dept)
+    }">Liste des référentiels chargés</a>
     </li>
     </ul>
         
diff --git a/scodoc.py b/scodoc.py
index e8839f7800941155f16f27c8ba06eba91dd4554b..52c4930228096122e586562f1515a99862d409bb 100755
--- a/scodoc.py
+++ b/scodoc.py
@@ -349,6 +349,7 @@ def abort_if_false(ctx, param, value):
 
 @app.cli.command()
 @click.option(
+    "-y",
     "--yes",
     is_flag=True,
     callback=abort_if_false,
diff --git a/tests/conftest.py b/tests/conftest.py
index f164da1658057fd168b1493dfc438293616d8832..eb1a494fd8aee866b68f72de4bf276aa36d4e6e8 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -38,7 +38,7 @@ def test_client():
                     u.add_role(admin_role, TestConfig.DEPT_TEST)
                 db.session.add(u)
                 db.session.commit()
-                # Creation département de Test
+                # Création département de Test
                 d = models.Departement(acronym=TestConfig.DEPT_TEST)
                 db.session.add(d)
                 db.session.commit()
diff --git a/tests/unit/sco_fake_gen.py b/tests/unit/sco_fake_gen.py
index 26c244023291df087b7d2a09c90f41a4689576e2..7e03f152c898bf9ab315cc04d2f943aea74bcc08 100644
--- a/tests/unit/sco_fake_gen.py
+++ b/tests/unit/sco_fake_gen.py
@@ -38,14 +38,28 @@ from app.scodoc import sco_utils as scu
 from app import log
 from app.scodoc.sco_exceptions import ScoValueError
 
+from tests.unit.setup import NOTES_T
+
 random.seed(12345)  # tests reproductibles
 
 
 NOMS_DIR = Config.SCODOC_DIR + "/tools/fakeportal/nomsprenoms"
-NOMS = [x.strip() for x in open(NOMS_DIR + "/noms.txt").readlines()]
-PRENOMS_H = [x.strip() for x in open(NOMS_DIR + "/prenoms-h.txt").readlines()]
-PRENOMS_F = [x.strip() for x in open(NOMS_DIR + "/prenoms-f.txt").readlines()]
-PRENOMS_X = [x.strip() for x in open(NOMS_DIR + "/prenoms-x.txt").readlines()]
+NOMS = [
+    x.strip()
+    for x in open(NOMS_DIR + "/noms.txt", encoding=scu.SCO_ENCODING).readlines()
+]
+PRENOMS_H = [
+    x.strip()
+    for x in open(NOMS_DIR + "/prenoms-h.txt", encoding=scu.SCO_ENCODING).readlines()
+]
+PRENOMS_F = [
+    x.strip()
+    for x in open(NOMS_DIR + "/prenoms-f.txt", encoding=scu.SCO_ENCODING).readlines()
+]
+PRENOMS_X = [
+    x.strip()
+    for x in open(NOMS_DIR + "/prenoms-x.txt", encoding=scu.SCO_ENCODING).readlines()
+]
 
 
 def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
@@ -297,18 +311,18 @@ class ScoFake(object):
     @logging_meth
     def create_note(
         self,
-        evaluation=None,
-        etud=None,
+        evaluation_id: int = None,
+        etudid: int = None,
         note=None,
-        comment=None,
-        user=None,  # User instance
+        comment: str = None,
+        user: User = None,  # User instance
     ):
         if user is None:
             user = self.default_user
         return sco_saisie_notes.notes_add(
             user,
-            evaluation["evaluation_id"],
-            [(etud["etudid"], note)],
+            evaluation_id,
+            [(etudid, note)],
             comment=comment,
         )
 
@@ -393,24 +407,24 @@ class ScoFake(object):
                     eval_list.append(e)
         return formsemestre_id, eval_list
 
-    def set_etud_notes_sem(
-        self, sem, eval_list, etuds, notes=None, random_min=0, random_max=20
+    def set_etud_notes_evals(
+        self, eval_list: list[dict], etuds: list[dict], notes=None
     ):
         """Met des notes aux étudiants indiqués des evals indiquées.
 
         Args:
-            sem: dict
-            eval_list: list of dicts
-            etuds: list of dicts
             notes: liste des notes (float).
-            Si non spécifié, tire au hasard dans `[random_min, random_max]`
+            Si non spécifié, utilise la liste NOTES_T
         """
-        set_random = notes is None
+        if notes is None:
+            notes = NOTES_T
         for e in eval_list:
-            if set_random:
-                notes = [float(random.randint(random_min, random_max)) for _ in etuds]
-            for etud, note in zip(etuds, notes):
-                self.create_note(evaluation=e, etud=etud, note=note)
+            for idx, etud in enumerate(etuds):
+                self.create_note(
+                    evaluation_id=e["id"],
+                    etudid=etud["id"],
+                    note=notes[idx % len(notes)],
+                )
 
     def set_code_jury(
         self,
diff --git a/tests/unit/setup.py b/tests/unit/setup.py
index f9798f19956334c49290fc4c96592efcceb7a5db..de7f26436cef4e1d9ba5ec7359a62082b9a0f32b 100644
--- a/tests/unit/setup.py
+++ b/tests/unit/setup.py
@@ -3,12 +3,16 @@ Quelques fonctions d'initialisation pour tests unitaires
 """
 
 from tests.unit import sco_fake_gen
-from app import db
 from app import models
 
 import app.scodoc.sco_utils as scu
 from app.scodoc import sco_codes_parcours
 
+# Valeurs des notes saisies par les tests:
+NOTES_T = [
+    float(x) for x in (20, 0, 10, 13 / 7.0, 12.5, 24.0 / 11) + tuple(range(1, 19))
+]
+
 
 def build_formation_test(
     nb_mods=1, parcours=sco_codes_parcours.ParcoursBUT, with_ue_sport=False
diff --git a/tests/unit/test_bulletin.py b/tests/unit/test_bulletin.py
new file mode 100644
index 0000000000000000000000000000000000000000..68a050d79bd3fc886d423ab91663c32b0720add9
--- /dev/null
+++ b/tests/unit/test_bulletin.py
@@ -0,0 +1,95 @@
+"""Tests unitaires : bulletins de notes
+
+Utiliser comme: 
+    pytest tests/unit/test_sco_basic.py
+
+Au besoin, créer un base de test neuve:
+    ./tools/create_database.sh SCODOC_TEST
+
+"""
+
+from app.models import FormSemestre, Identite
+
+from config import TestConfig
+
+import app
+from app.scodoc import sco_bulletins_json
+from app.scodoc import sco_preferences
+from tests.unit import sco_fake_gen
+from tests.unit import test_sco_basic
+
+
+DEPT = TestConfig.DEPT_TEST
+
+
+def test_bulletin(test_client):
+    """Vérifications sur les bulletins de notes"""
+    G = sco_fake_gen.ScoFake(verbose=False)
+    app.set_sco_dept(DEPT)
+    formsemestre = test_sco_basic.run_sco_basic()
+    modimpl = formsemestre.modimpls.first()
+    evaluation = modimpl.evaluations.first()
+    # S'assure qu'on a bien une formation classique:
+    assert formsemestre.formation.is_apc() is False
+    etud: Identite = formsemestre.etuds.first()
+    assert etud
+    # Ici on a un modimpl avec 9 inscrits, 2 evals ayant toutes leurs notes
+    # Vérification des min/max évaluation sur le bulletin
+    bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
+        formsemestre.id,
+        etud.id,
+        force_publishing=True,
+        xml_with_decisions=True,
+    )
+    assert isinstance(bul, dict)
+    assert bul["type"] == "classic"
+    modules_res = bul["ue"][0]["module"]
+    assert len(modules_res) == 1  # 1 seul module complet
+    module_res = modules_res[0]
+    assert modimpl.module.code == module_res["code"]
+    assert len(module_res["evaluation"]) == 2
+    note_eval_1 = module_res["evaluation"][0]
+    assert "note" in note_eval_1
+    assert "min" not in note_eval_1
+    assert not sco_preferences.get_preference("bul_show_minmax_eval")
+    # Change préférence pour avoir min/max évaluation
+    prefs = sco_preferences.get_base_preferences()
+    prefs.set(None, "bul_show_minmax_eval", True)
+    assert sco_preferences.get_preference("bul_show_minmax_eval")
+    # Redemande le bulletin
+    bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
+        formsemestre.id,
+        etud.id,
+        force_publishing=True,
+        xml_with_decisions=True,
+    )
+    note_eval_1 = bul["ue"][0]["module"][0]["evaluation"][0]
+    assert "min" in note_eval_1
+    assert "max" in note_eval_1
+    min_eval_1 = float(note_eval_1["min"])
+    max_eval_1 = float(note_eval_1["max"])
+    # la valeur actuelle est 12.34, on s'assure qu'elle n'est pas extrême:
+    assert min_eval_1 > 0
+    assert max_eval_1 < 20
+
+    # Saisie note pour changer min/max:
+    # Met le max à 20:
+    G.create_note(evaluation_id=evaluation.id, etudid=etud.id, note=20.0)
+    bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
+        formsemestre.id,
+        etud.id,
+        force_publishing=True,
+        xml_with_decisions=True,
+    )
+    note_eval_1 = bul["ue"][0]["module"][0]["evaluation"][0]
+    assert note_eval_1["max"] == "20.00"
+    # Met le min à zero:
+    G.create_note(evaluation_id=evaluation.id, etudid=etud.id, note=0.0)
+    bul = sco_bulletins_json.formsemestre_bulletinetud_published_dict(
+        formsemestre.id,
+        etud.id,
+        force_publishing=True,
+        xml_with_decisions=True,
+    )
+    note_eval_1 = bul["ue"][0]["module"][0]["evaluation"][0]
+    assert note_eval_1["min"] == "00.00"
diff --git a/tests/unit/test_departements.py b/tests/unit/test_departements.py
index e8ca47f60128b51ce19b2bdfde0e63a740f0182b..9f99b30e5e20f952d5eabec2ca4af1331042a6b3 100644
--- a/tests/unit/test_departements.py
+++ b/tests/unit/test_departements.py
@@ -11,7 +11,6 @@ from flask import g
 import app
 from app import db
 from app.models import Departement, ScoPreference, FormSemestre, formsemestre
-from app.scodoc import notesdb as ndb
 from app.scodoc import sco_formsemestre
 from app.scodoc import sco_preferences
 from tests.unit import test_sco_basic
diff --git a/tests/unit/test_notes_modules.py b/tests/unit/test_notes_modules.py
index 98f6f64049439d8c1207fc714087f7bc62a2f9fe..c04a6c777dc664d160a22e52957bde5f985acce6 100644
--- a/tests/unit/test_notes_modules.py
+++ b/tests/unit/test_notes_modules.py
@@ -108,10 +108,14 @@ def test_notes_modules(test_client):
     # --- Notes ordinaires
     note_1 = 12.0
     note_2 = 13.0
-    _, _, _ = G.create_note(evaluation=e1, etud=etuds[0], note=note_1)
-    _, _, _ = G.create_note(evaluation=e2, etud=etuds[0], note=note_2)
-    _, _, _ = G.create_note(evaluation=e1, etud=etuds[1], note=note_1 / 2)
-    _, _, _ = G.create_note(evaluation=e2, etud=etuds[1], note=note_2 / 3)
+    _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etuds[0]["id"], note=note_1)
+    _, _, _ = G.create_note(evaluation_id=e2["id"], etudid=etuds[0]["id"], note=note_2)
+    _, _, _ = G.create_note(
+        evaluation_id=e1["id"], etudid=etuds[1]["id"], note=note_1 / 2
+    )
+    _, _, _ = G.create_note(
+        evaluation_id=e2["id"], etudid=etuds[1]["id"], note=note_2 / 3
+    )
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
@@ -132,16 +136,16 @@ def test_notes_modules(test_client):
     )
 
     # Absence à une évaluation
-    _, _, _ = G.create_note(evaluation=e1, etud=etud, note=None)  # abs
-    _, _, _ = G.create_note(evaluation=e2, etud=etud, note=note_2)
+    _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=None)  # abs
+    _, _, _ = G.create_note(evaluation_id=e2["id"], etudid=etudid, note=note_2)
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
     note_th = (coef_1 * 0.0 + coef_2 * note_2) / (coef_1 + coef_2)
     assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(note_th)
     # Absences aux deux évaluations
-    _, _, _ = G.create_note(evaluation=e1, etud=etud, note=None)  # abs
-    _, _, _ = G.create_note(evaluation=e2, etud=etud, note=None)  # abs
+    _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=None)  # abs
+    _, _, _ = G.create_note(evaluation_id=e2["id"], etudid=etudid, note=None)  # abs
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
@@ -156,8 +160,10 @@ def test_notes_modules(test_client):
     )
 
     # Note excusée EXC <-> scu.NOTES_NEUTRALISE
-    _, _, _ = G.create_note(evaluation=e1, etud=etud, note=note_1)
-    _, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_NEUTRALISE)  # EXC
+    _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=note_1)
+    _, _, _ = G.create_note(
+        evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+    )  # EXC
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
@@ -171,8 +177,10 @@ def test_notes_modules(test_client):
         expected_moy_ue=note_1,
     )
     # Note en attente ATT <-> scu.NOTES_ATTENTE
-    _, _, _ = G.create_note(evaluation=e1, etud=etud, note=note_1)
-    _, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_ATTENTE)  # ATT
+    _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=note_1)
+    _, _, _ = G.create_note(
+        evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_ATTENTE
+    )  # ATT
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
@@ -186,8 +194,12 @@ def test_notes_modules(test_client):
         expected_moy_ue=note_1,
     )
     # Neutralisation (EXC) des 2 évals
-    _, _, _ = G.create_note(evaluation=e1, etud=etud, note=scu.NOTES_NEUTRALISE)  # EXC
-    _, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_NEUTRALISE)  # EXC
+    _, _, _ = G.create_note(
+        evaluation_id=e1["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+    )  # EXC
+    _, _, _ = G.create_note(
+        evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+    )  # EXC
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
@@ -201,8 +213,12 @@ def test_notes_modules(test_client):
         expected_moy_ue=np.nan,
     )
     # Attente (ATT) sur les 2 evals
-    _, _, _ = G.create_note(evaluation=e1, etud=etud, note=scu.NOTES_ATTENTE)  # ATT
-    _, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_ATTENTE)  # ATT
+    _, _, _ = G.create_note(
+        evaluation_id=e1["id"], etudid=etudid, note=scu.NOTES_ATTENTE
+    )  # ATT
+    _, _, _ = G.create_note(
+        evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_ATTENTE
+    )  # ATT
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
@@ -261,7 +277,7 @@ def test_notes_modules(test_client):
         {"etudid": etudid, "moduleimpl_id": moduleimpl_id},
         formsemestre_id=formsemestre_id,
     )
-    _, _, _ = G.create_note(evaluation=e1, etud=etud, note=12.5)
+    _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etudid, note=12.5)
 
     nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
     mod_stats = nt.get_mod_stats(moduleimpl_id)
@@ -289,7 +305,7 @@ def test_notes_modules(test_client):
         description="evaluation mod 2",
         coefficient=1.0,
     )
-    _, _, _ = G.create_note(evaluation=e_m2, etud=etud, note=19.5)
+    _, _, _ = G.create_note(evaluation_id=e_m2["id"], etudid=etudid, note=19.5)
     formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
     nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
     ue_status = nt.get_etud_ue_status(etudid, ue_id)
@@ -297,12 +313,16 @@ def test_notes_modules(test_client):
     # Moyenne d'UE si l'un des modules est EXC ("NA")
     # 2 modules, notes EXC dans le premier, note valide n dans le second
     # la moyenne de l'UE doit être n
-    _, _, _ = G.create_note(evaluation=e1, etud=etud, note=scu.NOTES_NEUTRALISE)  # EXC
-    _, _, _ = G.create_note(evaluation=e2, etud=etud, note=scu.NOTES_NEUTRALISE)  # EXC
-    _, _, _ = G.create_note(evaluation=e_m2, etud=etud, note=12.5)
-    _, _, _ = G.create_note(evaluation=e1, etud=etuds[1], note=11.0)
-    _, _, _ = G.create_note(evaluation=e2, etud=etuds[1], note=11.0)
-    _, _, _ = G.create_note(evaluation=e_m2, etud=etuds[1], note=11.0)
+    _, _, _ = G.create_note(
+        evaluation_id=e1["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+    )  # EXC
+    _, _, _ = G.create_note(
+        evaluation_id=e2["id"], etudid=etudid, note=scu.NOTES_NEUTRALISE
+    )  # EXC
+    _, _, _ = G.create_note(evaluation_id=e_m2["id"], etudid=etudid, note=12.5)
+    _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etuds[1]["id"], note=11.0)
+    _, _, _ = G.create_note(evaluation_id=e2["id"], etudid=etuds[1]["id"], note=11.0)
+    _, _, _ = G.create_note(evaluation_id=e_m2["id"], etudid=etuds[1]["id"], note=11.0)
 
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
@@ -366,8 +386,12 @@ def test_notes_modules_att_dem(test_client):
         coefficient=coef_1,
     )
     # Attente (ATT) sur les 2 evals
-    _, _, _ = G.create_note(evaluation=e1, etud=etuds[0], note=scu.NOTES_ATTENTE)  # ATT
-    _, _, _ = G.create_note(evaluation=e1, etud=etuds[1], note=scu.NOTES_ATTENTE)  # ATT
+    _, _, _ = G.create_note(
+        evaluation_id=e1["id"], etudid=etuds[0]["id"], note=scu.NOTES_ATTENTE
+    )  # ATT
+    _, _, _ = G.create_note(
+        evaluation_id=e1["id"], etudid=etuds[1]["id"], note=scu.NOTES_ATTENTE
+    )  # ATT
     # Démission du premier étudiant
     sco_formsemestre_inscriptions.do_formsemestre_demission(
         etuds[0]["etudid"],
@@ -406,7 +430,7 @@ def test_notes_modules_att_dem(test_client):
     assert note_e1 == scu.NOTES_ATTENTE  # XXXX un peu contestable
 
     # Saisie note ABS pour le deuxième etud
-    _, _, _ = G.create_note(evaluation=e1, etud=etuds[1], note=None)  # ABS
+    _, _, _ = G.create_note(evaluation_id=e1["id"], etudid=etuds[1]["id"], note=None)
     nt = check_nt(
         etuds[1]["etudid"],
         sem["formsemestre_id"],
diff --git a/tests/unit/test_notes_rattrapage.py b/tests/unit/test_notes_rattrapage.py
index 07a6763ff0430d9acd1831202444d18a89526daa..e804b5e632cbb344dc7bb90673e4333d1591fbdb 100644
--- a/tests/unit/test_notes_rattrapage.py
+++ b/tests/unit/test_notes_rattrapage.py
@@ -1,13 +1,11 @@
 """Test calculs rattrapages
 """
 
-from flask import g
-
 import app
-from app.but.bulletin_but import *
+
 from app.comp import res_sem
 from app.comp.res_but import ResultatsSemestreBUT
-from app.models import ModuleImpl
+from app.models import FormSemestre, ModuleImpl
 from app.scodoc import (
     sco_bulletins,
     sco_evaluation_db,
@@ -75,8 +73,8 @@ def test_notes_rattrapage(test_client):
         evaluation_type=scu.EVALUATION_RATTRAPAGE,
     )
     etud = etuds[0]
-    _, _, _ = G.create_note(evaluation=e, etud=etud, note=12.0)
-    _, _, _ = G.create_note(evaluation=e_rat, etud=etud, note=11.0)
+    _, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["id"], note=12.0)
+    _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["id"], note=11.0)
 
     # --- Vérifications internes structures ScoDoc
     formsemestre = FormSemestre.query.get(formsemestre_id)
@@ -100,21 +98,21 @@ def test_notes_rattrapage(test_client):
     # Note moyenne: ici le ratrapage est inférieur à la note:
     assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(12.0)
     # rattrapage > moyenne:
-    _, _, _ = G.create_note(evaluation=e_rat, etud=etud, note=18.0)
+    _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["id"], note=18.0)
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
     assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(18.0)
     # rattrapage vs absences
-    _, _, _ = G.create_note(evaluation=e, etud=etud, note=None)  # abs
-    _, _, _ = G.create_note(evaluation=e_rat, etud=etud, note=17.0)
+    _, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["id"], note=None)  # abs
+    _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["id"], note=17.0)
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
     assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(17.0)
     # et sans note de rattrapage
-    _, _, _ = G.create_note(evaluation=e, etud=etud, note=10.0)  # abs
-    _, _, _ = G.create_note(evaluation=e_rat, etud=etud, note=None)
+    _, _, _ = G.create_note(evaluation_id=e["id"], etudid=etud["id"], note=10.0)
+    _, _, _ = G.create_note(evaluation_id=e_rat["id"], etudid=etud["id"], note=None)
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
@@ -156,21 +154,25 @@ def test_notes_rattrapage(test_client):
     assert len(mod_res.get_evaluations_completes(moduleimpl)) == 2
 
     # Saisie note session 2:
-    _, _, _ = G.create_note(evaluation=e_session2, etud=etud, note=5.0)
+    _, _, _ = G.create_note(evaluation_id=e_session2["id"], etudid=etud["id"], note=5.0)
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
     # Note moyenne: utilise session 2 même si inférieure
     assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(5.0)
 
-    _, _, _ = G.create_note(evaluation=e_session2, etud=etud, note=20.0)
+    _, _, _ = G.create_note(
+        evaluation_id=e_session2["id"], etudid=etud["id"], note=20.0
+    )
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
     # Note moyenne: utilise session 2 même si inférieure
     assert b["ues"][0]["modules"][0]["mod_moy_txt"] == scu.fmt_note(20.0)
 
-    _, _, _ = G.create_note(evaluation=e_session2, etud=etud, note=None)
+    _, _, _ = G.create_note(
+        evaluation_id=e_session2["id"], etudid=etud["id"], note=None
+    )
     b = sco_bulletins.formsemestre_bulletinetud_dict(
         sem["formsemestre_id"], etud["etudid"]
     )
diff --git a/tests/unit/test_sco_basic.py b/tests/unit/test_sco_basic.py
index 89d023ef70ced82f2b74e6a21ccc8db0bfb05d96..f3d46758b10728c0b4f4d6f278233d8fac2b9617 100644
--- a/tests/unit/test_sco_basic.py
+++ b/tests/unit/test_sco_basic.py
@@ -11,12 +11,11 @@ Au besoin, créer un base de test neuve:
     ./tools/create_database.sh SCODOC_TEST
 
 """
-import random
-
 from app.models import FormSemestreInscription, Identite
 
 from config import TestConfig
 from tests.unit import sco_fake_gen
+from tests.unit.setup import NOTES_T
 
 import app
 from app import db
@@ -33,7 +32,6 @@ from app.scodoc import sco_evaluation_db
 from app.scodoc import sco_formsemestre_validation
 from app.scodoc import sco_cursus_dut
 from app.scodoc import sco_saisie_notes
-from app.scodoc import sco_utils as scu
 
 DEPT = TestConfig.DEPT_TEST
 
@@ -47,9 +45,10 @@ def test_sco_basic(test_client):
     run_sco_basic()
 
 
-def run_sco_basic(verbose=False):
+def run_sco_basic(verbose=False) -> FormSemestre:
     """Scénario de base: création formation, semestre, étudiants, notes,
     décisions jury
+    Renvoie le formsemestre créé.
     """
     G = sco_fake_gen.ScoFake(verbose=verbose)
 
@@ -91,11 +90,11 @@ def run_sco_basic(verbose=False):
     )
     assert q.count() == 1
     ins = q.first()
-    assert ins.etape == None
+    assert ins.etape is None
     assert ins.etat == "I"
-    assert ins.parcour == None
+    assert ins.parcour is None
 
-    # --- Creation évaluation
+    # --- Création évaluation
     e = G.create_evaluation(
         moduleimpl_id=moduleimpl_id,
         jour="01/01/2020",
@@ -104,10 +103,13 @@ def run_sco_basic(verbose=False):
     )
 
     # --- Saisie toutes les notes de l'évaluation
-    for etud in etuds:
+    for idx, etud in enumerate(etuds):
         nb_changed, nb_suppress, existing_decisions = G.create_note(
-            evaluation=e, etud=etud, note=float(random.randint(0, 20))
+            evaluation_id=e["id"], etudid=etud["id"], note=NOTES_T[idx % len(NOTES_T)]
         )
+        assert not existing_decisions
+        assert nb_suppress == 0
+        assert nb_changed == 1
 
     # --- Vérifie que les notes sont prises en compte:
     b = sco_bulletins.formsemestre_bulletinetud_dict(formsemestre_id, etud["etudid"])
@@ -132,13 +134,13 @@ def run_sco_basic(verbose=False):
         coefficient=1.0,
     )
     # Saisie les notes des 5 premiers étudiants:
-    for etud in etuds[:5]:
+    for idx, etud in enumerate(etuds[:5]):
         nb_changed, nb_suppress, existing_decisions = G.create_note(
-            evaluation=e2, etud=etud, note=float(random.randint(0, 20))
+            evaluation_id=e2["id"], etudid=etud["id"], note=NOTES_T[idx % len(NOTES_T)]
         )
     # Cette éval n'est pas complète
     etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"])
-    assert etat["evalcomplete"] == False
+    assert etat["evalcomplete"] is False
     # la première éval est toujours complète:
     etat = sco_evaluations.do_evaluation_etat(e["evaluation_id"])
     assert etat["evalcomplete"]
@@ -147,14 +149,14 @@ def run_sco_basic(verbose=False):
     e2["publish_incomplete"] = True
     sco_evaluation_db.do_evaluation_edit(e2)
     etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"])
-    assert etat["evalcomplete"] == False
+    assert etat["evalcomplete"] is False
     assert etat["nb_att"] == 0  # il n'y a pas de notes (explicitement) en attente
     assert etat["evalattente"]  # mais l'eval est en attente (prise en compte immédiate)
 
     # Saisie des notes qui manquent:
-    for etud in etuds[5:]:
+    for idx, etud in enumerate(etuds[5:]):
         nb_changed, nb_suppress, existing_decisions = G.create_note(
-            evaluation=e2, etud=etud, note=float(random.randint(0, 20))
+            evaluation_id=e2["id"], etudid=etud["id"], note=NOTES_T[idx % len(NOTES_T)]
         )
     etat = sco_evaluations.do_evaluation_etat(e2["evaluation_id"])
     assert etat["evalcomplete"]
@@ -173,7 +175,10 @@ def run_sco_basic(verbose=False):
     assert f'{etat["nb_inscrits"]} notes changées' in ans
     etat = sco_evaluations.do_evaluation_etat(e["evaluation_id"])
     assert etat["evalcomplete"]
-    # --- Saisie absences
+
+    # -----------------------
+    # --- Saisie absences ---
+    # -----------------------
     etudid = etuds[0]["etudid"]
 
     _ = sco_abs_views.doSignaleAbsence(
@@ -188,8 +193,8 @@ def run_sco_basic(verbose=False):
     )
 
     nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
-    assert nbabs == 6, "incorrect nbabs (%d)" % nbabs
-    assert nbabsjust == 2, "incorrect nbabsjust (%s)" % nbabsjust
+    assert nbabs == 6, f"incorrect nbabs ({nbabs})"
+    assert nbabsjust == 2, f"incorrect nbabsjust ({nbabsjust})"
 
     # --- Permission saisie notes et décisions de jury, avec ou sans démission ou défaillance
     # on n'a pas encore saisi de décisions
@@ -227,7 +232,7 @@ def run_sco_basic(verbose=False):
         for ue_id in dec_ues:
             assert dec_ues[ue_id]["code"] in {"ADM", "CMP"}
 
-    # ---- Suppression étudiant, vérification inscription
+    # ---- Suppression d'un étudiant, vérification inscription
     # (permet de tester les cascades)
     etud = Identite.query.get(etuds[0]["id"])
     assert etud is not None
@@ -239,3 +244,4 @@ def run_sco_basic(verbose=False):
         etudid=etudid, formsemestre_id=formsemestre_id
     )
     assert q.count() == 0
+    return formsemestre