diff --git a/app/api/departements.py b/app/api/departements.py
index 95a9c4e9a67a3f923c981fee1d0a222eb63e1218..7214032d15e6842e2214fde458547d87edd8e674 100644
--- a/app/api/departements.py
+++ b/app/api/departements.py
@@ -7,7 +7,7 @@
 """
   ScoDoc 9 API : accès aux départements
 
-  Note: les routes /departement[s] sont publiées sur l'API (/ScoDoc/api/), 
+  Note: les routes /departement[s] sont publiées sur l'API (/ScoDoc/api/),
   mais évidemment pas sur l'API web (/ScoDoc/<dept>/api).
 """
 from datetime import datetime
@@ -271,23 +271,11 @@ def dept_formsemestres_courants(acronym: str):
     """
     dept = Departement.query.filter_by(acronym=acronym).first_or_404()
     date_courante = request.args.get("date_courante")
-    if date_courante:
-        test_date = datetime.fromisoformat(date_courante)
-    else:
-        test_date = app.db.func.now()
-    # Les semestres en cours de ce département
-    formsemestres = FormSemestre.query.filter(
-        FormSemestre.dept_id == dept.id,
-        FormSemestre.date_debut <= test_date,
-        FormSemestre.date_fin >= test_date,
-    )
+    date_courante = datetime.fromisoformat(date_courante) if date_courante else None
     return [
-        d.to_dict_api()
-        for d in formsemestres.order_by(
-            FormSemestre.date_debut.desc(),
-            FormSemestre.modalite,
-            FormSemestre.semestre_id,
-            FormSemestre.titre,
+        formsemestre.to_dict_api()
+        for formsemestre in FormSemestre.get_dept_formsemestres_courants(
+            dept, date_courante
         )
     ]
 
diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py
index 6bfbbdbb2b6d6e40f3a2970bd4cd5e6889ebfd2a..10875cbff6d64c19844b3964c2b48743ff6d52a8 100644
--- a/app/models/formsemestre.py
+++ b/app/models/formsemestre.py
@@ -30,6 +30,7 @@ from app.models.but_refcomp import (
     parcours_formsemestre,
 )
 from app.models.config import ScoDocSiteConfig
+from app.models.departements import Departement
 from app.models.etudiants import Identite
 from app.models.evaluations import Evaluation
 from app.models.formations import Formation
@@ -521,7 +522,7 @@ class FormSemestre(db.Model):
         mois_pivot_periode=scu.MONTH_DEBUT_PERIODE2,
         jour_pivot_annee=1,
         jour_pivot_periode=1,
-    ):
+    ) -> tuple[int, int]:
         """Calcule la session associée à un formsemestre commençant en date_debut
         sous la forme (année, période)
             année: première année de l'année scolaire
@@ -571,6 +572,26 @@ class FormSemestre(db.Model):
             mois_pivot_periode=ScoDocSiteConfig.get_month_debut_periode2(),
         )
 
+    @classmethod
+    def get_dept_formsemestres_courants(
+        cls, dept: Departement, date_courante: datetime.datetime | None = None
+    ) -> db.Query:
+        """Liste (query) ordonnée des formsemestres courants, c'est
+        à dire contenant la date courant (si None, la date actuelle)"""
+        date_courante = date_courante or db.func.now()
+        # Les semestres en cours de ce département
+        formsemestres = FormSemestre.query.filter(
+            FormSemestre.dept_id == dept.id,
+            FormSemestre.date_debut <= date_courante,
+            FormSemestre.date_fin >= date_courante,
+        )
+        return formsemestres.order_by(
+            FormSemestre.date_debut.desc(),
+            FormSemestre.modalite,
+            FormSemestre.semestre_id,
+            FormSemestre.titre,
+        )
+
     def etapes_apo_vdi(self) -> list[ApoEtapeVDI]:
         "Liste des vdis"
         # was read_formsemestre_etapes
