diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py
index 87ec62a28fc12aa34146eed9b639d1b3fcb92f06..c0c29bf15978c0007dbe16c926693f1a8226f414 100644
--- a/app/but/bulletin_but.py
+++ b/app/but/bulletin_but.py
@@ -16,7 +16,7 @@ from app.scodoc import sco_bulletins, sco_utils as scu
 from app.scodoc import sco_bulletins_json
 from app.scodoc import sco_bulletins_pdf
 from app.scodoc import sco_preferences
-from app.scodoc.sco_codes_parcours import UE_SPORT
+from app.scodoc.sco_codes_parcours import UE_SPORT, DEF
 from app.scodoc.sco_utils import fmt_note
 
 
@@ -318,19 +318,42 @@ class BulletinBUT:
         return d
 
     def bulletin_etud_complet(self, etud: Identite) -> dict:
-        """Bulletin dict complet avec toutes les infos pour les bulletins pdf"""
+        """Bulletin dict complet avec toutes les infos pour les bulletins BUT pdf
+        Résultat compatible avec celui de sco_bulletins.formsemestre_bulletinetud_dict
+        """
         d = self.bulletin_etud(etud, self.res.formsemestre, force_publishing=True)
         d["etudid"] = etud.id
         d["etud"] = d["etudiant"]
         d["etud"]["nomprenom"] = etud.nomprenom
         d.update(self.res.sem)
+        etud_etat = self.res.get_etud_etat(etud.id)
         d["filigranne"] = sco_bulletins_pdf.get_filigranne(
-            self.res.get_etud_etat(etud.id),
+            etud_etat,
             self.prefs,
             decision_sem=d["semestre"].get("decision_sem"),
         )
+        if etud_etat == scu.DEMISSION:
+            d["demission"] = "(Démission)"
+        elif etud_etat == DEF:
+            d["demission"] = "(Défaillant)"
+        else:
+            d["demission"] = ""
+
         # --- Absences
         d["nbabs"], d["nbabsjust"] = self.res.formsemestre.get_abs_count(etud.id)
