diff --git a/app/static/js/table_recap.js b/app/static/js/table_recap.js
index 56d43e3d9a35d2e396c99094f364983946405b57..11a5db4d4685c927c378aaa1f65b27f78c6ed628 100644
--- a/app/static/js/table_recap.js
+++ b/app/static/js/table_recap.js
@@ -1,285 +1,311 @@
 // Tableau recap notes
 $(function () {
-    $(function () {
-        if ($('table.table_recap').length == 0) { return; }
+  $(function () {
+    if ($("table.table_recap").length == 0) {
+      return;
+    }
 
-        let hidden_colums = [
-            "etud_codes", "identite_detail",
-            "partition_aux", "partition_rangs", "admission",
-            "col_empty"
-        ];
-        // Etat (tri des colonnes) de la table:
+    let hidden_colums = [
+      "etud_codes",
+      "identite_detail",
+      "partition_aux",
+      "partition_rangs",
+      "admission",
+      "col_empty",
+    ];
+    // Etat (tri des colonnes) de la table:
 
-        const url = new URL(document.URL);
-        const formsemestre_id = url.searchParams.get("formsemestre_id");
-        const order_info_key = JSON.stringify([url.pathname, formsemestre_id]);
-        const etudids_key = JSON.stringify(["etudids", url.origin, formsemestre_id]);
-        const noms_key = JSON.stringify(["noms", url.origin, formsemestre_id]);
-        let order_info;
-        if (formsemestre_id) {
-            const x = localStorage.getItem(order_info_key);
-            if (x) {
-                try {
-                    order_info = JSON.parse(x);
-                } catch (error) {
-                    console.error(error);
-                }
-            }
-        }
-
-        // Les colonnes visibles sont mémorisées, il faut initialiser l'état des boutons
-        function update_buttons_labels(dt) {
-            // chaque bouton controle une classe stockée dans le data-group du span
-            document.querySelectorAll("button.dt-button").forEach(but => {
-                let g_span = but.querySelector("span > span");
-                if (g_span) {
-                    let group = g_span.dataset["group"];
-                    if (group) {
-                        // si le group (= la 1ere col.) est visible, but_on
-                        if (dt.columns("." + group).visible()[0]) {
-                            but.classList.add("but_on");
-                            but.classList.remove("but_off");
-                        } else {
-                            but.classList.add("but_off");
-                            but.classList.remove("but_on");
-                        }
-                    }
-                }
-            });
+    const url = new URL(document.URL);
+    const formsemestre_id = url.searchParams.get("formsemestre_id");
+    const order_info_key = JSON.stringify([url.pathname, formsemestre_id]);
+    const etudids_key = JSON.stringify([
+      "etudids",
+      url.origin,
+      formsemestre_id,
+    ]);
+    const noms_key = JSON.stringify(["noms", url.origin, formsemestre_id]);
+    let order_info;
+    if (formsemestre_id) {
+      const x = localStorage.getItem(order_info_key);
+      if (x) {
+        try {
+          order_info = JSON.parse(x);
+        } catch (error) {
+          console.error(error);
         }
+      }
+    }
 
-        // Changement visibilité groupes colonnes (boutons)
-        function toggle_col_but_visibility(e, dt, node, config) {
-            let group = node.children()[0].firstChild.dataset.group;
-            toggle_col_group_visibility(dt, group, node.hasClass("but_on"));
-        }
-        function toggle_col_ident_visibility(e, dt, node, config) {
-            let onoff = node.hasClass("but_on");
-            toggle_col_group_visibility(dt, "identite_detail", onoff);
-            toggle_col_group_visibility(dt, "identite_court", !onoff);
-        }
-        function toggle_col_ressources_visibility(e, dt, node, config) {
-            let onoff = node.hasClass("but_on");
-            toggle_col_group_visibility(dt, "col_res", onoff);
-            toggle_col_group_visibility(dt, "col_ue_bonus", onoff);
-            toggle_col_group_visibility(dt, "col_malus", onoff);
-        }
-        function toggle_col_group_visibility(dt, group, onoff) {
-            if (onoff) {
-                dt.columns('.' + group).visible(false);
+    // Les colonnes visibles sont mémorisées, il faut initialiser l'état des boutons
+    function update_buttons_labels(dt) {
+      // chaque bouton controle une classe stockée dans le data-group du span
+      document.querySelectorAll("button.dt-button").forEach((but) => {
+        let g_span = but.querySelector("span > span");
+        if (g_span) {
+          let group = g_span.dataset["group"];
+          if (group) {
+            // si le group (= la 1ere col.) est visible, but_on
+            if (dt.columns("." + group).visible()[0]) {
+              but.classList.add("but_on");
+              but.classList.remove("but_off");
             } else {
-                dt.columns('.' + group).visible(true);
+              but.classList.add("but_off");
+              but.classList.remove("but_on");
             }
-            update_buttons_labels(dt);
+          }
         }
-        // Definition des boutons au dessus de la table:
-        let buttons = [
-            {
-                extend: 'copyHtml5',
-                text: 'Copier',
-                exportOptions: { orthogonal: 'export' }
-            },
-            {
-                extend: 'excelHtml5',
-                // footer: true, // ne fonctionne pas ?
-                exportOptions: { orthogonal: 'export' },
-                title: document.querySelector('table.table_recap').dataset.filename
-            },
-            {
-                // force affichage de toutes les colonnes
-                text: '<a title="Afficher toutes les colonnes">&#10036;</a>',
-                action: function (e, dt, node, config) {
-                    dt.columns().visible(true);
-                    update_buttons_labels(dt);
-                }
-            },
-            {
-                text: '<a title="Rétablir l\'affichage par défaut" class="clearreload">&#128260;</a>',
-                action: function (e, dt, node, config) {
-                    localStorage.clear();
-                    console.log("cleared localStorage");
-                    location.reload();
-                }
-            },
-            {
-                text: '<span data-group="identite_detail">Civilité</span>',
-                action: toggle_col_ident_visibility,
-            },
-            {
-                text: '<span data-group="partition_aux"><a title="Affichage des groupes secondaires (la première partition est toujours affichée)">Groupes</a></span>',
-                action: toggle_col_but_visibility,
-            },
-            {
-                text: '<span data-group="partition_rangs"><a title="Rangs dans les groupes (si activés dans les partitions concernées)">Rg</a></span>',
-                action: toggle_col_but_visibility,
-            },
-        ]; // fin des boutons communs à toutes les tables recap
+      });
+    }
 
-        if ($('table.table_recap').hasClass("jury")) {
-            // Table JURY: 
-            // avec ou sans codes enregistrés
-            buttons.push(
-                {
-                    text: '<span data-group="recorded_code">Codes jury</span>',
-                    action: toggle_col_but_visibility,
-                });
-            if ($('table.table_recap').hasClass("apc")) {
-                // Boutons spécifiques à la table JURY BUT
-                buttons.push(
-                    {
-                        text: '<span data-group="cursus_but">Compétences</span>',
-                        action: toggle_col_but_visibility,
-                    });
-                buttons.push(
-                    {
-                        text: '<span data-group="col_rcue">RCUEs</span>',
-                        action: toggle_col_but_visibility,
-                    });
-            }
-        } else {
-            // BOUTONS SPECIFIQUES A LA TABLE RECAP NON JURY
-            buttons.push(
-                $('table.table_recap').hasClass("apc") ?
-                    {
-                        text: '<span data-group="col_res">Ressources</span>',
-                        action: toggle_col_ressources_visibility,
-                    } : {
-                        name: "toggle_mod",
-                        text: "Cacher les modules",
-                        action: function (e, dt, node, config) {
-                            let onoff = node.hasClass("but_on");
-                            toggle_col_group_visibility(dt, "col_mod:not(.col_empty)", onoff);
-                            toggle_col_group_visibility(dt, "col_ue_bonus", onoff);
-                            toggle_col_group_visibility(dt, "col_malus", onoff);
-                        }
-                    }
-            );
-            if ($('table.table_recap').hasClass("apc")) {
-                buttons.push({
-                    text: '<span data-group="col_sae">SAÉs</span>',
-                    action: toggle_col_but_visibility,
-                });
-            }
-            // S'il y a des colonnes vides:
-            if ($('table.table_recap td.col_empty').length > 0) {
-                buttons.push({ // modules vides
-                    text: '<span data-group="col_empty">Vides</span>',
-                    action: toggle_col_but_visibility,
-                });
+    // Changement visibilité groupes colonnes (boutons)
+    function toggle_col_but_visibility(e, dt, node, config) {
+      let group = node.children()[0].firstChild.dataset.group;
+      toggle_col_group_visibility(dt, group, node.hasClass("but_on"));
+    }
+    function toggle_col_ident_visibility(e, dt, node, config) {
+      let onoff = node.hasClass("but_on");
+      toggle_col_group_visibility(dt, "identite_detail", onoff);
+      toggle_col_group_visibility(dt, "identite_court", !onoff);
+    }
+    function toggle_col_ressources_visibility(e, dt, node, config) {
+      let onoff = node.hasClass("but_on");
+      toggle_col_group_visibility(dt, "col_res", onoff);
+      toggle_col_group_visibility(dt, "col_ue_bonus", onoff);
+      toggle_col_group_visibility(dt, "col_malus", onoff);
+    }
+    function toggle_col_group_visibility(dt, group, onoff) {
+      if (onoff) {
+        dt.columns("." + group).visible(false);
+      } else {
+        dt.columns("." + group).visible(true);
+      }
+      update_buttons_labels(dt);
+    }
+    // Definition des boutons au dessus de la table:
+    let buttons = [
+      {
+        extend: "copyHtml5",
+        text: "Copier",
+        exportOptions: { orthogonal: "export" },
+      },
+      {
+        extend: "excelHtml5",
+        // footer: true, // ne fonctionne pas ?
+        exportOptions: { orthogonal: "export" },
+        title: document.querySelector("table.table_recap").dataset.filename,
+      },
+      {
+        // force affichage de toutes les colonnes
+        text: '<a title="Afficher toutes les colonnes">&#10036;</a>',
+        action: function (e, dt, node, config) {
+          dt.columns().visible(true);
+          update_buttons_labels(dt);
+        },
+      },
+      {
+        text: '<a title="Rétablir l\'affichage par défaut" class="clearreload">&#128260;</a>',
+        action: function (e, dt, node, config) {
+          localStorage.clear();
+          console.log("cleared localStorage");
+          location.reload();
+        },
+      },
+      {
+        text: '<span data-group="identite_detail">Civilité</span>',
+        action: toggle_col_ident_visibility,
+      },
+      {
+        text: '<span data-group="partition_aux"><a title="Affichage des groupes secondaires (la première partition est toujours affichée)">Groupes</a></span>',
+        action: toggle_col_but_visibility,
+      },
+      {
+        text: '<span data-group="partition_rangs"><a title="Rangs dans les groupes (si activés dans les partitions concernées)">Rg</a></span>',
+        action: toggle_col_but_visibility,
+      },
+    ]; // fin des boutons communs à toutes les tables recap
+
+    if ($("table.table_recap").hasClass("jury")) {
+      // Table JURY:
+      // avec ou sans codes enregistrés
+      buttons.push({
+        text: '<span data-group="recorded_code">Codes jury</span>',
+        action: toggle_col_but_visibility,
+      });
+      if ($("table.table_recap").hasClass("apc")) {
+        // Boutons spécifiques à la table JURY BUT
+        buttons.push({
+          text: '<span data-group="cursus_but">Compétences</span>',
+          action: toggle_col_but_visibility,
+        });
+        buttons.push({
+          text: '<span data-group="col_rcue">RCUEs</span>',
+          action: toggle_col_but_visibility,
+        });
+      }
+    } else {
+      // BOUTONS SPECIFIQUES A LA TABLE RECAP NON JURY
+      buttons.push(
+        $("table.table_recap").hasClass("apc")
+          ? {
+              text: '<span data-group="col_res">Ressources</span>',
+              action: toggle_col_ressources_visibility,
             }
-            // Boutons admission (pas en jury)
-            if (!$('table.table_recap').hasClass("jury")) {
-                buttons.push(
-                    {
-                        text: '<span data-group="admission">Admission</span>',
-                        action: toggle_col_but_visibility,
-                    }
+          : {
+              name: "toggle_mod",
+              text: "Cacher les modules",
+              action: function (e, dt, node, config) {
+                let onoff = node.hasClass("but_on");
+                toggle_col_group_visibility(
+                  dt,
+                  "col_mod:not(.col_empty)",
+                  onoff
                 );
+                toggle_col_group_visibility(dt, "col_ue_bonus", onoff);
+                toggle_col_group_visibility(dt, "col_malus", onoff);
+              },
             }
-        }
-        // Boutons évaluations (si présentes)
-        if ($('table.table_recap').hasClass("with_evaluations")) {
-            buttons.push(
-                {
-                    text: '<span data-group="eval">Évaluations</span>',
-                    action: toggle_col_but_visibility,
-                }
-            );
-        }
-
-        // ------------- LA TABLE ---------
-        try {
-            let table = $('table.table_recap').DataTable(
-                {
-                    paging: false,
-                    searching: true,
-                    info: false,
-                    autoWidth: false,
-                    fixedHeader: {
-                        header: true,
-                        footer: false
-                    },
-                    orderCellsTop: true, // cellules ligne 1 pour tri 
-                    aaSorting: [], // Prevent initial sorting
-                    colReorder: true,
-                    stateSave: true, // enregistre état de la table (tris, ...)
-                    "columnDefs": [
-                        {
-                            // cache les codes, le détail de l'identité, les groupes, les colonnes admission et les vides
-                            targets: hidden_colums,
-                            visible: false,
-                        },
-                        {
-                            // Elimine les 0 à gauche pour les exports excel et les "copy"
-                            targets: ["col_mod", "col_moy_gen", "col_moy_ue", "col_res", "col_sae", "evaluation", "col_rcue"],
-                            render: function (data, type, row) {
-                                return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data;
-                            }
-                        },
-                        {
-                            // Elimine les "+"" pour les exports
-                            targets: ["col_ue_bonus", "col_malus"],
-                            render: function (data, type, row) {
-                                return type === 'export' ? data.replace(/.*\+(\d?\d?\.\d\d).*/m, '$1').replace(/0(\d\..*)/, '$1') : data;
-                            }
-                        },
-                        {
-                            // Elimine emoji warning sur UEs
-                            targets: ["col_ues_validables"],
-                            render: function (data, type, row) {
-                                return type === 'export' ? data.replace(/(\d+\/\d+).*/, '$1') : data;
-                            }
-                        }
+      );
+      if ($("table.table_recap").hasClass("apc")) {
+        buttons.push({
+          text: '<span data-group="col_sae">SAÉs</span>',
+          action: toggle_col_but_visibility,
+        });
+      }
+      // S'il y a des colonnes vides:
+      if ($("table.table_recap td.col_empty").length > 0) {
+        buttons.push({
+          // modules vides
+          text: '<span data-group="col_empty">Vides</span>',
+          action: toggle_col_but_visibility,
+        });
+      }
+      // Boutons admission (pas en jury)
+      if (!$("table.table_recap").hasClass("jury")) {
+        buttons.push({
+          text: '<span data-group="admission">Admission</span>',
+          action: toggle_col_but_visibility,
+        });
+      }
+    }
+    // Boutons évaluations (si présentes)
+    if ($("table.table_recap").hasClass("with_evaluations")) {
+      buttons.push({
+        text: '<span data-group="eval">Évaluations</span>',
+        action: toggle_col_but_visibility,
+      });
+    }
 
-                    ],
-                    dom: 'Bfrtip',
-                    buttons: buttons,
-                    "drawCallback": function (settings) {
-                        // permet de conserver l'ordre de tri des colonnes
-                        let table = $('table.table_recap').DataTable();
-                        let order_info = JSON.stringify(table.order());
-                        if (formsemestre_id) {
-                            localStorage.setItem(order_info_key, order_info);
-                        }
-                        let etudids = [];
-                        document.querySelectorAll("td.identite_court").forEach(e => {
-                            etudids.push(e.dataset.etudid);
-                        });
-                        let noms = [];
-                        document.querySelectorAll("td.identite_court").forEach(e => {
-                            noms.push(e.dataset.nomprenom);
-                        });
-                        localStorage.setItem(etudids_key, JSON.stringify(etudids));
-                        localStorage.setItem(noms_key, JSON.stringify(noms));
-                    },
-                    "order": order_info,
-                }
-            );
-            update_buttons_labels(table);
-        } catch (error) {
-            // l'erreur peut etre causee par un ancien storage:
-            localStorage.removeItem(etudids_key);
-            localStorage.removeItem(noms_key);
-            localStorage.removeItem(order_info_key);
-            location.reload();
-        }
-    });
-    $('table.table_recap tbody').on('click', 'tr', function () {
-        if ($(this).hasClass('selected')) {
-            $(this).removeClass('selected');
-        }
-        else {
-            $('table.table_recap tr.selected').removeClass('selected');
-            $(this).addClass('selected');
-        }
-    });
-    // Pour montrer et surligner l'étudiant sélectionné:
-    $(function () {
-        let row_selected = document.querySelector(".row_selected");
-        if (row_selected) {
-            row_selected.scrollIntoView();
-            window.scrollBy(0, -125);
-            row_selected.classList.add("selected");
-        }
-    });
+    // ------------- LA TABLE ---------
+    try {
+      let table = $("table.table_recap").DataTable({
+        paging: false,
+        searching: true,
+        info: false,
+        autoWidth: false,
+        fixedHeader: {
+          header: true,
+          footer: false,
+        },
+        orderCellsTop: true, // cellules ligne 1 pour tri
+        aaSorting: [], // Prevent initial sorting
+        colReorder: true,
+        stateSave: true, // enregistre état de la table (tris, ...)
+        columnDefs: [
+          {
+            // cache les codes, le détail de l'identité, les groupes, les colonnes admission et les vides
+            targets: hidden_colums,
+            visible: false,
+          },
+          {
+            // Elimine les 0 à gauche pour les exports excel et les "copy"
+            targets: [
+              "col_mod",
+              "col_moy_gen",
+              "col_moy_ue",
+              "col_res",
+              "col_sae",
+              "evaluation",
+              "col_rcue",
+            ],
+            render: function (data, type, row) {
+              return type === "export" ? data.replace(/0(\d\..*)/, "$1") : data;
+            },
+          },
+          {
+            // Elimine les "+"" pour les exports
+            targets: ["col_ue_bonus", "col_malus"],
+            render: function (data, type, row) {
+              return type === "export"
+                ? data
+                    .replace(/.*\+(\d?\d?\.\d\d).*/m, "$1")
+                    .replace(/0(\d\..*)/, "$1")
+                : data;
+            },
+          },
+          {
+            // Elimine emoji warning sur UEs
+            targets: ["col_ues_validables"],
+            render: function (data, type, row) {
+              return type === "export"
+                ? data.replace(/(\d+\/\d+).*/, "$1")
+                : data;
+            },
+          },
+        ],
+        dom: "Bfrtip",
+        buttons: buttons,
+        drawCallback: function (settings) {
+          // permet de conserver l'ordre de tri des colonnes
+          let table = $("table.table_recap").DataTable();
+          let order_info = JSON.stringify(table.order());
+          if (formsemestre_id) {
+            localStorage.setItem(order_info_key, order_info);
+          }
+          let etudids = [];
+          document.querySelectorAll("td.identite_court").forEach((e) => {
+            etudids.push(e.dataset.etudid);
+          });
+          let noms = [];
+          document.querySelectorAll("td.identite_court").forEach((e) => {
+            noms.push(e.dataset.nomprenom);
+          });
+          localStorage.setItem(etudids_key, JSON.stringify(etudids));
+          localStorage.setItem(noms_key, JSON.stringify(noms));
+        },
+        order: order_info,
+      });
+      update_buttons_labels(table);
+    } catch (error) {
+      // l'erreur peut etre causee par un ancien storage:
+      localStorage.removeItem(etudids_key);
+      localStorage.removeItem(noms_key);
+      localStorage.removeItem(order_info_key);
+      location.reload();
+    }
+  });
+  $("table.table_recap tbody").on("click", "tr", function () {
+    if ($(this).hasClass("selected")) {
+      $(this).removeClass("selected");
+    } else {
+      $("table.table_recap tr.selected").removeClass("selected");
+      $(this).addClass("selected");
+    }
+  });
+  // Pour montrer et surligner l'étudiant sélectionné:
+  $(function () {
+    let row_selected = document.querySelector(".row_selected");
+    if (row_selected) {
+      row_selected.scrollIntoView();
+      window.scrollBy(0, -125);
+      row_selected.classList.add("selected");
+    }
+  });
+  // Ajoute bulle aide sur colonne RCUEs
+  $(function () {
+    // explication colonne RCUEs
+    let th = document.querySelector(
+      "table.table_recap.apc th.col_rcues_validables"
+    );
+    th.title = "RCUEs validables avec ces notes";
+  });
 });