diff --git a/app/scodoc/sco_dept.py b/app/scodoc/sco_dept.py
index eacfe0439e0f6d0a614c73f76aeafb5c5a7816e7..b81ea09f5df895b85e47c2bab316d94099950d3f 100644
--- a/app/scodoc/sco_dept.py
+++ b/app/scodoc/sco_dept.py
@@ -149,21 +149,49 @@ def index_html(showcodes=0, showsemtable=0):
         </p>"""
     )
     #
-    if current_user.has_permission(Permission.EtudInscrit):
-        H.append(
-            """<hr>
+    H.append(
+        """<hr>
         <h3>Gestion des étudiants</h3>
         <ul>
-        <li><a class="stdlink" href="etudident_create_form">créer <em>un</em> nouvel étudiant</a>
+    """
+    )
+    if current_user.has_permission(Permission.EtudInscrit):
+        H.append(
+            f"""
+        <li><a class="stdlink" href="{
+                url_for("scolar.etudident_create_form", scodoc_dept=g.scodoc_dept)
+            }">créer <em>un</em> nouvel étudiant</a>
         </li>
-        <li><a class="stdlink" href="form_students_import_excel">importer de nouveaux étudiants</a>
-            (ne pas utiliser sauf cas particulier, utilisez plutôt le lien dans
+        <li><a class="stdlink" href="{
+                url_for("scolar.form_students_import_excel", scodoc_dept=g.scodoc_dept)
+            }">importer de nouveaux étudiants</a>
+            (<em>ne pas utiliser</em> sauf cas particulier&nbsp;: utilisez plutôt le lien dans
             le tableau de bord semestre si vous souhaitez inscrire les
             étudiants importés à un semestre)
         </li>
-        </ul>
         """
         )
+    H.append(
+        f"""
+        <li><a class="stdlink" href="{
+                url_for("scolar.export_etudiants_courants", scodoc_dept=g.scodoc_dept)
+            }">exporter tableau des étudiants des semestres en cours</a>
+        </li>
+        """
+    )
+    if current_user.has_permission(
+        Permission.EtudInscrit
+    ) and sco_preferences.get_preference("portal_url"):
+        H.append(
+            f"""
+            <li><a class="stdlink" href="{
+                url_for("scolar.formsemestre_import_etud_admission",
+                        scodoc_dept=g.scodoc_dept, tous_courants=1)
+            }">resynchroniser les données étudiants des semestres en cours depuis le portail</a>
+            </li>
+        """
+        )
+    H.append("</ul>")
     #
     if current_user.has_permission(Permission.EditApogee):
         H.append(
diff --git a/app/scodoc/sco_synchro_etuds.py b/app/scodoc/sco_synchro_etuds.py
index e220b84311bf2d387e323320f458deb77846253a..da4813f3f538e07f7c537d67ddd3f81ec27dbb71 100644
--- a/app/scodoc/sco_synchro_etuds.py
+++ b/app/scodoc/sco_synchro_etuds.py
@@ -793,21 +793,25 @@ def update_etape_formsemestre_inscription(ins, etud):
 
 
 def formsemestre_import_etud_admission(
-    formsemestre_id, import_identite=True, import_email=False
-):
+    formsemestre_id: int, import_identite=True, import_email=False
+) -> tuple[list[Identite], list[Identite], list[tuple[Identite, str]]]:
     """Tente d'importer les données admission depuis le portail
     pour tous les étudiants du semestre.
     Si  import_identite==True, recopie l'identité (nom/prenom/sexe/date_naissance)
     de chaque étudiant depuis le portail.
     N'affecte pas les etudiants inconnus sur le portail.
+    Renvoie:
+    - etuds_no_nip: liste d'étudiants sans code NIP
+    - etuds_unknown: etudiants avec NIP mais inconnus du portail
+    - changed_mails: (etudiant, old_mail) pour ceux dont le mail a changé
     """
     sem = sco_formsemestre.get_formsemestre(formsemestre_id)
     ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
         {"formsemestre_id": formsemestre_id}
     )
     log(f"formsemestre_import_etud_admission: {formsemestre_id} ({len(ins)} etuds)")
-    no_nip = []  # liste d'etudids sans code NIP
-    unknowns = []  # etudiants avec NIP mais inconnus du portail
+    etuds_no_nip: list[Identite] = []
+    etuds_unknown: list[Identite] = []
     changed_mails: list[tuple[Identite, str]] = []  # modification d'adresse mails
 
     # Essaie de recuperer les etudiants des étapes, car