+
+        # --- Decision Jury
+        infos, dpv = sco_bulletins.etud_descr_situation_semestre(
+            etud.id,
+            self.res.formsemestre.id,
+            format="html",
+            show_date_inscr=self.prefs["bul_show_date_inscr"],
+            show_decisions=self.prefs["bul_show_decision"],
+            show_uevalid=self.prefs["bul_show_uevalid"],
+            show_mention=self.prefs["bul_show_mention"],
+        )
+
+        d.update(infos)
         # --- Rangs
         d[
             "rang_nt"
@@ -341,5 +364,6 @@ class BulletinBUT:
         d.update(
             sco_bulletins.get_appreciations_list(self.res.formsemestre.id, etud.id)
         )
-        # XXX TODO A COMPLETER ?
+        d.update(sco_bulletins.make_context_dict(self.res.formsemestre, d["etud"]))
+
         return d
diff --git a/app/scodoc/sco_abs_notification.py b/app/scodoc/sco_abs_notification.py
index f15e7d4c87a0495b2250c0a0a06d9137b485d1bd..466d13df18a819f589d61d8f66862c850e128d4e 100644
--- a/app/scodoc/sco_abs_notification.py
+++ b/app/scodoc/sco_abs_notification.py
@@ -35,6 +35,7 @@ import datetime
 
 from flask import g, url_for
 from flask_mail import Message
+from app.models.formsemestre import FormSemestre
 
 import app.scodoc.notesdb as ndb
 import app.scodoc.sco_utils as scu
@@ -55,27 +56,30 @@ def abs_notify(etudid, date):
     """
     from app.scodoc import sco_abs
 
-    sem = retreive_current_formsemestre(etudid, date)
-    if not sem:
+    formsemestre = retreive_current_formsemestre(etudid, date)
+    if not formsemestre:
         return  # non inscrit a la date, pas de notification
 
-    nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
-    do_abs_notify(sem, etudid, date, nbabs, nbabsjust)
+    nbabs, nbabsjust = sco_abs.get_abs_count_in_interval(
+        etudid, formsemestre.date_debut.isoformat(), formsemestre.date_fin.isoformat()
+    )
+    do_abs_notify(formsemestre, etudid, date, nbabs, nbabsjust)
 
 
-def do_abs_notify(sem, etudid, date, nbabs, nbabsjust):
+def do_abs_notify(formsemestre: FormSemestre, etudid, date, nbabs, nbabsjust):
     """Given new counts of absences, check if notifications are requested and send them."""
     # prefs fallback to global pref if sem is None:
-    if sem:
-        formsemestre_id = sem["formsemestre_id"]
+    if formsemestre:
+        formsemestre_id = formsemestre.id
     else:
         formsemestre_id = None
-    prefs = sco_preferences.SemPreferences(formsemestre_id=sem["formsemestre_id"])
+    prefs = sco_preferences.SemPreferences(formsemestre_id=formsemestre_id)
 
     destinations = abs_notify_get_destinations(
-        sem, prefs, etudid, date, nbabs, nbabsjust
+        formsemestre, prefs, etudid, date, nbabs, nbabsjust
     )
-    msg = abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust)
+
+    msg = abs_notification_message(formsemestre, prefs, etudid, nbabs, nbabsjust)
     if not msg:
         return  # abort
 
@@ -131,19 +135,19 @@ def abs_notify_send(destinations, etudid, msg, nbabs, nbabsjust, formsemestre_id
     )
 
 
-def abs_notify_get_destinations(sem, prefs, etudid, date, nbabs, nbabsjust):
+def abs_notify_get_destinations(
+    formsemestre: FormSemestre, prefs, etudid, date, nbabs, nbabsjust
+) -> set:
     """Returns set of destination emails to be notified"""
-    formsemestre_id = sem["formsemestre_id"]
 
     destinations = []  # list of email address to notify
 
-    if abs_notify_is_above_threshold(etudid, nbabs, nbabsjust, formsemestre_id):
-        if sem and prefs["abs_notify_respsem"]:
+    if abs_notify_is_above_threshold(etudid, nbabs, nbabsjust, formsemestre.id):
+        if prefs["abs_notify_respsem"]:
             # notifie chaque responsable du semestre
-            for responsable_id in sem["responsables"]:
-                u = sco_users.user_info(responsable_id)
-                if u["email"]:
-                    destinations.append(u["email"])
+            for responsable in formsemestre.responsables:
+                if responsable.email:
+                    destinations.append(responsable.email)
         if prefs["abs_notify_chief"] and prefs["email_chefdpt"]:
             destinations.append(prefs["email_chefdpt"])
         if prefs["abs_notify_email"]:
@@ -156,7 +160,7 @@ def abs_notify_get_destinations(sem, prefs, etudid, date, nbabs, nbabsjust):
     # Notification (à chaque fois) des resp. de modules ayant des évaluations
     # à cette date
     # nb: on pourrait prevoir d'utiliser un autre format de message pour ce cas
-    if sem and prefs["abs_notify_respeval"]:
+    if prefs["abs_notify_respeval"]:
         mods = mod_with_evals_at_date(date, etudid)
         for mod in mods:
             u = sco_users.user_info(mod["responsable_id"])
@@ -232,7 +236,9 @@ def user_nbdays_since_last_notif(email_addr, etudid):
         return None
 
 
-def abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust):
+def abs_notification_message(
+    formsemestre: FormSemestre, prefs, etudid, nbabs, nbabsjust
+):
     """Mime notification message based on template.
     returns a Message instance
     or None if sending should be canceled (empty template).
@@ -242,7 +248,7 @@ def abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust):
     etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
 
     # Variables accessibles dans les balises du template: %(nom_variable)s :
-    values = sco_bulletins.make_context_dict(sem, etud)
+    values = sco_bulletins.make_context_dict(formsemestre, etud)
 
     values["nbabs"] = nbabs
     values["nbabsjust"] = nbabsjust
@@ -264,9 +270,11 @@ def abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust):
     return msg
 
 
