diff --git a/app/models/evaluations.py b/app/models/evaluations.py
index 4eb738cf2c140a43812b0174c151f302cb5c4c11..d7c41030f14a44daeed715c42b434f8a2f802c3b 100644
--- a/app/models/evaluations.py
+++ b/app/models/evaluations.py
@@ -1,7 +1,6 @@
 # -*- coding: UTF-8 -*
 
-"""ScoDoc models: evaluations
-"""
+"""ScoDoc models: evaluations"""
 import datetime
 from operator import attrgetter
 
@@ -548,6 +547,25 @@ class Evaluation(models.ScoDocModel):
             .all()
         )
 
+    def get_bounds(self) -> tuple[int, int]:
+        """Calcule les notes min et max pour une évaluation."""
+        note_max = self.note_max or 0.0
+        module = self.moduleimpl.module
+        if module.module_type in (
+            scu.ModuleType.STANDARD,
+            scu.ModuleType.RESSOURCE,
+            scu.ModuleType.SAE,
+        ):
+            if self.evaluation_type == Evaluation.EVALUATION_BONUS:
+                note_min, note_max = -20, 20
+            else:
+                note_min = scu.NOTES_MIN
+        elif module.module_type == scu.ModuleType.MALUS:
+            note_min = -20.0
+        else:
+            raise ValueError("Invalid module type")  # bug
+        return note_min, note_max
+
 
 class EvaluationUEPoids(models.ScoDocModel):
     """Poids des évaluations (BUT)
diff --git a/app/scodoc/sco_page_etud.py b/app/scodoc/sco_page_etud.py
index eeac44d88533f2e24010eb2967c1b7da973fd962..9e45d0ba770377a92b5cf2693b25c04fc69432fa 100644
--- a/app/scodoc/sco_page_etud.py
+++ b/app/scodoc/sco_page_etud.py
@@ -108,7 +108,7 @@ def _menu_scolarite(
         and authuser.has_permission(Permission.EtudInscrit)
         and not locked
     )
-    note_enabled = etat_inscription == "I" and (
+    note_enabled = etat_inscription == scu.INSCRIT and (
         etat_inscription != scu.DEMISSION
         and (
             authuser.has_permission(Permission.EditAllNotes)
@@ -171,7 +171,7 @@ def _menu_scolarite(
             "enabled": authuser.has_permission(Permission.EtudInscrit),
         },
         {
-            "title": "Gérer les notes",
+            "title": "Editer toutes les notes",
             "endpoint": "notes.form_saisie_notes_par_etu",
             "args": {"etu_id": etudid, "semestre_id": formsemestre.id},
             "enabled": note_enabled,
diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py
index ea31e95380446ac4ad6ddfc9783a45590fb32291..0e65b1dca4c1bf90e65f5e68b3d069f52dbc3fd7 100644
--- a/app/scodoc/sco_saisie_notes.py
+++ b/app/scodoc/sco_saisie_notes.py
@@ -112,26 +112,6 @@ def convert_note_from_string(
     return note_value, invalid
 
 
-def get_bounds(evaluation: Evaluation) -> tuple[int, int]:
-    """Calcule les notes min et max pour une évaluation."""
-    note_max = evaluation.note_max or 0.0
-    module: Module = evaluation.moduleimpl.module
-    if module.module_type in (
-        scu.ModuleType.STANDARD,
-        scu.ModuleType.RESSOURCE,
-        scu.ModuleType.SAE,
-    ):
-        if evaluation.evaluation_type == Evaluation.EVALUATION_BONUS:
-            note_min, note_max = -20, 20
-        else:
-            note_min = scu.NOTES_MIN
-    elif module.module_type == ModuleType.MALUS:
-        note_min = -20.0
-    else:
-        raise ValueError("Invalid module type")  # bug
-    return note_min, note_max
-
-
 def check_notes(
     notes: list[(int, float | str)], evaluation: Evaluation
 ) -> tuple[list[tuple[int, float]], list[int], list[int], list[int], list[int]]:
@@ -149,7 +129,7 @@ def check_notes(
         etudids_non_inscrits : etudid non inscrits à ce module
                                 (ne considère pas l'inscr. au semestre)
     """