@@ -828,7 +832,7 @@ def formsemestre_import_etud_admission(
         etud: Identite = Identite.query.get_or_404(etudid)
         code_nip = etud.code_nip
         if not code_nip:
-            no_nip.append(etudid)
+            etuds_no_nip.append(etud)
         else:
             data_apo = apo_etuds.get(code_nip)
             if not data_apo:
@@ -865,7 +869,7 @@ def formsemestre_import_etud_admission(
                         if adresse.email != data_apo["mail"]:
                             changed_mails.append((etud, old_mail))
             else:
-                unknowns.append(code_nip)
+                etuds_unknown.append(etud)
     db.session.commit()
     sco_cache.invalidate_formsemestre(formsemestre_id=sem["formsemestre_id"])
-    return no_nip, unknowns, changed_mails
+    return etuds_no_nip, etuds_unknown, changed_mails
diff --git a/app/static/css/gt_table.css b/app/static/css/gt_table.css
index f4b644df4ab733677cb1d6676befbe49d1ecd87c..e3d3246bd916adea39efd562efb5840af6c00c71 100644
--- a/app/static/css/gt_table.css
+++ b/app/static/css/gt_table.css
@@ -1,4 +1,4 @@
-/* 
+/*
  * DataTables style for ScoDoc gen_tables
  * generated using https://datatables.net/manual/styling/theme-creator
  * and customized by hand
@@ -138,111 +138,111 @@ table.dataTable.display tbody tr:hover.selected {
   background-color: #a9b7d1;
 }
 
-table.dataTable.order-column tbody tr>.sorting_1,
-table.dataTable.order-column tbody tr>.sorting_2,
-table.dataTable.order-column tbody tr>.sorting_3,
-table.dataTable.display tbody tr>.sorting_1,
-table.dataTable.display tbody tr>.sorting_2,
-table.dataTable.display tbody tr>.sorting_3 {
+table.dataTable.order-column tbody tr > .sorting_1,
+table.dataTable.order-column tbody tr > .sorting_2,
+table.dataTable.order-column tbody tr > .sorting_3,
+table.dataTable.display tbody tr > .sorting_1,
+table.dataTable.display tbody tr > .sorting_2,
+table.dataTable.display tbody tr > .sorting_3 {
   background-color: #f9f9f9;
 }
 
-table.dataTable.order-column tbody tr.selected>.sorting_1,
-table.dataTable.order-column tbody tr.selected>.sorting_2,
-table.dataTable.order-column tbody tr.selected>.sorting_3,
-table.dataTable.display tbody tr.selected>.sorting_1,
-table.dataTable.display tbody tr.selected>.sorting_2,
-table.dataTable.display tbody tr.selected>.sorting_3 {
+table.dataTable.order-column tbody tr.selected > .sorting_1,
+table.dataTable.order-column tbody tr.selected > .sorting_2,
+table.dataTable.order-column tbody tr.selected > .sorting_3,
+table.dataTable.display tbody tr.selected > .sorting_1,
+table.dataTable.display tbody tr.selected > .sorting_2,
+table.dataTable.display tbody tr.selected > .sorting_3 {
   background-color: #acbad4;
 }
 
-table.dataTable.display tbody tr.odd>.sorting_1,
-table.dataTable.order-column.stripe tbody tr.odd>.sorting_1 {
+table.dataTable.display tbody tr.odd > .sorting_1,
+table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
   background-color: #f1f1f1;
 }
 
-table.dataTable.display tbody tr.odd>.sorting_2,
-table.dataTable.order-column.stripe tbody tr.odd>.sorting_2 {
+table.dataTable.display tbody tr.odd > .sorting_2,
+table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
   background-color: #f3f3f3;
 }
 
-table.dataTable.display tbody tr.odd>.sorting_3,
-table.dataTable.order-column.stripe tbody tr.odd>.sorting_3 {
+table.dataTable.display tbody tr.odd > .sorting_3,
+table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
   background-color: whitesmoke;
 }
 
-table.dataTable.display tbody tr.odd.selected>.sorting_1,
-table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1 {
+table.dataTable.display tbody tr.odd.selected > .sorting_1,
+table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
   background-color: #a6b3cd;
 }
 
-table.dataTable.display tbody tr.odd.selected>.sorting_2,
-table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2 {
+table.dataTable.display tbody tr.odd.selected > .sorting_2,
+table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
   background-color: #a7b5ce;
 }
 
-table.dataTable.display tbody tr.odd.selected>.sorting_3,
-table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3 {
+table.dataTable.display tbody tr.odd.selected > .sorting_3,
+table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
   background-color: #a9b6d0;
 }
 
-table.dataTable.display tbody tr.even>.sorting_1,
-table.dataTable.order-column.stripe tbody tr.even>.sorting_1 {
+table.dataTable.display tbody tr.even > .sorting_1,
+table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
   background-color: #f9f9f9;
 }
 
-table.dataTable.display tbody tr.even>.sorting_2,
-table.dataTable.order-column.stripe tbody tr.even>.sorting_2 {
+table.dataTable.display tbody tr.even > .sorting_2,
+table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
   background-color: #fbfbfb;
 }
 
-table.dataTable.display tbody tr.even>.sorting_3,
-table.dataTable.order-column.stripe tbody tr.even>.sorting_3 {
+table.dataTable.display tbody tr.even > .sorting_3,
+table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
   background-color: #fdfdfd;
 }
 
-table.dataTable.display tbody tr.even.selected>.sorting_1,
-table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1 {
+table.dataTable.display tbody tr.even.selected > .sorting_1,
+table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
   background-color: #acbad4;
 }
 
-table.dataTable.display tbody tr.even.selected>.sorting_2,
-table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2 {
+table.dataTable.display tbody tr.even.selected > .sorting_2,
+table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
   background-color: #adbbd6;
 }
 
-table.dataTable.display tbody tr.even.selected>.sorting_3,
-table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3 {
+table.dataTable.display tbody tr.even.selected > .sorting_3,
+table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
   background-color: #afbdd8;
 }
 
-table.dataTable.display tbody tr:hover>.sorting_1,
-table.dataTable.order-column.hover tbody tr:hover>.sorting_1 {
+table.dataTable.display tbody tr:hover > .sorting_1,
+table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {
   background-color: #eaeaea;
 }
 
-table.dataTable.display tbody tr:hover>.sorting_2,
-table.dataTable.order-column.hover tbody tr:hover>.sorting_2 {
+table.dataTable.display tbody tr:hover > .sorting_2,
+table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {
   background-color: #ebebeb;
 }
 
-table.dataTable.display tbody tr:hover>.sorting_3,
-table.dataTable.order-column.hover tbody tr:hover>.sorting_3 {
+table.dataTable.display tbody tr:hover > .sorting_3,
+table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {
   background-color: #eeeeee;
 }
 
-table.dataTable.display tbody tr:hover.selected>.sorting_1,
-table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1 {
+table.dataTable.display tbody tr:hover.selected > .sorting_1,
+table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {
   background-color: #a1aec7;
 }
 
-table.dataTable.display tbody tr:hover.selected>.sorting_2,
-table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2 {
+table.dataTable.display tbody tr:hover.selected > .sorting_2,
+table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {
   background-color: #a2afc8;
 }
 
-table.dataTable.display tbody tr:hover.selected>.sorting_3,
-table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3 {
+table.dataTable.display tbody tr:hover.selected > .sorting_3,
+table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {
   background-color: #a4b2cb;
 }
 
@@ -419,7 +419,13 @@ table.dataTable td {
   color: #333333 !important;
   border: 1px solid #979797;
   background-color: white;
-  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, gainsboro));
+  background: -webkit-gradient(
+    linear,
+    left top,
+    left bottom,
+    color-stop(0%, white),
+    color-stop(100%, gainsboro)
+  );
   /* Chrome,Safari4+ */
   background: -webkit-linear-gradient(top, white 0%, gainsboro 100%);
   /* Chrome10+,Safari5.1+ */
@@ -447,7 +453,13 @@ table.dataTable td {
   color: white !important;
   border: 1px solid #111111;
   background-color: #585858;
-  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111111));
+  background: -webkit-gradient(
+    linear,
+    left top,
+    left bottom,
+    color-stop(0%, #585858),
+    color-stop(100%, #111111)
+  );
   /* Chrome,Safari4+ */
   background: -webkit-linear-gradient(top, #585858 0%, #111111 100%);
   /* Chrome10+,Safari5.1+ */
@@ -464,7 +476,13 @@ table.dataTable td {
 .dataTables_wrapper .dataTables_paginate .paginate_button:active {
   outline: none;
   background-color: #2b2b2b;
-  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));
+  background: -webkit-gradient(
+    linear,
+    left top,
+    left bottom,
+    color-stop(0%, #2b2b2b),
+    color-stop(100%, #0c0c0c)
+  );
   /* Chrome,Safari4+ */
   background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
   /* Chrome10+,Safari5.1+ */
@@ -495,12 +513,50 @@ table.dataTable td {
   text-align: center;
   font-size: 1.2em;
   background-color: white;
-  background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
-  background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
-  background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
-  background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
-  background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
-  background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
+  background: -webkit-gradient(
+    linear,
+    left top,
+    right top,
+    color-stop(0%, rgba(255, 255, 255, 0)),
+    color-stop(25%, rgba(255, 255, 255, 0.9)),
+    color-stop(75%, rgba(255, 255, 255, 0.9)),
+    color-stop(100%, rgba(255, 255, 255, 0))
+  );
+  background: -webkit-linear-gradient(
+    left,
+    rgba(255, 255, 255, 0) 0%,
+    rgba(255, 255, 255, 0.9) 25%,
+    rgba(255, 255, 255, 0.9) 75%,
+    rgba(255, 255, 255, 0) 100%
+  );
+  background: -moz-linear-gradient(
+    left,
+    rgba(255, 255, 255, 0) 0%,
+    rgba(255, 255, 255, 0.9) 25%,
+    rgba(255, 255, 255, 0.9) 75%,
+    rgba(255, 255, 255, 0) 100%
+  );
+  background: -ms-linear-gradient(
+    left,
+    rgba(255, 255, 255, 0) 0%,
+    rgba(255, 255, 255, 0.9) 25%,
+    rgba(255, 255, 255, 0.9) 75%,
+    rgba(255, 255, 255, 0) 100%
+  );
+  background: -o-linear-gradient(
+    left,
+    rgba(255, 255, 255, 0) 0%,
+    rgba(255, 255, 255, 0.9) 25%,
+    rgba(255, 255, 255, 0.9) 75%,
+    rgba(255, 255, 255, 0) 100%
+  );
+  background: linear-gradient(
+    to right,
+    rgba(255, 255, 255, 0) 0%,
+    rgba(255, 255, 255, 0.9) 25%,
+    rgba(255, 255, 255, 0.9) 75%,
+    rgba(255, 255, 255, 0) 100%
+  );
 }
 
 .dataTables_wrapper .dataTables_length,
@@ -520,17 +576,69 @@ table.dataTable td {
   -webkit-overflow-scrolling: touch;
 }
 
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th,
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td,
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th,
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td {
+.dataTables_wrapper
+  .dataTables_scroll
+  div.dataTables_scrollBody
+  > table
+  > thead
+  > tr
+  > th,
+.dataTables_wrapper
+  .dataTables_scroll
+  div.dataTables_scrollBody
+  > table
+  > thead
+  > tr
+  > td,
+.dataTables_wrapper
+  .dataTables_scroll
+  div.dataTables_scrollBody
+  > table
+  > tbody
+  > tr
+  > th,
+.dataTables_wrapper
+  .dataTables_scroll
+  div.dataTables_scrollBody
+  > table
+  > tbody
+  > tr
+  > td {
   vertical-align: middle;
 }
 
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th>div.dataTables_sizing,
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td>div.dataTables_sizing,
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th>div.dataTables_sizing,
-.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td>div.dataTables_sizing {
+.dataTables_wrapper
+  .dataTables_scroll
+  div.dataTables_scrollBody
+  > table
+  > thead
+  > tr
+  > th
+  > div.dataTables_sizing,
+.dataTables_wrapper
+  .dataTables_scroll
+  div.dataTables_scrollBody
+  > table
+  > thead
+  > tr
+  > td
+  > div.dataTables_sizing,
+.dataTables_wrapper
+  .dataTables_scroll
+  div.dataTables_scrollBody
+  > table
+  > tbody
+  > tr
+  > th
+  > div.dataTables_sizing,
+.dataTables_wrapper
+  .dataTables_scroll
+  div.dataTables_scrollBody
+  > table
+  > tbody
+  > tr
+  > td
+  > div.dataTables_sizing {
   height: 0;
   overflow: hidden;
   margin: 0 !important;
@@ -541,8 +649,8 @@ table.dataTable td {
   border-bottom: 1px solid #111111;
 }
 
-.dataTables_wrapper.no-footer div.dataTables_scrollHead>table,
-.dataTables_wrapper.no-footer div.dataTables_scrollBody>table {
+.dataTables_wrapper.no-footer div.dataTables_scrollHead > table,
+.dataTables_wrapper.no-footer div.dataTables_scrollBody > table {
   border-bottom: none;
 }
 
@@ -555,7 +663,6 @@ table.dataTable td {
 }
 
 @media screen and (max-width: 767px) {
-
   .dataTables_wrapper .dataTables_info,
   .dataTables_wrapper .dataTables_paginate {
     float: none;
@@ -568,7 +675,6 @@ table.dataTable td {
 }
 
 @media screen and (max-width: 640px) {
-
   .dataTables_wrapper .dataTables_length,
   .dataTables_wrapper .dataTables_filter {
     float: none;
@@ -580,8 +686,7 @@ table.dataTable td {
   }
 }
 
-
-/* ------ Ajouts spécifiques pour ScoDoc: 
+/* ------ Ajouts spécifiques pour ScoDoc:
  */
 
 table.gt_table td {
@@ -624,7 +729,6 @@ table.dataTable.stripe.hover tbody tr.odd:hover td,
 table.dataTable.stripe.hover tbody tr.odd:hover td.sorting_1,
 table.dataTable.order-column.stripe.hover tbody tr.odd:hover td.sorting_1 {
   background-color: rgb(80%, 85%, 80%);
-  ;
 }
 
 /* Lignes paires */
@@ -638,7 +742,6 @@ table.dataTable.stripe.hover tbody tr.even:hover td,
 table.dataTable.stripe.hover tbody tr.even:hover td.sorting_1,
 table.dataTable.order-column.stripe.hover tbody tr.even:hover td.sorting_1 {
   background-color: rgb(85%, 85%, 85%);
-  ;
 }
 
 /* Reglage largeur de la table */
@@ -652,4 +755,9 @@ table.dataTable.gt_table {
 /* Tables non centrées (inutile) */
 table.dataTable.gt_table.gt_left {
   margin-left: 16px;
-}
\ No newline at end of file
+}
+table.dataTable.gt_table.gt_left td,
+table.dataTable.gt_table.gt_left th {
+  text-align: left;
+}
+scodoc;css
diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css
index f96efa8b4b16f17ef7b153557f8ba5eccf3ccb80..97d195715f37397178a008035e2e08710b2b3d01 100644
--- a/app/static/css/scodoc.css
+++ b/app/static/css/scodoc.css
@@ -1133,7 +1133,8 @@ a.redlink:hover {
   text-decoration: underline;
 }
 
-a.discretelink {
+a.discretelink,
+a:discretelink:visited {
   color: black;
   text-decoration: none;
 }
@@ -1567,6 +1568,7 @@ h2.formsemestre,
 #gtrcontent h2 {
   margin-top: 2px;
   font-size: 130%;
+  font-weight: bold;
 }
 
 .formsemestre_page_title table.semtitle,
diff --git a/app/tables/list_etuds.py b/app/tables/list_etuds.py
index c559e932669d97d64272e0d48f870f76029edb9f..b520e50d2cac80b03da1d7203fcdb43fb8344f8d 100644
--- a/app/tables/list_etuds.py
+++ b/app/tables/list_etuds.py
@@ -7,8 +7,7 @@
 """Liste simple d'étudiants
 """
 
-from flask import g, url_for
-from app.models import Identite
+from app.models import FormSemestre, FormSemestreInscription, Identite
 from app.tables import table_builder as tb
 
 
@@ -26,6 +25,7 @@ class TableEtud(tb.Table):
         with_foot_titles=False,
         **kwargs,
     ):
+        etuds = etuds or []
         self.rows: list["RowEtud"] = []  # juste pour que VSCode nous aide sur .rows
         classes = classes or ["gt_table", "gt_left"]
         super().__init__(
@@ -46,10 +46,12 @@ class TableEtud(tb.Table):
 
 class RowEtud(tb.Row):
     "Ligne de la table d'étudiants"
+
     # pour le moment très simple, extensible (codes, liens bulletins, ...)
     def __init__(self, table: TableEtud, etud: Identite, *args, **kwargs):
         super().__init__(table, etud.id, *args, **kwargs)
         self.etud = etud
+        self.target_url = etud.url_fiche()
 
     def add_etud_cols(self):
         """Ajoute colonnes étudiant: codes, noms"""
@@ -85,7 +87,7 @@ class RowEtud(tb.Row):
             etud.nom_disp(),
             "identite_detail",
             data={"order": etud.sort_key},
-            target=url_bulletin,
+            target=self.target_url,
             target_attrs={"class": "etudinfo discretelink", "id": str(etud.id)},
         )
         self.add_cell("prenom", "Prénom", etud.prenom, "identite_detail")
@@ -115,3 +117,70 @@ def html_table_etuds(etudids) -> str:
     etuds = etuds_sorted_from_ids(etudids)
     table = TableEtud(etuds)
     return table.html()
+
+
+class RowEtudWithInfos(RowEtud):
+    """Ligne de la table d'étudiants avec plus d'informations:
+    département, formsemestre, codes, boursier
+    """
+
+    def __init__(
+        self,
+        table: TableEtud,
+        etud: Identite,
+        formsemestre: FormSemestre,
+        inscription: FormSemestreInscription,
+        *args,
+        **kwargs,
+    ):
+        super().__init__(table, etud, *args, **kwargs)
+        self.formsemestre = formsemestre
+        self.inscription = inscription
+
+    def add_etud_cols(self):
+        """Ajoute colonnes étudiant: codes, noms"""
+        self.add_cell("dept", "Dépt.", self.etud.departement.acronym, "identite_detail")
+        self.add_cell(
+            "formsemestre",
+            "Semestre",
+            f"""{self.formsemestre.titre_formation()} {
+                            ('S'+str(self.formsemestre.semestre_id))
+                            if self.formsemestre.semestre_id > 0 else ''}
+                        """,
+            "identite_detail",
+        )
+        super().add_etud_cols()
+        self.add_cell(
+            "etat",
+            "État",
+            self.inscription.etat,
+            "inscription",
+        )
+        self.add_cell(
+            "boursier",
+            "Boursier",
+            "O" if self.etud.boursier else "N",
+            "identite_infos",
+        )
+
+
+class TableEtudWithInfos(TableEtud):
+    """Table d'étudiants avec formsemestre et inscription"""
+
+    def add_formsemestre(self, formsemestre: FormSemestre):
+        "Ajoute les étudiants de ce semestre à la table"
+        etuds = formsemestre.get_inscrits(order=True, include_demdef=True)
+        for etud in etuds:
+            row = self.row_class(
+                self, etud, formsemestre, formsemestre.etuds_inscriptions.get(etud.id)
+            )
+            row.add_etud_cols()
+            self.add_row(row)
+
+
+def table_etudiants_courants(formsemestres: list[FormSemestre]) -> TableEtud:
+    """Table des étudiants des formsemestres indiqués"""
+    table = TableEtudWithInfos(row_class=RowEtudWithInfos)
+    for formsemestre in formsemestres:
+        table.add_formsemestre(formsemestre)
+    return table
diff --git a/app/views/scolar.py b/app/views/scolar.py
index ac908ce7687867e622c887ba5f021b9e64ecb822..958d7b0a0a798565b6ee39f88106c40b7b6d35cd 100644
--- a/app/views/scolar.py
+++ b/app/views/scolar.py
@@ -105,6 +105,7 @@ from app.scodoc import sco_synchro_etuds
 from app.scodoc import sco_trombino
 from app.scodoc import sco_trombino_tours
 from app.scodoc import sco_up_to_date
+from app.tables import list_etuds
 
 
 def sco_publish(route, function, permission, methods=["GET"]):
@@ -2071,6 +2072,21 @@ def check_group_apogee(group_id, etat=None, fix=False, fixmail=False):
     return "\n".join(H) + html_sco_header.sco_footer()
 
 
+@bp.route("/export_etudiants_courants")
+@scodoc
+@permission_required(Permission.ScoView)
+def export_etudiants_courants():
+    """Table export de tous les étudiants des formsemestres en cours."""
+    departement = Departement.query.get(g.scodoc_dept_id)
+    if not departement:
+        raise ScoValueError("département invalide")
+    formsemestres = FormSemestre.get_dept_formsemestres_courants(departement)
+    table = list_etuds.table_etudiants_courants(formsemestres)
+    return render_template(
+        "scolar/export_etudiants_courants.j2", sco=ScoData(), table=table
+    )
+
+
 @bp.route("/form_students_import_excel", methods=["GET", "POST"])
 @scodoc
 @permission_required(Permission.EtudInscrit)
@@ -2361,35 +2377,57 @@ def form_students_import_infos_admissions(formsemestre_id=None):
 @scodoc
 @permission_required(Permission.EtudChangeAdr)
 @scodoc7func
-def formsemestre_import_etud_admission(formsemestre_id, import_email=True):
-    """Ré-importe donnees admissions par synchro Portail Apogée"""
-    (
-        no_nip,
-        unknowns,
-        changed_mails,
-    ) = sco_synchro_etuds.formsemestre_import_etud_admission(
-        formsemestre_id, import_identite=True, import_email=import_email
-    )
-    H = [
-        html_sco_header.html_sem_header("Ré-import données admission"),
-        "<h3>Opération effectuée</h3>",
-    ]
-    if no_nip:
-        H.append("<p>Attention: étudiants sans NIP: " + str(no_nip) + "</p>")
-    if unknowns:
-        H.append(
-            "<p>Attention: étudiants inconnus du portail: codes NIP="
-            + str(unknowns)
-            + "</p>"
+def formsemestre_import_etud_admission(
+    formsemestre_id=None, import_email=True, tous_courants=False
+):
+    """Ré-importe donnees admissions par synchro Portail Apogée.
+    Si tous_courants, le fait pour tous les formsemestres courants du département
+    """
+    if tous_courants:
+        departement = Departement.query.get(g.scodoc_dept_id)
+        formsemestres = FormSemestre.get_dept_formsemestres_courants(departement)
+    else:
+        formsemestres = [FormSemestre.get_formsemestre(formsemestre_id)]
+
+    diag_by_sem = {}
+    for formsemestre in formsemestres:
+        (
+            etuds_no_nip,
+            etuds_unknown,
+            changed_mails,
+        ) = sco_synchro_etuds.formsemestre_import_etud_admission(
+            formsemestre.id, import_identite=True, import_email=import_email
         )
-    if changed_mails:
-        H.append("<h3>Adresses mails modifiées:</h3><ul>")
-        for etud, old_mail in changed_mails:
-            H.append(
-                f"""<li>{etud.nom}: <tt>{old_mail}</tt> devient <tt>{etud.email}</tt></li>"""
-            )
-        H.append("</ul>")
-    return "\n".join(H) + html_sco_header.sco_footer()
+        diag = ""
+        if etuds_no_nip:
+            diag += f"""<p>Attention: étudiants sans NIP:
+                    {', '.join([e.html_link_fiche() for e in etuds_no_nip])}
+                    </p>"""
+
+        if etuds_unknown:
+            diag += f"""<p>Attention: étudiants inconnus du portail:
+                {', '.join([(e.html_link_fiche() + ' (nip= ' + e.code_nip + ')')
+                            for e in etuds_unknown])}
+                </p>"""
+
+        if changed_mails:
+            diag += """<p>Adresses mails modifiées:</p><ul>"""
+            for etud, old_mail in changed_mails:
+                diag += f"""<li>{etud.nom}: <tt>{old_mail}</tt> devient <tt>{etud.email}</tt></li>"""
+            diag += "</ul>"
+        diag_by_sem[formsemestre.id] = diag
+
+    return f"""
+        { html_sco_header.html_sem_header("Ré-import données admission") }
+        <h3>Opération effectuée</h3>
+        <p>Sur le(s) semestres(s):</p>
+        <ul>
+            <li>
+            { '</li><li>'.join( [(s.html_link_status() + diag_by_sem[s.id]) for s in formsemestres ]) }
+            </li>
+        </ul>
+        { html_sco_header.sco_footer() }
+        """
 
 
 sco_publish(