-def retreive_current_formsemestre(etudid, cur_date):
+def retreive_current_formsemestre(etudid: int, cur_date) -> FormSemestre:
     """Get formsemestre dans lequel etudid est (ou était) inscrit a la date indiquée
     date est une chaine au format ISO (yyyy-mm-dd)
+
+    Result: FormSemestre ou None si pas inscrit à la date indiquée
     """
     req = """SELECT i.formsemestre_id 
     FROM notes_formsemestre_inscription i, notes_formsemestre sem
@@ -278,8 +286,8 @@ def retreive_current_formsemestre(etudid, cur_date):
     if not r:
         return None
     # s'il y a plusieurs semestres, prend le premier (rarissime et non significatif):
-    sem = sco_formsemestre.get_formsemestre(r[0]["formsemestre_id"])
-    return sem
+    formsemestre = FormSemestre.query.get(r[0]["formsemestre_id"])
+    return formsemestre
 
 
 def mod_with_evals_at_date(date_abs, etudid):
diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py
index 60cf4931d3012c2272f6f233f9eae27c2ae86180..6b1afc4a2948b6676b623b2c704c74ed08769fa7 100644
--- a/app/scodoc/sco_bulletins.py
+++ b/app/scodoc/sco_bulletins.py
@@ -78,33 +78,20 @@ from app.scodoc import sco_bulletins_legacy
 from app.scodoc import sco_bulletins_ucac  # format expérimental UCAC Cameroun
 
 
-def make_context_dict(sem, etud):
+def make_context_dict(formsemestre: FormSemestre, etud: dict) -> dict:
     """Construit dictionnaire avec valeurs pour substitution des textes
     (preferences bul_pdf_*)
     """
-    C = sem.copy()
-    C["responsable"] = " ,".join(
-        [
-            sco_users.user_info(responsable_id)["prenomnom"]
-            for responsable_id in sem["responsables"]
-        ]
-    )
-
-    annee_debut = sem["date_debut"].split("/")[2]
-    annee_fin = sem["date_fin"].split("/")[2]
-    if annee_debut != annee_fin:
-        annee = "%s - %s" % (annee_debut, annee_fin)
-    else:
-        annee = annee_debut
-    C["anneesem"] = annee
+    C = formsemestre.get_infos_dict()
+    C["responsable"] = formsemestre.responsables_str()
+    C["anneesem"] = C["annee"]  # backward compat
     C.update(etud)
     # copie preferences
-    # XXX devrait acceder directement à un dict de preferences, à revoir
     for name in sco_preferences.get_base_preferences().prefs_name:
-        C[name] = sco_preferences.get_preference(name, sem["formsemestre_id"])
+        C[name] = sco_preferences.get_preference(name, formsemestre.id)
 
     # ajoute groupes et group_0, group_1, ...
-    sco_groups.etud_add_group_infos(etud, sem)
+    sco_groups.etud_add_group_infos(etud, formsemestre.id)
     C["groupes"] = etud["groupes"]
     n = 0
     for partition_id in etud["partitions"]:
@@ -125,7 +112,8 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
     Le contenu du dictionnaire dépend des options (rangs, ...)
     et de la version choisie (short, long, selectedevals).
 
-    Cette fonction est utilisée pour les bulletins HTML et PDF, mais pas ceux en XML.
+    Cette fonction est utilisée pour les bulletins CLASSIQUES (DUT, ...)
+    en HTML et PDF, mais pas ceux en XML.
     """
     from app.scodoc import sco_abs
 
@@ -190,7 +178,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
     )
     I["etud_etat"] = nt.get_etud_etat(etudid)
     I["filigranne"] = sco_bulletins_pdf.get_filigranne(
-        I["etud_etat"], prefs, decision_dem=I["decision_sem"]
+        I["etud_etat"], prefs, decision_sem=I["decision_sem"]
     )
     I["demission"] = ""
     if I["etud_etat"] == scu.DEMISSION:
@@ -384,7 +372,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
         I["matieres_modules"].update(_sort_mod_by_matiere(modules, nt, etudid))
 
     #
-    C = make_context_dict(I["sem"], I["etud"])
+    C = make_context_dict(formsemestre, I["etud"])
     C.update(I)
     #
     # log( 'C = \n%s\n' % pprint.pformat(C) ) # tres pratique pour voir toutes les infos dispo
@@ -842,7 +830,7 @@ def formsemestre_bulletinetud(
 
     H = [
         _formsemestre_bulletinetud_header_html(
-            etud, etudid, sem, formsemestre_id, format, version
+            etud, etudid, formsemestre, format, version
         ),
         bulletin,
     ]
@@ -1063,8 +1051,7 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr):
 def _formsemestre_bulletinetud_header_html(
     etud,
     etudid,
-    sem,
-    formsemestre_id=None,
+    formsemestre: FormSemestre,
     format=None,
     version=None,
 ):