-    note_min, note_max = get_bounds(evaluation)
+    note_min, note_max = evaluation.get_bounds()
     # Vérifie inscription au module (même DEM/DEF)
     etudids_inscrits_mod = {
         i.etudid for i in evaluation.moduleimpl.query_inscriptions().all()
@@ -779,30 +759,28 @@ def saisie_notes(evaluation: Evaluation, group_ids: list[int] | tuple[int] = ())
 
 
 # US 1030 - DEB
-
-
 class DataSection:
     """Agrège les données sur un ensemble de module (Ressources, SAEs, Standard, Malus).
     Nécessaire pour garantir l'ordre de présentation pour les formations en apc.
     data_modmpl: la liste des modules de la section.
     """
 
-    def __init__(self, module_type):
+    def __init__(self, module_type: ModuleType):
         self.name = scu.MODULE_TYPE_NAMES[module_type]
         self.data_modimpl = []
 
-    def add_modimpl(self, modimpl):
+    def add_modimpl(self, modimpl: ModuleImpl):
         self.data_modimpl.append(modimpl)
 
 
 class DataModimpl:
-    """Aggrèges les données sur un module pour un étudiants donné.
-    # récupére les notes sur les années précédentes pour aider à la saisie des cursus adaptés (cursus)
-    # (sportifs, étalement sur plusieurs années, etc.).
-    data_evals: lal iste des évaluations du module.
+    """Aggrège les données sur un module pour un étudiant donné.
+    # TODO ?: récupére les notes sur les années précédentes pour aider à la saisie
+    # des cursus adaptés (cursus) (sportifs, étalement sur plusieurs années, etc.).
+    data_evals: la iste des évaluations du module.
     """
 
-    def __init__(self, etudiant: Identite, modimpl):
+    def __init__(self, etudiant: Identite, modimpl: ModuleImpl):
         self.etudiant = etudiant
         self.data_evals = []
         self.modimpl = modimpl
@@ -823,55 +801,111 @@ class DataModimpl:
         #     if ins.formsemestre.formation_id == formation_id:
         #         self.cursus[ins.formsemestre.id] = ins.formsemestre
 
-    def add_eval(self, data_eval):
+    def add_eval(self, data_eval: DataSection):
         self.data_evals.append(data_eval)
 
 
 class DataEval:
     """Récupère les données sur une évaluation appliquée à un étudiant"""
 
-    def __init__(self, etudid, evaluation):
+    def __init__(self, etudid: int, evaluation: Evaluation):
         self.etudid = etudid
         self.evaluation = evaluation
         self.evaluation_id = evaluation.id
-        self.note_min, self.note_max = get_bounds(evaluation)
-        self.data_debut = evaluation.date_fin
-        self.date_fin = evaluation.date_fin
         self.description = evaluation.description
-        self.ue_poids = evaluation.get_ue_poids_dict()
-        self.evaluation_type = evaluation.evaluation_type
-        self.note = NotesNotes.query.filter_by(
-            evaluation_id=self.evaluation.id, etudid=self.etudid
-        ).first()
-        self.note_value = "" if self.note is None else self.note.value
-        self.history = f"""<span id="formnotes_hist_{self.evaluation_id}">{
-        get_note_history_menu(evaluation.id, etudid)
-        }</span>"""
-
-    def forminput(self):
+        self.note_min, self.note_max = evaluation.get_bounds()
+        self.evaluation_type: EvaluationType = evaluation.evaluation_type
+        self.note = get_evaluation_etud_note(evaluation, etudid)
+        # self.data_debut = evaluation.date_debut
+        # self.date_fin = evaluation.date_fin
+        # self.ue_poids = evaluation.get_ue_poids_dict()
+        self.note_value = (
+            ""
+            if self.note is None
+            else scu.fmt_note(self.note.value, fixed_precision_str=False)
+        )
+
+    def titre(self) -> str:
+        """Retourne la chaine Html pour le titre d'une évaluation"""
+        eval_type: str = {
+            0: "",  # EVALUATION_NORMAL:
+            1: " [rattrapage]",  # EVALUATION_RATTRAPAGE
+            2: " [deuxième session]",  # EVALUATION_SESSION2
+            3: " [bonus]",  # EVALUATION_BONUS
+        }[self.evaluation_type]
+        return f"""
+        <a href="{self.link_to_eval()}">
+                    {self.description or "évaluation sans titre"}{eval_type}
+                    </a>
+        """
+
+    def forminput(self) -> str:
         return f"""<input type="text" size="5" id="formnotes_note_{self.evaluation_id}" 
         class="note" onkeypress="return enter_focus_next(this, event);" 
         note_min="{self.note_min}" note_max="{self.note_max}" evaluation_id="{self.evaluation_id}"
         original-value="{self.note_value}" value="{self.note_value}" />"""
 
+    def link_to_eval(self) -> str:
+        """Retourne l'url' vers une évaluation"""
+        return url_for(
+            "notes.evaluation_listenotes",
+            scodoc_dept=g.scodoc_dept,
+            evaluation_id=self.evaluation.id,
+        )
+
+    def history(self):
+        return f"""<span id="formnotes_hist_{self.evaluation_id}">
+        {get_note_history_menu(self.evaluation.id, self.etudid)}</span>"""
+
+
+class DataForm:
+    """
+    Class qui décrit les données d'une page de saisie par étudiant:
+    decision_jury: bool = indique si une décision de jury a été prise pour cet étudiant
+    data_sections: dict[ModuleTYpe, DataSection] = pour chaque section (ressource, SAE, Malus, Standard),
+                                                        la liste de ses modimpl concernés
+    """
+
+    def __init__(self, etudiant: Identite, semestre: FormSemestre, decision_jury: bool):
+        self.etudiant = etudiant
+        self.semestre = semestre
+        self.decision_jury = decision_jury
+        self.data_sections = {}
+
+    def add_section(self, data_section: DataSection):
+        self.data_sections.append(data_section)
 
-def get_data(semestre: FormSemestre, etudiant: Identite) -> dict:
-    """Récupère les infos pour un étudiant (cf ticket 1030)"""
+    def link_to_etudiant(self) -> str:
+        link: str = url_for(
+            "notes.formsemestre_bulletinetud",
+            scodoc_dept=g.scodoc_dept,
+            formsemestre_id=self.semestre.id,
+            etudid=self.etudiant.etudid,
+        )
+        return f"""
+        <a href="{link}">{self.etudiant.nomprenom}</a>
+        """
+
+
+def get_data(semestre: FormSemestre, etudiant: Identite) -> DataForm:
+    """Récupère les infos pour une page (donc un étudiant (cf ticket 1030))"""
     res = res_sem.load_formsemestre_results(semestre)
-    data = {
-        "decision_jury": res.etud_has_decision(etudiant.etudid, include_rcues=False),
-        "data_sections": {},
-    }
+    data = DataForm(
+        etudiant, semestre, res.etud_has_decision(etudiant.etudid, include_rcues=False)
+    )
     if semestre.formation.is_apc():
         for module_type in ModuleType:
-            data["data_sections"][module_type] = DataSection(module_type)
+            data.data_sections[module_type] = DataSection(module_type)
+    else:
+        for module_type in [ ModuleType.STANDARD, ModuleType.MALUS]:
+            data.data_sections[module_type] = DataSection(module_type)
     for modimpl in semestre.modimpls_sorted:
         module_type = modimpl.module.module_type
         if res.modimpl_inscr_df[modimpl.id][etudiant.id]:
             data_modimpl = DataModimpl(etudiant, modimpl)
             for evaluation in modimpl.evaluations:
                 data_modimpl.add_eval(DataEval(etudiant.etudid, evaluation))
-            data["data_sections"][module_type].add_modimpl(data_modimpl)
+            data.data_sections[module_type].add_modimpl(data_modimpl)
     return data
 
 
@@ -891,38 +925,44 @@ def saisie_notes_par_etu(semestre: FormSemestre, etudiant: Identite):
             inscription = ins
     # assert ins is not None
     if ins is None:
-        raise ValueError("Tentative d accès à une inscription inexistante")  # bug
-    if ins.etat != "I":
+        raise ScoValueError("Tentative d accès à une inscription inexistante")  # bug
+    if ins.etat != scu.INSCRIT:
         raise ValueError(
             "Etudiant démissionnaire ou défaillant"
         )  # ne devrait pas être accessible. bug
     "Formulaire de saisie de note centré sur l'étudiant"
     data = get_data(semestre, etudiant)
+    if semestre.formation.is_apc():
+        module_type_order = [
+            ModuleType.RESSOURCE,
+            ModuleType.SAE,
+            ModuleType.STANDARD,
+            ModuleType.MALUS,
+        ]
+    else:
+        module_type_order = [
+            ModuleType.STANDARD,
+            ModuleType.MALUS,
+        ]
     return render_template(
         "etud/saisie_notes_par_etu.j2",
         title="Saisie notes par étudiant",
         javascripts=["js/saisie_notes.js", "js/saisie_notes_par_etu.js"],
         sco=ScoData(formsemestre=semestre),
-        semestre=semestre,
-        etudiant=etudiant,
         data=data,
         readonly=readonly,
         get_note_history_menu=get_note_history_menu,
-        module_type_order=[
-            ModuleType.RESSOURCE,
-            ModuleType.SAE,
-            ModuleType.STANDARD,
-            ModuleType.MALUS,
-        ],
+        module_type_order=module_type_order,
     )
 
 
 # US 1030 - FIN
 
 
-def get_evaluation_etud_note(evaluation: Evaluation, etudid: int):
-    a = Notes.query.filter_by(id=evaluation.id, etudid=etudid).first()
-    pass
+def get_evaluation_etud_note(evaluation: Evaluation, etudid: int) -> float | str | None:
+    return NotesNotes.query.filter_by(
+        evaluation_id=evaluation.id, etudid=etudid
+    ).first()
 
 
 def get_sorted_etuds_notes(
diff --git a/app/static/js/saisie_notes.js b/app/static/js/saisie_notes.js
index 553c30016f7c4322a93da1c394244003d411dd94..b79ffffcc8f75b51499b169852ac280815817d15 100644
--- a/app/static/js/saisie_notes.js
+++ b/app/static/js/saisie_notes.js
@@ -111,10 +111,20 @@ function get_field_id(elt) {      // la forme des id est ????_#### (où #### est
   return elt.id.split('_').pop(); // formnotes_note_#### ou eval_####
 }
 
+function get_hist_field(field_id) {
+  return document.getElementById(hist_prefix + field_id);
+}
+
+function get_note_field(field_id) {
+  return document.getElementById(note_prefix + field_id);
+}
+
 function write_on_blur(elt) {
     if (elt.getAttribute("data-modified") === "true") {
-      valid_note.call(elt);
-      elt.setAttribute("data-modified", "false"); // Reset the modified flag
+        if (elt.value !== elt.getAttribute("data-last-saved-value")) {
+            valid_note.call(elt);
+            elt.setAttribute("data-modified", "false"); // Reset the modified flag
+        }
     }
 }
 
diff --git a/app/static/js/saisie_notes_par_etu.js b/app/static/js/saisie_notes_par_etu.js
index 5c907152298ad5b625a02b99a8bd76395c6f5635..da7dfe0b5f9a1b8513afc911c5726d5e839ea691 100644
--- a/app/static/js/saisie_notes_par_etu.js
+++ b/app/static/js/saisie_notes_par_etu.js
@@ -21,14 +21,6 @@ function get_evaluation_id(field_id) {
   return parseInt(note_field.getAttribute("evaluation_id"));
 }
 
-function get_note_field(field_id) {
-  return document.getElementById(note_prefix + field_id);
-}
-
-function get_hist_field(field_id) {
-  return document.getElementById(hist_prefix + field_id);
-}
-
 function get_etudid(field_id) {
   return parseInt(form_etudid);
 }
diff --git a/app/static/js/saisie_notes_par_eval.js b/app/static/js/saisie_notes_par_eval.js
index d4e226661f6df7e0aad961a0ad769bf0ee5ff567..4c188e35c26069c2e5ba0dba30a05b2eb4c30532 100644
--- a/app/static/js/saisie_notes_par_eval.js
+++ b/app/static/js/saisie_notes_par_eval.js
@@ -12,14 +12,6 @@ function get_note_max(field_id) {
   return note_max;
 }
 
-function get_note_field(field_id) {
-  return document.getElementById(note_prefix + field_id);
-}
-
-function get_hist_field(field_id) {
-  return document.getElementById(hist_prefix + field_id);
-}
-
 function get_etudid(field_id) {
   return parseInt(get_note_field(field_id).getAttribute("data-etudid"));
 }
@@ -29,9 +21,7 @@ document.addEventListener("DOMContentLoaded", function () {
   noteInputs.forEach(function (input) {
     console.log(get_field_id(input));
     input.addEventListener("input", function() {
-      if (this.value !== this.getAttribute("data-last-saved-value")) {
-        this.setAttribute("data-modified", "true");
-      }
+      this.setAttribute("data-modified", "true");
     });
     input.addEventListener("blur", input.addEventListener("blur", function(event){write_on_blur(event.currentTarget)} ));
   });
diff --git a/app/templates/etud/saisie_notes_par_etu.j2 b/app/templates/etud/saisie_notes_par_etu.j2
index b4f55ba97d8be2ea0b1a7dc8b7bfeae470e0f391..b0c6d6948cfb6bc228862096c462a73f1cfd2b04 100644
--- a/app/templates/etud/saisie_notes_par_etu.j2
+++ b/app/templates/etud/saisie_notes_par_etu.j2
@@ -34,8 +34,8 @@
 {% block app_content %}
     <div id="saisie_notes_par_etu" class="table-container">
         <span class="titre">{{title}}</span>
-        <h3> Saisie des notes de l'étudiant : {{etudiant.nom}} {{etudiant.prenom}} </h3>
-        {% if data["decision_jury"] %}
+        <h3> Saisie des notes de l'étudiant : {{data.link_to_etudiant() | safe }} </h3>
+        {% if data.decision_jury %}
         <div class="warning">
             <ul class="tf-msg">
             <li class="tf-msg">Attention: il y a déjà des <b>décisions de jury</b> enregistrées pour
@@ -44,38 +44,38 @@
         </div>
         {% endif %}
         <div id="formnotes">
-            <input type="hidden" id="etudiant_id" value="{{etudiant.id}}" />
-            <input type="hidden" id="formnotes_formsemestre_id" value="{{semestre.id}}" />
-            {% if data["decision_jury"] %}
+            <input type="hidden" id="etudiant_id" value="{{data.etudiant.id}}" />
+            <input type="hidden" id="formnotes_formsemestre_id" value="{{data.semestre.id}}" />
+            {% if data.decision_jury %}
             {% endif %}
             <div class="label">commentaire</div>
             <div class="long_input">
                 <input type="text" id="formnotes_comment" name="comment" class="input" size="44" onkeypress="return enter_focus_next(this, event);">
             </div>
             {% for module_type in module_type_order %}
-                {%  if data["data_sections"][module_type].data_modimpl %}
-                    <div class="section">{{ data["data_sections"][module_type].name }}</div>
-                    {% for data_modimpl in data["data_sections"][module_type].data_modimpl %}
+                {%  if data.data_sections[module_type].data_modimpl %}
+                    <div class="section">{{ data.data_sections[module_type].name }}</div>
+                    {% for data_modimpl in data.data_sections[module_type].data_modimpl %}
                        <div class="module">{{data_modimpl.link | safe}}</div>
                        {% for data_eval in data_modimpl.data_evals %}
-                            <div class="label">{{ data_eval.description }}
-                                <input type="hidden" id="date_debut_{{ data_eval.evaluation_id }}" value="{{ data_eval.date_debut }}">
-                                <input type="hidden" id="date_fin_{{ data_eval.evaluation_id }}" value="{{ data_eval.date_fin }}">
-                                <input type="hidden" id="evaluation_type_{{ data_eval.evaluation_id }}" value="{{ data_eval.evaluation_type }}">
-                                    {% for ue in data_eval.ue_poids %}
-                                        <div id="poids_{{ data_eval.evaluation_id }}_{{ ue }}">
-                                            <input type="hidden" class="ue" value="{{ ue }}">
-                                            <input type="hidden" class="evaluation" value="{{ data_eval.evaluation_id }}">
-                                            <input type="hidden" value="{{ data_eval.ue_poids[ue] }}">
-                                        </div>
-                                    {% endfor %}
-                                </input>
+                            <div class="label">
+                                {{ data_eval.titre() | safe }}
+{#                                <input type="hidden" id="date_debut_{{ data_eval.evaluation_id }}" value="{{ data_eval.date_debut }}">#}
+{#                                <input type="hidden" id="date_fin_{{ data_eval.evaluation_id }}" value="{{ data_eval.date_fin }}">#}
+{#                                    {% for ue in data_eval.ue_poids %}#}
+{#                                        <div id="poids_{{ data_eval.evaluation_id }}_{{ ue }}">#}
+{#                                            <input type="hidden" class="ue" value="{{ ue }}">#}
+{#                                            <input type="hidden" class="evaluation" value="{{ data_eval.evaluation_id }}">#}
+{#                                            <input type="hidden" value="{{ data_eval.ue_poids[ue] }}">#}
+{#                                        </div>#}
+{#                                    {% endfor %}#}
+{#                                </input>#}
                             </div>
                             <div class="input">
                                 {{ data_eval.forminput() | safe  }}
                             </div>
                             <div class="history">
-                                {{ data_eval.history | safe }}
+                                {{ data_eval.history() | safe }}
                             </div>
                        {%  endfor %}
                     {%  endfor %}
@@ -87,7 +87,7 @@
             style="pointer-events: unset;
        color: unset; text-decoration: unset;
        cursor: unset;">Terminer</a>
-            {% if data["decision_jury"] %}
+            {% if data.decision_jury %}
             <span id="jurylink" class="jurylink"></span>
             {% endif %}
         </div>