diff --git a/app/api/formations.py b/app/api/formations.py
index f675acc32647570d14f07d876c19608e1b08ab22..d2de7ce2e97ff76df18721d30d8b3a2dcb0c0aed 100644
--- a/app/api/formations.py
+++ b/app/api/formations.py
@@ -30,7 +30,7 @@ from app.models import (
     Module,
     UniteEns,
 )
-from app.scodoc import sco_formations
+from app.formations import formation_io
 from app.scodoc.sco_permissions import Permission
 
 
@@ -141,7 +141,7 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
     formation = query.first_or_404(formation_id)
     app.set_sco_dept(formation.departement.acronym)
     try:
-        data = sco_formations.formation_export(formation_id, export_ids)
+        data = formation_io.formation_export(formation_id, export_ids)
     except ValueError:
         return json_error(500, message="Erreur inconnue")
 
diff --git a/app/scodoc/sco_edit_formation.py b/app/formations/edit_formation.py
similarity index 99%
rename from app/scodoc/sco_edit_formation.py
rename to app/formations/edit_formation.py
index 6eb276035f0e011445603f2f6688db67ba5a8f21..3b9a263689c4d7492b4d410da068ccc59bd0105e 100644
--- a/app/scodoc/sco_edit_formation.py
+++ b/app/formations/edit_formation.py
@@ -33,6 +33,7 @@ from flask import flash, g, url_for, render_template, request
 import sqlalchemy
 
 from app import db
+from app.formations import edit_ue
 from app.models import SHORT_STR_LEN
 from app.models.formations import Formation
 from app.models.modules import Module
@@ -45,7 +46,6 @@ from app.scodoc.sco_exceptions import ScoValueError, ScoNonEmptyFormationObject
 
 from app.scodoc import sco_cache
 from app.scodoc import codes_cursus
-from app.scodoc import sco_edit_ue
 
 
 def formation_delete(formation_id=None, dialog_confirmed=False):
@@ -123,7 +123,7 @@ def do_formation_delete(formation_id):
         db.session.flush()
         # Suppression des UEs
         for ue in formation.ues:
-            sco_edit_ue.do_ue_delete(ue, force=True)
+            edit_ue.do_ue_delete(ue, force=True)
 
         db.session.delete(formation)
 
diff --git a/app/scodoc/sco_edit_matiere.py b/app/formations/edit_matiere.py
similarity index 92%
rename from app/scodoc/sco_edit_matiere.py
rename to app/formations/edit_matiere.py
index 9bba3d05c6b1631ec40e89e93951515bec96d860..26add3956c6e5a32ffc16f1dd872335219baf4ad 100644
--- a/app/scodoc/sco_edit_matiere.py
+++ b/app/formations/edit_matiere.py
@@ -61,7 +61,7 @@ def matiere_list(*args, **kw):
 
 def do_matiere_edit(*args, **kw):
     "edit a matiere"
-    from app.scodoc import sco_edit_ue
+    from app.formations import edit_ue
 
     cnx = ndb.GetDBConnexion()
     # check
@@ -70,17 +70,17 @@ def do_matiere_edit(*args, **kw):
         raise ScoLockedFormError()
     # edit
     _matiereEditor.edit(cnx, *args, **kw)
-    formation_id = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]["formation_id"]
+    formation_id = edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]["formation_id"]
     db.session.get(Formation, formation_id).invalidate_cached_sems()
 
 
 def do_matiere_create(args):
     "create a matiere"
-    from app.scodoc import sco_edit_ue
+    from app.formations import edit_ue
 
     cnx = ndb.GetDBConnexion()
     # check
-    ue = sco_edit_ue.ue_list({"ue_id": args["ue_id"]})[0]
+    ue = edit_ue.ue_list({"ue_id": args["ue_id"]})[0]
     # create matiere
     r = _matiereEditor.create(cnx, args)
 
@@ -180,23 +180,22 @@ def can_delete_matiere(matiere: Matiere) -> tuple[bool, str]:
 
 def do_matiere_delete(oid):
     "delete matiere and attached modules"
-    from app.scodoc import sco_edit_ue
-    from app.scodoc import sco_edit_module
+    from app.formations import edit_module, edit_ue
 
     cnx = ndb.GetDBConnexion()
     # check
     matiere = Matiere.query.get_or_404(oid)
     mat = matiere_list({"matiere_id": oid})[0]  # compat sco7
-    ue = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]
+    ue = edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]
     if not can_delete_matiere(matiere):
         # il y a au moins un modimpl dans un module de cette matière
         raise ScoNonEmptyFormationObject("Matière", matiere.titre)
 
-    log("do_matiere_delete: matiere_id=%s" % matiere.id)
+    log(f"do_matiere_delete: matiere_id={matiere.id}")
     # delete all modules in this matiere
-    mods = sco_edit_module.module_list({"matiere_id": matiere.id})
+    mods = edit_module.module_list({"matiere_id": matiere.id})
     for mod in mods:
-        sco_edit_module.do_module_delete(mod["module_id"])
+        edit_module.do_module_delete(mod["module_id"])
     _matiereEditor.delete(cnx, oid)
 
     # news
@@ -211,7 +210,7 @@ def do_matiere_delete(oid):
 
 def matiere_delete(matiere_id=None):
     """Delete matière"""
-    from app.scodoc import sco_edit_ue
+    from app.formations import edit_ue
 
     matiere = Matiere.query.get_or_404(matiere_id)
     if not can_delete_matiere(matiere):
@@ -228,15 +227,15 @@ def matiere_delete(matiere_id=None):
         )
 
     mat = matiere_list(args={"matiere_id": matiere_id})[0]