@@ -1078,33 +1065,27 @@ def _formsemestre_bulletinetud_header_html(
             ],
             cssstyles=["css/radar_bulletin.css"],
         ),
-        """<table class="bull_head"><tr><td>
-          <h2><a class="discretelink" href="%s">%s</a></h2>
-          """
-        % (
+        f"""<table class="bull_head"><tr><td>
+          <h2><a class="discretelink" href="{
             url_for(
-                "scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"]
-            ),
-            etud["nomprenom"],
-        ),
-        """
-          <form name="f" method="GET" action="%s">"""
-        % request.base_url,
-        f"""Bulletin <span class="bull_liensemestre"><a href="{
-            url_for("notes.formsemestre_status", 
-            scodoc_dept=g.scodoc_dept, 
-            formsemestre_id=sem["formsemestre_id"])}
-            ">{sem["titremois"]}</a></span> 
-          <br/>"""
-        % sem,
-        """<table><tr>""",
-        """<td>établi le %s (notes sur 20)</td>""" % time.strftime("%d/%m/%Y à %Hh%M"),
-        """<td><span class="rightjust">
-             <input type="hidden" name="formsemestre_id" value="%s"></input>"""
-        % formsemestre_id,
-        """<input type="hidden" name="etudid" value="%s"></input>""" % etudid,
-        """<input type="hidden" name="format" value="%s"></input>""" % format,
-        """<select name="version" onchange="document.f.submit()" class="noprint">""",
+            "scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"]
+            )}">{etud["nomprenom"]}</a></h2>
+
+          <form name="f" method="GET" action="{request.base_url}">
+            Bulletin <span class="bull_liensemestre"><a href="{
+            url_for("notes.formsemestre_status",
+            scodoc_dept=g.scodoc_dept,
+            formsemestre_id=formsemestre.id)}
+            ">{formsemestre.titre_mois()}</a></span>
+          <br/>
+        <table><tr>
+        <td>établi le {time.strftime("%d/%m/%Y à %Hh%M")} (notes sur 20)</td>
+        <td><span class="rightjust">
+        <input type="hidden" name="formsemestre_id" value="{formsemestre.id}"></input>
+        <input type="hidden" name="etudid" value="{etudid}"></input>
+        <input type="hidden" name="format" value="{format}"></input>
+        <select name="version" onchange="document.f.submit()" class="noprint">
+        """,
     ]
     for (v, e) in (
         ("short", "Version courte"),
@@ -1125,7 +1106,7 @@ def _formsemestre_bulletinetud_header_html(
             "title": "Réglages bulletins",
             "endpoint": "notes.formsemestre_edit_options",
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 # "target_url": url_for(
                 #     "notes.formsemestre_bulletinetud",
                 #     scodoc_dept=g.scodoc_dept,
@@ -1133,17 +1114,16 @@ def _formsemestre_bulletinetud_header_html(
                 #     etudid=etudid,
                 # ),
             },
-            "enabled": (current_user.id in sem["responsables"])
-            or current_user.has_permission(Permission.ScoImplement),
+            "enabled": formsemestre.can_be_edited_by(current_user),
         },
         {
             "title": 'Version papier (pdf, format "%s")'
             % sco_bulletins_generator.bulletin_get_class_name_displayed(
-                formsemestre_id
+                formsemestre.id
             ),
             "endpoint": endpoint,
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
                 "version": version,
                 "format": "pdf",
@@ -1153,19 +1133,19 @@ def _formsemestre_bulletinetud_header_html(
             "title": "Envoi par mail à %s" % etud["email"],
             "endpoint": endpoint,
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
                 "version": version,
                 "format": "pdfmail",
             },
             # possible slt si on a un mail...
-            "enabled": etud["email"] and can_send_bulletin_by_mail(formsemestre_id),
+            "enabled": etud["email"] and can_send_bulletin_by_mail(formsemestre.id),
         },
         {
             "title": "Envoi par mail à %s (adr. personnelle)" % etud["emailperso"],
             "endpoint": endpoint,
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
                 "version": version,
                 "format": "pdfmail",
@@ -1173,13 +1153,13 @@ def _formsemestre_bulletinetud_header_html(
             },
             # possible slt si on a un mail...
             "enabled": etud["emailperso"]
-            and can_send_bulletin_by_mail(formsemestre_id),
+            and can_send_bulletin_by_mail(formsemestre.id),
         },
         {
             "title": "Version json",
             "endpoint": endpoint,
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
                 "version": version,
                 "format": "json",
@@ -1189,7 +1169,7 @@ def _formsemestre_bulletinetud_header_html(
             "title": "Version XML",
             "endpoint": endpoint,
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
                 "version": version,
                 "format": "xml",
@@ -1199,19 +1179,19 @@ def _formsemestre_bulletinetud_header_html(
             "title": "Ajouter une appréciation",
             "endpoint": "notes.appreciation_add_form",
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
             },
             "enabled": (
-                (current_user.id in sem["responsables"])
-                or (current_user.has_permission(Permission.ScoEtudInscrit))
+                formsemestre.can_be_edited_by(current_user)
+                or current_user.has_permission(Permission.ScoEtudInscrit)
             ),
         },
         {
             "title": "Enregistrer un semestre effectué ailleurs",
             "endpoint": "notes.formsemestre_ext_create_form",
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
             },
             "enabled": current_user.has_permission(Permission.ScoImplement),
@@ -1220,34 +1200,34 @@ def _formsemestre_bulletinetud_header_html(
             "title": "Enregistrer une validation d'UE antérieure",
             "endpoint": "notes.formsemestre_validate_previous_ue",
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
             },
-            "enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
+            "enabled": sco_permissions_check.can_validate_sem(formsemestre.id),
         },
         {
             "title": "Enregistrer note d'une UE externe",
             "endpoint": "notes.external_ue_create_form",
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
             },
-            "enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
+            "enabled": sco_permissions_check.can_validate_sem(formsemestre.id),
         },
         {
             "title": "Entrer décisions jury",
             "endpoint": "notes.formsemestre_validation_etud_form",
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
             },
-            "enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
+            "enabled": sco_permissions_check.can_validate_sem(formsemestre.id),
         },
         {
             "title": "Editer PV jury",
             "endpoint": "notes.formsemestre_pvjury_pdf",
             "args": {
-                "formsemestre_id": formsemestre_id,
+                "formsemestre_id": formsemestre.id,
                 "etudid": etudid,
             },
             "enabled": True,
@@ -1263,7 +1243,7 @@ def _formsemestre_bulletinetud_header_html(
             url_for(
                 "notes.formsemestre_bulletinetud",
                 scodoc_dept=g.scodoc_dept,
-                formsemestre_id=formsemestre_id,
+                formsemestre_id=formsemestre.id,
                 etudid=etudid,
                 format="pdf",
                 version=version,
diff --git a/app/scodoc/sco_find_etud.py b/app/scodoc/sco_find_etud.py
index 4bc039baa2de534858db5d67ef57b23acd69cf05..9c563048097b97e4025a907d7938da09040b38fa 100644
--- a/app/scodoc/sco_find_etud.py
+++ b/app/scodoc/sco_find_etud.py
@@ -180,7 +180,9 @@ def search_etud_in_dept(expnom=""):
             e["_nomprenom_target"] = target
             e["inscription_target"] = target
             e["_nomprenom_td_attrs"] = 'id="%s" class="etudinfo"' % (e["etudid"])
-            sco_groups.etud_add_group_infos(e, e["cursem"])
+            sco_groups.etud_add_group_infos(
+                e, e["cursem"]["formsemestre_id"] if e["cursem"] else None
+            )
 
         tab = GenTable(
             columns_ids=("nomprenom", "code_nip", "inscription", "groupes"),
diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py
index 6a1ee4679b940e124523d76d62233f638743b455..2a27b498d001fa1100835018d28431d5232c9186 100644
--- a/app/scodoc/sco_groups.py
+++ b/app/scodoc/sco_groups.py
@@ -321,7 +321,7 @@ def get_group_infos(group_id, etat=None):  # was _getlisteetud
             t["etath"] = t["etat"]
     # Add membership for all partitions, 'partition_id' : group
     for etud in members:  # long: comment eviter ces boucles ?
-        etud_add_group_infos(etud, sem)
+        etud_add_group_infos(etud, sem["formsemestre_id"])
 
     if group["group_name"] != None:
         group_tit = "%s %s" % (group["partition_name"], group["group_name"])
@@ -413,12 +413,12 @@ def formsemestre_get_etud_groupnames(formsemestre_id, attr="group_name"):
     return R
 
 
-def etud_add_group_infos(etud, sem, sep=" "):
+def etud_add_group_infos(etud, formsemestre_id, sep=" "):
     """Add informations on partitions and group memberships to etud (a dict with an etudid)"""
     etud[
         "partitions"
     ] = collections.OrderedDict()  # partition_id : group + partition_name
-    if not sem:
+    if not formsemestre_id:
         etud["groupes"] = ""
         return etud
 
@@ -430,7 +430,7 @@ def etud_add_group_infos(etud, sem, sep=" "):
         and p.formsemestre_id = %(formsemestre_id)s
         ORDER BY p.numero
         """,
-        {"etudid": etud["etudid"], "formsemestre_id": sem["formsemestre_id"]},
+        {"etudid": etud["etudid"], "formsemestre_id": formsemestre_id},
     )
 
     for info in infos:
@@ -439,13 +439,13 @@ def etud_add_group_infos(etud, sem, sep=" "):
 
     # resume textuel des groupes:
     etud["groupes"] = sep.join(
-        [g["group_name"] for g in infos if g["group_name"] != None]
+        [gr["group_name"] for gr in infos if gr["group_name"] is not None]
     )
     etud["partitionsgroupes"] = sep.join(
         [
-            g["partition_name"] + ":" + g["group_name"]
-            for g in infos
-            if g["group_name"] != None
+            gr["partition_name"] + ":" + gr["group_name"]
+            for gr in infos
+            if gr["group_name"] is not None
         ]
     )
 
diff --git a/app/scodoc/sco_import_etuds.py b/app/scodoc/sco_import_etuds.py
index b717f2d19fa99a25dc88eb7e78358d0b37dae7f1..6d4988d17c36b5f7baf6829c74d441cd6020cd14 100644
--- a/app/scodoc/sco_import_etuds.py
+++ b/app/scodoc/sco_import_etuds.py
@@ -203,7 +203,7 @@ def sco_import_generate_excel_sample(
             for field in titles:
                 if field == "groupes":
                     sco_groups.etud_add_group_infos(
-                        etud, groups_infos.formsemestre, sep=";"
+                        etud, groups_infos.formsemestre_id, sep=";"
                     )
                     l.append(etud["partitionsgroupes"])
                 else:
diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py
index 831cc87a947504a2f4f5ed0c6c6e5b3a8c6dc94e..807792fb00f8c724b541e28dd9daf506b8152060 100644
--- a/app/scodoc/sco_inscr_passage.py
+++ b/app/scodoc/sco_inscr_passage.py
@@ -196,7 +196,10 @@ def do_inscrit(sem, etudids, inscrit_groupes=False):
             if len(etud["sems"]) < 2:
                 continue
             prev_formsemestre = etud["sems"][1]
-            sco_groups.etud_add_group_infos(etud, prev_formsemestre)
+            sco_groups.etud_add_group_infos(
+                etud,
+                prev_formsemestre["formsemestre_id"] if prev_formsemestre else None,
+            )
 
             cursem_groups_by_name = dict(
                 [
diff --git a/app/scodoc/sco_page_etud.py b/app/scodoc/sco_page_etud.py
index 358521397528909ff11e1df3537a6a46cd908480..73a355b621911a3048b0be2960dfd886347fc10e 100644
--- a/app/scodoc/sco_page_etud.py
+++ b/app/scodoc/sco_page_etud.py
@@ -215,7 +215,9 @@ def ficheEtud(etudid=None):
         info["modifadresse"] = ""
 
     # Groupes:
-    sco_groups.etud_add_group_infos(info, info["cursem"])
+    sco_groups.etud_add_group_infos(
+        info, info["cursem"]["formsemestre_id"] if info["cursem"] else None
+    )
 
     # Parcours de l'étudiant
     if info["sems"]:
diff --git a/app/views/scolar.py b/app/views/scolar.py
index 680f1dd2f981581f409a01de962d709c18d052d2..3258ec5847f506553f24aefc2990b684a72ca5f3 100644
--- a/app/views/scolar.py
+++ b/app/views/scolar.py
@@ -513,7 +513,7 @@ def etud_info(etudid=None, format="xml"):
 
     sem = etud["cursem"]
     if sem:
-        sco_groups.etud_add_group_infos(etud, sem)
+        sco_groups.etud_add_group_infos(etud, sem["formsemestre_id"] if sem else None)
         d["insemestre"] = [
             {
                 "current": "1",