-    UE = sco_edit_ue.ue_list(args={"ue_id": mat["ue_id"]})[0]
+    ue_dict = edit_ue.ue_list(args={"ue_id": mat["ue_id"]})[0]
     H = [
         "<h2>Suppression de la matière %(titre)s" % mat,
-        " dans l'UE (%(acronyme)s))</h2>" % UE,
+        " dans l'UE (%(acronyme)s))</h2>" % ue_dict,
     ]
     dest_url = url_for(
         "notes.ue_table",
         scodoc_dept=g.scodoc_dept,
-        formation_id=str(UE["formation_id"]),
+        formation_id=str(ue_dict["formation_id"]),
     )
     tf = TrivialFormulator(
         request.base_url,
@@ -261,18 +260,18 @@ def matiere_delete(matiere_id=None):
 
 def matiere_edit(matiere_id=None):
     """Edit matiere"""
-    from app.scodoc import sco_edit_ue
+    from app.formations import edit_ue
 
     F = matiere_list(args={"matiere_id": matiere_id})
     if not F:
         raise ScoValueError("Matière inexistante !")
     F = F[0]
-    ues = sco_edit_ue.ue_list(args={"ue_id": F["ue_id"]})
+    ues = edit_ue.ue_list(args={"ue_id": F["ue_id"]})
     if not ues:
         raise ScoValueError("UE inexistante !")
     ue = ues[0]
     formation: Formation = Formation.query.get_or_404(ue["formation_id"])
-    ues = sco_edit_ue.ue_list(args={"formation_id": ue["formation_id"]})
+    ues = edit_ue.ue_list(args={"formation_id": ue["formation_id"]})
     ue_names = ["%(acronyme)s (%(titre)s)" % u for u in ues]
     ue_ids = [u["ue_id"] for u in ues]
     H = [
diff --git a/app/scodoc/sco_edit_module.py b/app/formations/edit_module.py
similarity index 99%
rename from app/scodoc/sco_edit_module.py
rename to app/formations/edit_module.py
index 27b197e9fe127d415f4b2ad9f5895a0b1e721671..8cb8dccf67cd71b6558489404f3707fcd5d31e15 100644
--- a/app/scodoc/sco_edit_module.py
+++ b/app/formations/edit_module.py
@@ -35,6 +35,7 @@ from flask_login import current_user
 
 from app import db, log
 from app import models
+from app.formations import edit_matiere
 from app.models import APO_CODE_STR_LEN
 from app.models import Formation, Matiere, Module, UniteEns
 from app.models import FormSemestre, ModuleImpl
@@ -52,7 +53,6 @@ from app.scodoc.sco_exceptions import (
     ScoNonEmptyFormationObject,
 )
 from app.scodoc import codes_cursus
-from app.scodoc import sco_edit_matiere
 from app.scodoc import sco_moduleimpl
 
 _moduleEditor = ndb.EditableTable(
@@ -833,7 +833,7 @@ def module_edit(
             if matiere:
                 tf[2]["matiere_id"] = matiere.id
             else:
-                matiere_id = sco_edit_matiere.do_matiere_create(
+                matiere_id = edit_matiere.do_matiere_create(
                     {"ue_id": ue.id, "titre": ue.titre or "", "numero": 1},
                 )
                 tf[2]["matiere_id"] = matiere_id
@@ -899,8 +899,6 @@ def module_table(formation_id):
     """Liste des modules de la formation
     (XXX inutile ou a revoir)
     """
-    from app.scodoc import sco_formations
-
     if not formation_id:
         raise ScoValueError("invalid formation !")
     formation: Formation = Formation.query.get_or_404(formation_id)
diff --git a/app/scodoc/sco_edit_ue.py b/app/formations/edit_ue.py
similarity index 98%
rename from app/scodoc/sco_edit_ue.py
rename to app/formations/edit_ue.py
index c47cca531886fc8cfbb40e034a80978a587d04c6..3e4d136416c0408edef8bebe9d580fbb3a649dce 100644
--- a/app/scodoc/sco_edit_ue.py
+++ b/app/formations/edit_ue.py
@@ -37,6 +37,7 @@ from flask_login import current_user
 
 from app import db, log
 from app.but import apc_edit_ue
+from app.formations import edit_matiere, edit_module
 from app.models import APO_CODE_STR_LEN, SHORT_STR_LEN
 from app.models import (
     Formation,
@@ -63,8 +64,6 @@ from app.scodoc.sco_exceptions import (
 
 from app.scodoc import codes_cursus
 from app.scodoc import sco_edit_apc
-from app.scodoc import sco_edit_matiere
-from app.scodoc import sco_edit_module
 from app.scodoc import sco_groups
 from app.scodoc import sco_moduleimpl
 from app.scodoc import sco_tag_module
@@ -548,12 +547,12 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
             if is_apc or cursus.UE_IS_MODULE or tf[2]["create_matiere"]:
                 # rappel: en APC, toutes les UE ont une matière, créée ici
                 # (inutilisée mais à laquelle les modules sont rattachés)
-                matiere_id = sco_edit_matiere.do_matiere_create(
+                matiere_id = edit_matiere.do_matiere_create(
                     {"ue_id": ue_id, "titre": tf[2]["titre"], "numero": 1},
                 )
             if cursus.UE_IS_MODULE:
                 # dans ce mode, crée un (unique) module dans l'UE:
-                _ = sco_edit_module.do_module_create(
+                _ = edit_module.do_module_create(
                     {
                         "titre": tf[2]["titre"],
                         "code": tf[2]["acronyme"],
@@ -608,7 +607,7 @@ def _add_ue_semestre_id(ues: list[dict], is_apc):
             ue["semestre_id"] = codes_cursus.UE_SEM_DEFAULT
         else:
             # était le comportement ScoDoc7
-            modules = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
+            modules = edit_module.module_list(args={"ue_id": ue["ue_id"]})
             if modules:
                 ue["semestre_id"] = modules[0]["semestre_id"]
             else:
@@ -1254,11 +1253,11 @@ def _ue_table_matieres(
     H = []
     if not parcours.UE_IS_MODULE:
         H.append('<ul class="notes_matiere_list">')
-    matieres = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
+    matieres = edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
     for mat in matieres:
         if not parcours.UE_IS_MODULE:
             H.append('<li class="notes_matiere_list">')
-            if editable and not sco_edit_matiere.matiere_is_locked(mat["matiere_id"]):
+            if editable and not edit_matiere.matiere_is_locked(mat["matiere_id"]):
                 H.append(
                     f"""<a class="stdlink" href="{
                         url_for("notes.matiere_edit",
@@ -1267,10 +1266,10 @@ def _ue_table_matieres(
                     """
                 )
             H.append("%(titre)s" % mat)
-            if editable and not sco_edit_matiere.matiere_is_locked(mat["matiere_id"]):
+            if editable and not edit_matiere.matiere_is_locked(mat["matiere_id"]):
                 H.append("</a>")
 
-        modules = sco_edit_module.module_list(args={"matiere_id": mat["matiere_id"]})
+        modules = edit_module.module_list(args={"matiere_id": mat["matiere_id"]})
         H.append(
             _ue_table_modules(
                 parcours,
@@ -1327,9 +1326,7 @@ def _ue_table_modules(
     H = ['<ul class="notes_module_list">']
     im = 0
     for mod in modules:
-        mod["nb_moduleimpls"] = sco_edit_module.module_count_moduleimpls(
-            mod["module_id"]
-        )
+        mod["nb_moduleimpls"] = edit_module.module_count_moduleimpls(mod["module_id"])
         klass = "notes_module_list"
         if mod["module_type"] == ModuleType.MALUS:
             klass += " module_malus"
@@ -1363,7 +1360,7 @@ def _ue_table_modules(
         H.append("</span>")
 
         mod_editable = (
-            editable  # and not sco_edit_module.module_is_locked( Mod['module_id'])
+            editable  # and not edit_module.module_is_locked( Mod['module_id'])
         )
         if mod_editable:
             H.append(
diff --git a/app/scodoc/sco_formations.py b/app/formations/formation_io.py
similarity index 94%
rename from app/scodoc/sco_formations.py
rename to app/formations/formation_io.py
index e86b371dc1966647bc3097f5c7932a012953cb4c..bd21357262c0efaafbb7b68435d639ee662bd8a4 100644
--- a/app/scodoc/sco_formations.py
+++ b/app/formations/formation_io.py
@@ -34,9 +34,9 @@ from flask import flash, g, request, url_for
 from flask_login import current_user
 
 import app.scodoc.sco_utils as scu
-import app.scodoc.notesdb as ndb
 from app import db
 from app import log
+from app.formations import edit_matiere, edit_module, edit_ue
 from app.models import Formation, FormSemestre, Module, UniteEns
 from app.models import ScolarNews
 from app.models.but_refcomp import (
@@ -48,49 +48,13 @@ from app.models.but_refcomp import (
 )
 from app.scodoc import sco_cache
 from app.scodoc import codes_cursus
-from app.scodoc import sco_edit_matiere
-from app.scodoc import sco_edit_module
-from app.scodoc import sco_edit_ue
 from app.scodoc import sco_preferences
 from app.scodoc import sco_tag_module
 from app.scodoc import sco_xml
-import sco_version
 from app.scodoc.gen_tables import GenTable
 from app.scodoc.sco_exceptions import ScoValueError, ScoFormatError
 from app.scodoc.sco_permissions import Permission
-
-_formationEditor = ndb.EditableTable(
-    "notes_formations",
-    "formation_id",
-    (
-        "formation_id",
-        "acronyme",
-        "titre",
-        "titre_officiel",
-        "version",
-        "formation_code",
-        "type_parcours",
-        "code_specialite",
-        "referentiel_competence_id",
-        "commentaire",
-    ),
-    filter_dept=True,
-    sortkey="acronyme",
-)
-
-
-def formation_list(formation_id=None, args={}):  ### XXX obsolete, à supprimer
-    """List formation(s) with given id, or matching args
-    (when args is given, formation_id is ignored).
-    """
-    if not args:
-        if formation_id is None:
-            args = {}
-        else:
-            args = {"formation_id": formation_id}
-    cnx = ndb.GetDBConnexion()
-    r = _formationEditor.list(cnx, args=args)
-    return r
+import sco_version
 
 
 def formation_export_dict(
@@ -149,7 +113,7 @@ def formation_export_dict(
             ue_dict.pop("code_apogee_rcue", None)
         if ue_dict.get("ects") is None:
             ue_dict.pop("ects", None)
-        mats = sco_edit_matiere.matiere_list({"ue_id": ue.id})
+        mats = edit_matiere.matiere_list({"ue_id": ue.id})
         mats.sort(key=lambda m: m["numero"] or 0)
         ue_dict["matiere"] = mats
         for mat in mats:
@@ -158,7 +122,7 @@ def formation_export_dict(
                 del mat["id"]
                 del mat["matiere_id"]
                 del mat["ue_id"]
-            mods = sco_edit_module.module_list({"matiere_id": matiere_id})
+            mods = edit_module.module_list({"matiere_id": matiere_id})
             mods.sort(key=lambda m: (m["numero"] or 0, m["code"]))
             mat["module"] = mods
             for mod in mods:
@@ -314,7 +278,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
     Returns:
         formation_id, modules_old2new, ues_old2new
     """
-    from app.scodoc import sco_edit_formation
+    from app.formations import edit_formation
 
     if isinstance(doc, bytes):
         doc = doc.decode(scu.SCO_ENCODING)
@@ -359,7 +323,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
     f_dict["version"] = version + 1
 
     # create formation
-    formation = sco_edit_formation.do_formation_create(f_dict)
+    formation = edit_formation.do_formation_create(f_dict)
     log(f"formation {formation.id} created")
 
     ues_old2new = {}  # xml ue_id : new ue_id
@@ -387,7 +351,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
                 )
             # Note: si le code est indiqué "" dans le xml, il faut le conserver vide
             # pour la comparaison ultérieure des formations XXX
-            ue_id = sco_edit_ue.do_ue_create(ue_info[1], allow_empty_ue_code=True)
+            ue_id = edit_ue.do_ue_create(ue_info[1], allow_empty_ue_code=True)
             ue: UniteEns = db.session.get(UniteEns, ue_id)
             assert ue
             if xml_ue_id:
@@ -435,7 +399,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
 
                 assert mat_info[0] == "matiere"
                 mat_info[1]["ue_id"] = ue_id
-                mat_id = sco_edit_matiere.do_matiere_create(mat_info[1])
+                mat_id = edit_matiere.do_matiere_create(mat_info[1])
                 # -- create modules
                 for mod_info in mat_info[2]:
                     assert mod_info[0] == "module"
@@ -449,7 +413,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
                     mod_info[1]["ue_id"] = ue_id
                     if not "module_type" in mod_info[1]:
                         mod_info[1]["module_type"] = scu.ModuleType.STANDARD
-                    mod_id = sco_edit_module.do_module_create(mod_info[1])
+                    mod_id = edit_module.do_module_create(mod_info[1])
                     if xml_module_id:
                         modules_old2new[int(xml_module_id)] = mod_id
                     if len(mod_info) > 2:
diff --git a/app/scodoc/sco_formation_versions.py b/app/formations/formation_versions.py
similarity index 98%
rename from app/scodoc/sco_formation_versions.py
rename to app/formations/formation_versions.py
index b532146c42386fc562bfcaefd541bdc25941c429..afc41b293dde346b5193f76bf0aa649ec85a1f67 100644
--- a/app/scodoc/sco_formation_versions.py
+++ b/app/formations/formation_versions.py
@@ -47,7 +47,7 @@ import app.scodoc.sco_utils as scu
 
 from app import log
 from app.scodoc.sco_exceptions import ScoValueError
-from app.scodoc import sco_formations
+from app.formations import formation_io
 
 
 def formsemestre_associate_new_version(
@@ -202,7 +202,7 @@ def do_formsemestres_associate_new_version(
         new_formation_id,
         modules_old2new,
         ues_old2new,
-    ) = sco_formations.formation_create_new_version(formation_id, redirect=False)
+    ) = formation_io.formation_create_new_version(formation_id, redirect=False)
     # Log new ues:
     for ue_id in ues_old2new:
         ue = db.session.get(UniteEns, ue_id)
@@ -275,13 +275,13 @@ def formations_are_equals(
     """True if the two formations are exactly the same, except for their versions.
     Can specify either formation2 or its dict repr.
     """
-    fd1 = sco_formations.formation_export_dict(
+    fd1 = formation_io.formation_export_dict(
         formation1, export_external_ues=True, ue_reference_style="acronyme"
     )
     if formation2_dict is None:
         if formation2 is None:
             raise ValueError("must specify formation2 or formation2_dict")
-        formation2_dict = sco_formations.formation_export_dict(
+        formation2_dict = formation_io.formation_export_dict(
             formation2, export_external_ues=True, ue_reference_style="acronyme"
         )
     del fd1["version"]
diff --git a/app/models/ues.py b/app/models/ues.py
index 5ff4258b140c19dd15ae2dab1a64783be7828a3a..3e74b88b1eb54f38d78b16347fa32c441f5f2cfd 100644
--- a/app/models/ues.py
+++ b/app/models/ues.py
@@ -186,9 +186,9 @@ class UniteEns(models.ScoDocModel):
 
     def is_locked(self) -> tuple[bool, str]:
         """True if UE should not be modified"""
-        from app.scodoc import sco_edit_ue
+        from app.formations import edit_ue
 
-        return sco_edit_ue.ue_is_locked(self.id)
+        return edit_ue.ue_is_locked(self.id)
 
     def can_be_deleted(self) -> bool:
         """True si l'UE n'a pas de moduleimpl rattachés
diff --git a/app/scodoc/sco_bulletins_json.py b/app/scodoc/sco_bulletins_json.py
index 46383b0d1c2a5b95ea773578cebe277ec76f3edc..1746145789c4c497c70641fa3a2620be2bc867db 100644
--- a/app/scodoc/sco_bulletins_json.py
+++ b/app/scodoc/sco_bulletins_json.py
@@ -36,6 +36,7 @@ from flask import abort
 from app import db, ScoDocJSONEncoder
 from app.comp import res_sem
 from app.comp.res_compat import NotesTableCompat
+from app.formations import edit_ue
 from app.models import but_validations
 from app.models import BulAppreciations, Evaluation, Matiere, UniteEns
 from app.models.etudiants import Identite
@@ -44,7 +45,6 @@ from app.models.formsemestre import FormSemestre
 import app.scodoc.sco_utils as scu
 import app.scodoc.notesdb as ndb
 from app.scodoc import sco_assiduites
-from app.scodoc import sco_edit_ue
 from app.scodoc import sco_evaluations
 from app.scodoc import sco_formsemestre
 from app.scodoc import sco_groups
@@ -495,7 +495,7 @@ def dict_decision_jury(
                 "decisions_ue"
             ]:  # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
                 for ue_id in decision["decisions_ue"].keys():
-                    ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0]
+                    ue = edit_ue.ue_list({"ue_id": ue_id})[0]
                     d["decision_ue"].append(
                         dict(
                             ue_id=ue["ue_id"],
diff --git a/app/scodoc/sco_bulletins_xml.py b/app/scodoc/sco_bulletins_xml.py
index 77f95ac28ec9afd13a9d2a58a3970309a9a2b48f..dfff8feeb5b53709469e2ab09a29aac39108552b 100644
--- a/app/scodoc/sco_bulletins_xml.py
+++ b/app/scodoc/sco_bulletins_xml.py
@@ -46,6 +46,7 @@ from xml.etree.ElementTree import Element
 
 from app.comp import res_sem
 from app.comp.res_compat import NotesTableCompat
+from app.formations import edit_ue
 import app.scodoc.sco_utils as scu
 import app.scodoc.notesdb as ndb
 from app import log
@@ -53,7 +54,6 @@ from app.but.bulletin_but_xml_compat import bulletin_but_xml_compat
 from app.models import BulAppreciations, Evaluation, FormSemestre
 from app.scodoc import sco_assiduites
 from app.scodoc import codes_cursus
-from app.scodoc import sco_edit_ue
 from app.scodoc import sco_formsemestre
 from app.scodoc import sco_groups
 from app.scodoc import sco_photos
@@ -393,7 +393,7 @@ def make_xml_formsemestre_bulletinetud(
                 "decisions_ue"
             ]:  # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
                 for ue_id in decision["decisions_ue"].keys():
-                    ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0]
+                    ue = edit_ue.ue_list({"ue_id": ue_id})[0]
                     doc.append(
                         Element(
                             "decision_ue",
diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py
index 8af2d89e4b4def7ba0b0360d9d50806354e124a5..5c5b541a28b95e9d8bcbb1ee3aa2c88cafe0ed45 100644
--- a/app/scodoc/sco_formsemestre_edit.py
+++ b/app/scodoc/sco_formsemestre_edit.py
@@ -34,6 +34,7 @@ import sqlalchemy as sa
 
 from app import db
 from app.auth.models import User
+from app.formations import edit_module
 from app.models import APO_CODE_STR_LEN, SHORT_STR_LEN
 from app.models import (
     ApcValidationAnnee,
@@ -62,7 +63,6 @@ from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
 from app.scodoc.sco_permissions import Permission
 from app.scodoc.sco_vdi import ApoEtapeVDI
 from app.scodoc import codes_cursus
-from app.scodoc import sco_edit_module
 from app.scodoc import sco_formsemestre
 from app.scodoc import sco_groups_copy
 from app.scodoc import sco_moduleimpl
@@ -972,7 +972,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
                     "responsable_id": tf[2]["MI" + str(module_id)],
                 }
                 moduleimpl_id = sco_moduleimpl.do_moduleimpl_create(modargs)
-                mod = sco_edit_module.module_list({"module_id": module_id})[0]
+                mod = edit_module.module_list({"module_id": module_id})[0]
                 msg += [
                     "création de %s (%s)" % (mod["code"] or "?", mod["titre"] or "?")
                 ]
@@ -1022,7 +1022,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
                 sco_moduleimpl.do_moduleimpl_edit(
                     modargs, formsemestre_id=formsemestre.id
                 )
-                mod = sco_edit_module.module_list({"module_id": module_id})[0]
+                mod = edit_module.module_list({"module_id": module_id})[0]
     # --- Association des parcours
     if formsemestre is None:
         formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py
index 93c382db57a6608fbe95f2b658a9bcd3cf85ea81..17907c1a5fc5031b9bb0ec51e07f82aff8b10c02 100755
--- a/app/scodoc/sco_formsemestre_status.py
+++ b/app/scodoc/sco_formsemestre_status.py
@@ -40,6 +40,7 @@ from app.but.cursus_but import formsemestre_warning_apc_setup
 from app.comp import res_sem
 from app.comp.res_common import ResultatsSemestre
 from app.comp.res_compat import NotesTableCompat
+from app.formations import formation_io
 from app.models import (
     Evaluation,
     Formation,
@@ -64,7 +65,6 @@ from app.scodoc import sco_assiduites as scass
 from app.scodoc import sco_bulletins
 from app.scodoc import sco_cache
 from app.scodoc import sco_evaluations
-from app.scodoc import sco_formations
 from app.scodoc import sco_formsemestre
 from app.scodoc import sco_formsemestre_inscriptions
 from app.scodoc import sco_groups
@@ -515,10 +515,9 @@ def formsemestre_page_title(formsemestre_id=None):
 def fill_formsemestre(sem: dict):  # XXX OBSOLETE
     """Add some  fields in formsemestres dicts"""
     formsemestre_id = sem["formsemestre_id"]
-
-    F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
-    sem["formation"] = F
-    parcours = codes_cursus.get_cursus_from_code(F["type_parcours"])
+    formation = Formation.get_formation(sem["formation_id"])
+    sem["formation"] = formation.to_dict(with_departement=False)
+    parcours = codes_cursus.get_cursus_from_code(formation.type_parcours)
     if sem["semestre_id"] != -1:
         sem["num_sem"] = f""", {parcours.SESSION_NAME} {sem["semestre_id"]}"""
     else:
diff --git a/app/scodoc/sco_placement.py b/app/scodoc/sco_placement.py
index 1b51ced2b3db2f5c737085e3b37addfea7a93dac..67fe315687da7c74c4f8e032017f9cc68415c4f6 100644
--- a/app/scodoc/sco_placement.py
+++ b/app/scodoc/sco_placement.py
@@ -48,11 +48,11 @@ from wtforms import (
     HiddenField,
     SelectMultipleField,
 )
+from app.formations import edit_module
 from app.models import Evaluation, ModuleImpl
 import app.scodoc.sco_utils as scu
 import app.scodoc.notesdb as ndb
 from app.scodoc import sco_preferences
-from app.scodoc import sco_edit_module
 from app.scodoc import sco_evaluations
 from app.scodoc import sco_excel
 from app.scodoc.sco_excel import ScoExcelBook, COLORS
@@ -243,7 +243,7 @@ class PlacementRunner:
         self.moduleimpl_data = sco_moduleimpl.moduleimpl_list(
             moduleimpl_id=self.moduleimpl_id
         )[0]
-        self.module_data = sco_edit_module.module_list(
+        self.module_data = edit_module.module_list(
             args={"module_id": self.moduleimpl_data["module_id"]}
         )[0]
         self.sem = sco_formsemestre.get_formsemestre(
diff --git a/app/scodoc/sco_tag_module.py b/app/scodoc/sco_tag_module.py
index bc5fd03cfb0fb558b95070e88c8da12924aeac0f..5dfc26a5554824b63e36986106cd91d2fe30d2f2 100644
--- a/app/scodoc/sco_tag_module.py
+++ b/app/scodoc/sco_tag_module.py
@@ -39,8 +39,8 @@ import re
 from flask import g
 
 from app import db, log
+from app.formations import edit_module
 from app.models import Formation, NotesTag
-from app.scodoc import sco_edit_module
 import app.scodoc.sco_utils as scu
 import app.scodoc.notesdb as ndb
 from app.scodoc.sco_exceptions import ScoValueError
@@ -265,7 +265,7 @@ def module_tag_set(module_id="", taglist=None):
     # TODO Voir ItemSuiviTag et api etud_suivi
 
     # Sanity check:
-    mod_dict = sco_edit_module.module_list(args={"module_id": module_id})
+    mod_dict = edit_module.module_list(args={"module_id": module_id})
     if not mod_dict:
         raise ScoValueError("invalid module !")
 
diff --git a/app/scodoc/sco_ue_external.py b/app/scodoc/sco_ue_external.py
index 6d529b28279569374a214fa7bbad5517297ab791..b795002936da6f1fc35be7e158a267cf3140c89c 100644
--- a/app/scodoc/sco_ue_external.py
+++ b/app/scodoc/sco_ue_external.py
@@ -56,15 +56,13 @@ Solution proposée (nov 2014):
 import flask
 from flask import flash, g, request, render_template, url_for
 from flask_login import current_user
+from app.formations import edit_matiere, edit_module, edit_ue
 from app.models.formsemestre import FormSemestre
 
 
 from app import db, log
 from app.models import Evaluation, Identite, ModuleImpl, UniteEns
 from app.scodoc import codes_cursus
-from app.scodoc import sco_edit_matiere
-from app.scodoc import sco_edit_module
-from app.scodoc import sco_edit_ue
 from app.scodoc import sco_moduleimpl
 from app.scodoc import sco_saisie_notes
 from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
@@ -97,10 +95,8 @@ def external_ue_create(
     #
     formation_id = formsemestre.formation.id
 
-    numero = sco_edit_ue.next_ue_numero(
-        formation_id, semestre_id=formsemestre.semestre_id
-    )
-    ue_id = sco_edit_ue.do_ue_create(
+    numero = edit_ue.next_ue_numero(formation_id, semestre_id=formsemestre.semestre_id)
+    ue_id = edit_ue.do_ue_create(
         {
             "formation_id": formation_id,
             "semestre_idx": formsemestre.semestre_id,
@@ -114,11 +110,11 @@ def external_ue_create(
     )
     ue = db.session.get(UniteEns, ue_id)
     flash(f"UE créée (code {ue.ue_code})")
-    matiere_id = sco_edit_matiere.do_matiere_create(
+    matiere_id = edit_matiere.do_matiere_create(
         {"ue_id": ue_id, "titre": titre or acronyme, "numero": 1}
     )
 
-    module_id = sco_edit_module.do_module_create(
+    module_id = edit_module.do_module_create(
         {
             "titre": "UE extérieure",
             "code": acronyme,
diff --git a/app/views/notes.py b/app/views/notes.py
index a9d8e3b7f894fb2ad566bd3d32e387bc260e4e40..4aa70730972b43c521448c6e4f38dbd2593c9b79 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -78,8 +78,14 @@ from app.decorators import (
     permission_required_compat_scodoc7,
 )
 
-
-# ---------------
+from app.formations import (
+    edit_formation,
+    edit_matiere,
+    edit_module,
+    edit_ue,
+    formation_io,
+    formation_versions,
+)
 from app.scodoc import sco_utils as scu
 
 from app.scodoc.sco_exceptions import (
@@ -97,10 +103,6 @@ from app.scodoc import (
     sco_cost_formation,
     sco_debouche,
     sco_edit_apc,
-    sco_edit_formation,
-    sco_edit_matiere,
-    sco_edit_module,
-    sco_edit_ue,
     sco_etape_apogee_view,
     sco_etud,
     sco_evaluations,
@@ -109,8 +111,6 @@ from app.scodoc import (
     sco_evaluation_edit,
     sco_evaluation_recap,
     sco_export_results,
-    sco_formations,
-    sco_formation_versions,
     sco_formsemestre,
     sco_formsemestre_custommenu,
     sco_formsemestre_edit,
@@ -192,7 +192,7 @@ sco_publish(
 )
 sco_publish(
     "/formsemestre_associate_new_version",
-    sco_formation_versions.formsemestre_associate_new_version,
+    formation_versions.formsemestre_associate_new_version,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
@@ -240,19 +240,19 @@ sco_publish(
 
 sco_publish(
     "/formation_create",
-    sco_edit_formation.formation_create,
+    edit_formation.formation_create,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
 sco_publish(
     "/formation_delete",
-    sco_edit_formation.formation_delete,
+    edit_formation.formation_delete,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
 sco_publish(
     "/formation_edit",
-    sco_edit_formation.formation_edit,
+    edit_formation.formation_edit,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
@@ -426,13 +426,13 @@ sco_publish(
 )
 sco_publish(
     "/ue_create",
-    sco_edit_ue.ue_create,
+    edit_ue.ue_create,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
 sco_publish(
     "/ue_delete",
-    sco_edit_ue.ue_delete,
+    edit_ue.ue_delete,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
@@ -443,7 +443,7 @@ sco_publish(
 @permission_required(Permission.EditFormation)
 def ue_edit(ue_id: int):
     "Edition de l'UE"
-    return sco_edit_ue.ue_edit(ue_id)
+    return edit_ue.ue_edit(ue_id)
 
 
 @bp.route("/set_ue_niveau_competence", methods=["POST"])
@@ -485,7 +485,7 @@ def get_ue_niveaux_options_html():
 @permission_required(Permission.ScoView)
 @scodoc7func
 def ue_table(formation_id=None, semestre_idx=1, msg=""):
-    return sco_edit_ue.ue_table(
+    return edit_ue.ue_table(
         formation_id=formation_id, semestre_idx=semestre_idx, msg=msg
     )
 
@@ -528,7 +528,7 @@ def ue_sharing_code():
     ue_code = request.args.get("ue_code")
     ue_id = request.args.get("ue_id")
     hide_ue_id = request.args.get("hide_ue_id")
-    return sco_edit_ue.ue_sharing_code(
+    return edit_ue.ue_sharing_code(
         ue_code=ue_code,
         ue_id=None if ((ue_id is None) or ue_id == "") else int(ue_id),
         hide_ue_id=(
@@ -562,46 +562,46 @@ sco_publish(
 )
 sco_publish(
     "/formation_add_malus_modules",
-    sco_edit_module.formation_add_malus_modules,
+    edit_module.formation_add_malus_modules,
     Permission.EditFormation,
 )
 sco_publish(
     "/matiere_create",
-    sco_edit_matiere.matiere_create,
+    edit_matiere.matiere_create,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
 sco_publish(
     "/matiere_delete",
-    sco_edit_matiere.matiere_delete,
+    edit_matiere.matiere_delete,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
 sco_publish(
     "/matiere_edit",
-    sco_edit_matiere.matiere_edit,
+    edit_matiere.matiere_edit,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
 sco_publish(
     "/module_create",
-    sco_edit_module.module_create,
+    edit_module.module_create,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
 sco_publish(
     "/module_delete",
-    sco_edit_module.module_delete,
+    edit_module.module_delete,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
 sco_publish(
     "/module_edit",
-    sco_edit_module.module_edit,
+    edit_module.module_edit,
     Permission.EditFormation,
     methods=["GET", "POST"],
 )
-sco_publish("/module_list", sco_edit_module.module_table, Permission.ScoView)
+sco_publish("/module_list", edit_module.module_table, Permission.ScoView)
 sco_publish("/module_tag_search", sco_tag_module.module_tag_search, Permission.ScoView)
 
 
@@ -670,7 +670,7 @@ def index_html():
     detail = scu.to_bool(request.args.get("detail", False))
 
     editable = current_user.has_permission(Permission.EditFormation)
-    table = sco_formations.formation_list_table(detail=detail)
+    table = formation_io.formation_list_table(detail=detail)
 
     if fmt != "html":
         return table.make_page(fmt=fmt, filename=f"Formations-{g.scodoc_dept}")
@@ -747,7 +747,7 @@ def index_html():
 @scodoc7func
 def formation_export(formation_id, export_ids=False, fmt=None, export_codes_apo=True):
     "Export de la formation au format indiqué (xml ou json)"
-    return sco_formations.formation_export(
+    return formation_io.formation_export(
         formation_id,
         export_ids=export_ids,
         fmt=fmt,
@@ -789,9 +789,7 @@ def formation_import_xml_form():
     elif tf[0] == -1:
         return flask.redirect(url_for("notes.index_html", scodoc_dept=g.scodoc_dept))
     else:
-        formation_id, _, _ = sco_formations.formation_import_xml(
-            tf[2]["xmlfile"].read()
-        )
+        formation_id, _, _ = formation_io.formation_import_xml(tf[2]["xmlfile"].read())
 
         return render_template(
             "sco_page_dept.j2",
@@ -813,8 +811,8 @@ def formation_import_xml_form():
         )
 
 
-sco_publish("/module_move", sco_edit_formation.module_move, Permission.EditFormation)
-sco_publish("/ue_move", sco_edit_formation.ue_move, Permission.EditFormation)
+sco_publish("/module_move", edit_formation.module_move, Permission.EditFormation)
+sco_publish("/ue_move", edit_formation.ue_move, Permission.EditFormation)
 
 
 @bp.route("/ue_clone", methods=["POST"])
@@ -2578,7 +2576,7 @@ def check_sem_integrity(formsemestre_id, fix=False):
     bad_sem = []
     formations_set = set()  # les formations mentionnées dans les UE et modules
     for modimpl in modimpls:
-        mod = sco_edit_module.module_list({"module_id": modimpl["module_id"]})[0]
+        mod = edit_module.module_list({"module_id": modimpl["module_id"]})[0]
         formations_set.add(mod["formation_id"])
         ue = UniteEns.query.get_or_404(mod["ue_id"])
         ue_dict = ue.to_dict()
diff --git a/app/views/notes_formsemestre.py b/app/views/notes_formsemestre.py
index e3e25c902552d1746a679de4393c873b5ecc7800..4a62df44bc70f608ab7cd9269fc3158a4d6dd9a5 100644
--- a/app/views/notes_formsemestre.py
+++ b/app/views/notes_formsemestre.py
@@ -41,6 +41,7 @@ from app.decorators import (
     scodoc,
     permission_required,
 )
+from app.formations import formation_io, formation_versions
 from app.forms.formsemestre import (
     change_formation,
     edit_modimpls_codes_apo,
@@ -55,8 +56,6 @@ from app.models import (
 )
 from app.scodoc import (
     sco_edt_cal,
-    sco_formations,
-    sco_formation_versions,
     sco_groups_view,
 )
 from app.scodoc.sco_exceptions import ScoValueError
@@ -78,7 +77,7 @@ def formsemestre_change_formation(formsemestre_id: int):
     existant.
     """
     formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
-    formation_dict = sco_formations.formation_export_dict(
+    formation_dict = formation_io.formation_export_dict(
         formsemestre.formation, export_external_ues=True, ue_reference_style="acronyme"
     )
     formations = [
@@ -87,7 +86,7 @@ def formsemestre_change_formation(formsemestre_id: int):
             dept_id=formsemestre.dept_id, acronyme=formsemestre.formation.acronyme
         )
         if formation.id != formsemestre.formation.id
-        and sco_formation_versions.formations_are_equals(
+        and formation_versions.formations_are_equals(
             formation, formation2_dict=formation_dict
         )
     ]
@@ -108,7 +107,7 @@ def formsemestre_change_formation(formsemestre_id: int):
                 new_formation: Formation = Formation.query.filter_by(
                     dept_id=g.scodoc_dept_id, formation_id=new_formation_id
                 ).first_or_404()
-                sco_formation_versions.formsemestre_change_formation(
+                formation_versions.formsemestre_change_formation(
                     formsemestre, new_formation
                 )
                 flash("Formation du semestre modifiée")
diff --git a/tests/scenarios/test_scenario1_formation.py b/tests/scenarios/test_scenario1_formation.py
index d0c1549eea85b89a3605a6b437c2bacc99d617a4..eb93f3d6bc9ba44456b06c2a92e7e7e67c12649f 100644
--- a/tests/scenarios/test_scenario1_formation.py
+++ b/tests/scenarios/test_scenario1_formation.py
@@ -12,8 +12,7 @@ Usage: pytest tests/scenarios/test_scenario1_formation.py
 # code écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en août 2021
 
 from tests.unit import sco_fake_gen
-from app.scodoc import sco_edit_module
-from app.scodoc import sco_formations
+from app.formations import edit_module, formation_io
 
 
 @pytest.mark.skip  # test obsolete
@@ -32,7 +31,7 @@ def run_scenario1():
         doc = f.read()
 
     # --- Création de la formation
-    f = sco_formations.formation_import_xml(doc=doc)
+    f = formation_io.formation_import_xml(doc=doc)
 
     # --- Création des semestres
     formation_id = f[0]
@@ -53,7 +52,7 @@ def run_scenario1():
     ]
 
     # --- Implémentation des modules
-    modules = sco_edit_module.module_list({"formation_id": formation_id})
+    modules = edit_module.module_list({"formation_id": formation_id})
     mods_imp = []
     for mod in modules:
         mi = G.create_moduleimpl(
diff --git a/tests/unit/sco_fake_gen.py b/tests/unit/sco_fake_gen.py
index 607b96e4865a435a12dbb9654f47f187677f4ba5..61bbb8f919069f435e5d566842229e6f41f3b223 100644
--- a/tests/unit/sco_fake_gen.py
+++ b/tests/unit/sco_fake_gen.py
@@ -16,6 +16,7 @@ import typing
 
 from app import db, log
 from app.auth.models import User
+from app.formations import edit_matiere, edit_module, edit_ue
 from app.models import (
     Departement,
     Evaluation,
@@ -26,9 +27,6 @@ from app.models import (
     ModuleImpl,
 )
 from app.scodoc import codes_cursus
-from app.scodoc import sco_edit_matiere
-from app.scodoc import sco_edit_module
-from app.scodoc import sco_edit_ue
 from app.scodoc import sco_formsemestre
 from app.scodoc import sco_formsemestre_inscriptions
 from app.scodoc import sco_formsemestre_validation
@@ -198,17 +196,17 @@ class ScoFake(object):
         return: ue_id
         """
         if numero is None:
-            numero = sco_edit_ue.next_ue_numero(formation_id, 0)
-        oid = sco_edit_ue.do_ue_create(locals())
-        oids = sco_edit_ue.ue_list(args={"ue_id": oid})
+            numero = edit_ue.next_ue_numero(formation_id, 0)
+        oid = edit_ue.do_ue_create(locals())
+        oids = edit_ue.ue_list(args={"ue_id": oid})
         if not oids:
             raise ScoValueError("ue not created !")
         return oid
 
     @logging_meth
     def create_matiere(self, ue_id=None, titre=None, numero=0) -> int:
-        oid = sco_edit_matiere.do_matiere_create(locals())
-        oids = sco_edit_matiere.matiere_list(args={"matiere_id": oid})
+        oid = edit_matiere.do_matiere_create(locals())
+        oids = edit_matiere.matiere_list(args={"matiere_id": oid})
         if not oids:
             raise ScoValueError("matiere not created !")
         return oid
@@ -233,8 +231,8 @@ class ScoFake(object):
         matiere = db.session.get(Matiere, matiere_id)
         ue_id = matiere.ue.id
         formation_id = matiere.ue.formation.id
-        oid = sco_edit_module.do_module_create(locals())
-        oids = sco_edit_module.module_list(args={"module_id": oid})
+        oid = edit_module.do_module_create(locals())
+        oids = edit_module.module_list(args={"module_id": oid})
         if not oids:
             raise ScoValueError(f"module not created ! (oid={oid})")
         return oid
diff --git a/tests/unit/test_formations.py b/tests/unit/test_formations.py
index 7be6f9ba9f76e361f9c346e43e47af1c8df4979a..66eea7867bb14966aab074d1a0040392bd81fc5c 100644
--- a/tests/unit/test_formations.py
+++ b/tests/unit/test_formations.py
@@ -48,13 +48,16 @@ import os
 import pytest
 
 from app import db
+from app.formations import (
+    edit_formation,
+    edit_matiere,
+    edit_module,
+    edit_ue,
+    formation_io,
+)
 from app.models import Formation, ModuleImpl
-from app.scodoc import sco_edit_formation, sco_formsemestre
-from app.scodoc import sco_edit_matiere
-from app.scodoc import sco_edit_module
-from app.scodoc import sco_edit_ue
+from app.scodoc import sco_formsemestre
 from app.scodoc import sco_exceptions
-from app.scodoc import sco_formations
 from app.scodoc import sco_formsemestre_edit
 from app.scodoc import sco_moduleimpl
 from app.views import notes
@@ -183,7 +186,7 @@ def test_formations(test_client):
     )
 
     # --- Export de formation vers JSON
-    exp = sco_formations.formation_export(
+    exp = formation_io.formation_export(
         formation_id=formation_id, fmt="json", export_ids=True
     ).get_data(as_text=True)
     assert isinstance(exp, str)
@@ -259,22 +262,22 @@ def test_formations(test_client):
         formsemestre_id=sem2["formsemestre_id"]
     )
 
-    li_module = sco_edit_module.module_list()
+    li_module = edit_module.module_list()
     assert len(li_module) == 4
     # Suppression impossible car utilisé dans le semestre formsemestre_idt:
     module3 = db.session.get(ModuleImpl, mi3).module
     with pytest.raises(sco_exceptions.ScoNonEmptyFormationObject):
-        sco_edit_module.module_delete(module_id=module3.id)
+        edit_module.module_delete(module_id=module3.id)
 
     sco_formsemestre_edit.do_formsemestre_delete(formsemestre_idt)
 
-    li_module2_before = sco_edit_module.module_list()
+    li_module2_before = edit_module.module_list()
 
-    sco_edit_module.do_module_delete(module3.id)
-    sco_edit_module.do_module_delete(module_id_t)
+    edit_module.do_module_delete(module3.id)
+    edit_module.do_module_delete(module_id_t)
 
     # deuxieme methode de supression d'un module
-    li_module2_after = sco_edit_module.module_list()
+    li_module2_after = edit_module.module_list()
 
     assert (
         len(li_module2_after) == len(li_module2_before) - 2
@@ -284,21 +287,21 @@ def test_formations(test_client):
 
     assert len(lim_sem2) == 0  # deuxieme vérification si le module s'est bien sup
 
-    li_mat = sco_edit_matiere.matiere_list()
+    li_mat = edit_matiere.matiere_list()
     assert len(li_mat) == 4
-    sco_edit_matiere.do_matiere_delete(oid=matiere_id3)  # on supprime la matiere
-    li_mat2 = sco_edit_matiere.matiere_list()
+    edit_matiere.do_matiere_delete(oid=matiere_id3)  # on supprime la matiere
+    li_mat2 = edit_matiere.matiere_list()
     assert len(li_mat2) == 3  # verification de la suppression de la matiere
 
-    li_ue = sco_edit_ue.ue_list()
+    li_ue = edit_ue.ue_list()
     assert len(li_ue) == 4
-    sco_edit_ue.ue_delete(ue_id=uet_id, dialog_confirmed=True)
-    li_ue2 = sco_edit_ue.ue_list()
+    edit_ue.ue_delete(ue_id=uet_id, dialog_confirmed=True)
+    li_ue2 = edit_ue.ue_list()
     assert len(li_ue2) == 3  # verification de la suppression de l'UE
 
     # --- Suppression d'une formation
 
-    sco_edit_formation.do_formation_delete(formation_id=formation_id2)
+    edit_formation.do_formation_delete(formation_id=formation_id2)
     formation = db.session.get(Formation, formation_id2)
     assert formation is None
 
@@ -315,11 +318,11 @@ def test_import_formation(test_client, filename="formation-exemple-1.xml"):
         doc = f.read()
 
     # --- Création de la formation
-    f = sco_formations.formation_import_xml(doc)
+    f = formation_io.formation_import_xml(doc)
     assert len(f) == 3  # 3-uple
     formation_id = f[0]
     # --- Vérification des UE
-    ues = sco_edit_ue.ue_list({"formation_id": formation_id})
+    ues = edit_ue.ue_list({"formation_id": formation_id})
     assert len(ues) == 10
     assert all(not ue["is_external"] for ue in ues)  # aucune UE externe dans le XML
     # --- Mise en place de 4 semestres
@@ -338,7 +341,7 @@ def test_import_formation(test_client, filename="formation-exemple-1.xml"):
         )
     ]
     # et les modules
-    modules = sco_edit_module.module_list({"formation_id": formation_id})
+    modules = edit_module.module_list({"formation_id": formation_id})
     for mod in modules:
         moduleimpl_id = G.create_moduleimpl(
             module_id=mod["module_id"],
@@ -348,7 +351,5 @@ def test_import_formation(test_client, filename="formation-exemple-1.xml"):
         assert mi["module_id"] == mod["module_id"]
 
     # --- Export formation en XML
-    doc1 = sco_formations.formation_export(formation_id, fmt="xml").get_data(
-        as_text=True
-    )
+    doc1 = formation_io.formation_export(formation_id, fmt="xml").get_data(as_text=True)
     assert isinstance(doc1, str)
diff --git a/tests/unit/test_formsemestre.py b/tests/unit/test_formsemestre.py
index 64a7b307d82c7f939e581acb72605aaedd1c0e58..9d0313624323dc79ab91f2741b21bee6e230178a 100644
--- a/tests/unit/test_formsemestre.py
+++ b/tests/unit/test_formsemestre.py
@@ -8,16 +8,15 @@ import pytest
 
 import app
 from app import db
+from app.formations import edit_ue, formation_versions
 from app.models import Formation, FormSemestre, FormSemestreDescription
 from app.scodoc import (
     sco_archives_formsemestre,
     sco_cost_formation,
     sco_debouche,
-    sco_edit_ue,
     sco_evaluations,
     sco_evaluation_check_abs,
     sco_evaluation_recap,
-    sco_formation_versions,
     sco_formsemestre_edit,
     sco_formsemestre_inscriptions,
     sco_formsemestre_status,
@@ -61,7 +60,7 @@ def test_formsemestres_associate_new_version(test_client):
     assert {s.semestre_id for s in formsemestres} == {1}
     # Les rattache à une nouvelle version de la formation:
     formsemestre_ids = [s.id for s in formsemestres]
-    sco_formation_versions.do_formsemestres_associate_new_version(
+    formation_versions.do_formsemestres_associate_new_version(
         formation.id, formsemestre_ids
     )
     new_formation: Formation = Formation.query.filter_by(
@@ -91,7 +90,7 @@ def test_formsemestre_misc_views(test_client):
 
     # ----- MENU SEMESTRE
     _ = sco_formsemestre_status.formsemestre_status(formsemestre_id=formsemestre.id)
-    _ = sco_edit_ue.ue_table(formsemestre.formation_id)
+    _ = edit_ue.ue_table(formsemestre.formation_id)
     _ = sco_formsemestre_edit.formsemestre_editwithmodules(formsemestre.id)
     _ = sco_preferences.SemPreferences(formsemestre_id=formsemestre.id).edit()
     _ = sco_formsemestre_edit.formsemestre_edit_options(formsemestre.id)
@@ -123,7 +122,7 @@ def test_formsemestre_misc_views(test_client):
     assert isinstance(ans, (str, Response))  # ici str
     # Juste la page dialogue avant opération::
     ans = sco_formsemestre_edit.formsemestre_clone(formsemestre.id)
-    ans = sco_formation_versions.formsemestre_associate_new_version(
+    ans = formation_versions.formsemestre_associate_new_version(
         formsemestre.formation_id, formsemestre.id
     )
     ans = sco_formsemestre_edit.formsemestre_delete(formsemestre.id)
diff --git a/tests/unit/yaml_setup.py b/tests/unit/yaml_setup.py
index d2040ea5cfd6763ba369c54691386b11c2e2042d..8bebe86aab065aa79160b3683f798c1b5cbeeedd 100644
--- a/tests/unit/yaml_setup.py
+++ b/tests/unit/yaml_setup.py
@@ -50,7 +50,7 @@ from flask import g
 from app import db
 
 from app.auth.models import User
-
+from app.formations import formation_io
 from app.models import (
     ApcParcours,
     DispenseUE,
@@ -63,7 +63,6 @@ from app.models import (
     UniteEns,
 )
 
-from app.scodoc import sco_formations
 from app.scodoc import sco_formsemestre_inscriptions
 from app.scodoc import sco_groups
 from app.scodoc import sco_saisie_notes
@@ -86,7 +85,7 @@ def setup_formation(formation_infos: dict) -> Formation:
         doc = f.read()
 
     # --- Création de la formation
-    formation_id, _, _ = sco_formations.formation_import_xml(doc)
+    formation_id, _, _ = formation_io.formation_import_xml(doc)
     formation: Formation = db.session.get(Formation, formation_id)
     assert formation
     return formation
diff --git a/tools/fakedatabase/create_test_api_database.py b/tools/fakedatabase/create_test_api_database.py
index f2e7463dff533f7ed780cdb5023e813232727081..b3ee1590ced5ee22c6f51af21b82be8bd5d1a11e 100644
--- a/tools/fakedatabase/create_test_api_database.py
+++ b/tools/fakedatabase/create_test_api_database.py
@@ -17,6 +17,7 @@ from app import db
 from app.auth.models import Role, User
 from app.but.import_refcomp import orebut_import_refcomp
 from app import models
+from app.formations import formation_io
 from app.models import departements
 from app.models import (
     Absence,
@@ -33,8 +34,6 @@ from app.models import (
 )
 from app.scodoc import (
     sco_cache,
-    sco_evaluation_db,
-    sco_formations,
     sco_formsemestre_inscriptions,
     sco_groups,
 )
@@ -68,7 +67,7 @@ def import_formation(dept_id: int) -> Formation:
     with open(FORMATION_XML_FILENAME, encoding="utf-8") as f:
         doc = f.read()
     # --- Création de la formation (import programme)
-    f = sco_formations.formation_import_xml(doc)
+    f = formation_io.formation_import_xml(doc)
     formation = db.session.get(Formation, f[0])
     # --- Association ref. comp.
     with open(REFCOMP_FILENAME, encoding="utf-8") as f: