diff --git a/app/scodoc/sco_lycee.py b/app/scodoc/sco_lycee.py
index e70a70d7806e9e816cadf2526e30757f0746534a..b6b0ebe8596e606b91ec2827e104ba44dce7357a 100644
--- a/app/scodoc/sco_lycee.py
+++ b/app/scodoc/sco_lycee.py
@@ -44,6 +44,7 @@ from app.scodoc import (
     sco_report,
     sco_etud,
 )
+from app.scodoc.sco_exceptions import ScoValueError
 from app.models import FormSemestre
 from app.scodoc.gen_tables import GenTable
 import app.scodoc.sco_utils as scu
@@ -180,13 +181,20 @@ def _table_etuds_lycees(etuds, group_lycees, title, preferences, no_links=False)
 
 def formsemestre_etuds_lycees(
     formsemestre_id,
-    group_ids: list[int] = None,  # si indiqué, ne prend que ces groupes
     fmt="html",
     only_primo=False,
     no_grouping=False,
 ):
     """Table des lycées d'origine"""
     formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
+    if request.method == "POST":
+        group_ids = request.form.getlist("group_ids")
+    else:
+        group_ids = request.args.getlist("group_ids")
+    try:
+        group_ids = [int(gid) for gid in group_ids]
+    except ValueError as exc:
+        raise ScoValueError("group_ids invalide") from exc
     groups_infos = sco_groups_view.DisplayedGroupsInfos(
         group_ids,
         formsemestre_id=formsemestre.id,
@@ -195,11 +203,17 @@ def formsemestre_etuds_lycees(
     tab, etuds_by_lycee = formsemestre_table_etuds_lycees(
         formsemestre, groups_infos, only_primo=only_primo, group_lycees=not no_grouping
     )
-    tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
-    if only_primo:
-        tab.base_url += "&only_primo=1"
-    if no_grouping:
-        tab.base_url += "&no_grouping=1"
+    tab.base_url = (
+        scu.build_url_query(
+            request.base_url,
+            formsemestre_id=formsemestre.id,
+            no_grouping="1" if no_grouping else None,
+            only_primo="on" if only_primo else None,
+        )
+        + ("&" + groups_infos.groups_query_args)
+        if groups_infos
+        else ""
+    )
     table_html = tab.make_page(fmt=fmt, with_html_headers=False)
     if fmt != "html":
         return table_html
diff --git a/app/scodoc/sco_report.py b/app/scodoc/sco_report.py
index 05371480e6b884725a26e188dcc6553760bab993..1b77beeac33dc979b910ef98c0c0b0c83c16189d 100644
--- a/app/scodoc/sco_report.py
+++ b/app/scodoc/sco_report.py
@@ -800,11 +800,20 @@ def formsemestre_suivi_cohorte(
         only_primo=only_primo,
     )
     tab.base_url = (
-        "%s?formsemestre_id=%s&percent=%s&bac=%s&bacspecialite=%s&civilite=%s"
-        % (request.base_url, formsemestre.id, percent, bac, bacspecialite, civilite)
+        scu.build_url_query(
+            request.base_url,
+            bac=bac,
+            bacspecialite=bacspecialite,
+            civilite=civilite,
+            formsemestre_id=formsemestre.id,
+            percent=percent,
+            only_primo="on" if only_primo else None,
+        )
+        + ("&" + groups_infos.groups_query_args)
+        if groups_infos
+        else ""
     )
-    if only_primo:
-        tab.base_url += "&only_primo=on"
+
     t = tab.make_page(fmt=fmt, with_html_headers=False)
     if fmt != "html":
         return t
@@ -1806,9 +1815,16 @@ def formsemestre_graph_cursus(
             % sem,
             f"""(<a href="{
                 url_for("notes.formsemestre_graph_cursus", fmt="pdf", **url_kw)
+                + ("&" + groups_infos.groups_query_args)
+                if groups_infos
+                else ""
                 }">version pdf</a>,
                 <a href="{
-                    url_for("notes.formsemestre_graph_cursus", fmt="png", **url_kw)}">image PNG</a>)
+                url_for("notes.formsemestre_graph_cursus", fmt="png", **url_kw)
+                + ("&" + groups_infos.groups_query_args)
+                if groups_infos
+                else ""
+                }">image PNG</a>)
 
             </p>
             <p class="help">Le graphe permet de suivre les étudiants inscrits dans le semestre
diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py
index 8cee570742507b0b4f371b4b5c0071f45531147e..71bc7e4ce41e8fb17e1e82c469e6097d99ebdbff 100644
--- a/app/scodoc/sco_utils.py
+++ b/app/scodoc/sco_utils.py
@@ -912,11 +912,11 @@ def unescape_html(s):
 
 
 def build_url_query(url: str, **params) -> str:
-    """Add parameters to existing url, as a query string"""
+    """Add parameters to existing url, as a query string. Eliminate None values."""
     url_parse = urlparse(url)
     query = url_parse.query
     url_dict = dict(parse_qsl(query))
-    url_dict.update(params)
+    url_dict.update({k: v for k, v in params.items() if v is not None})
     url_new_query = urlencode(url_dict)
     url_parse = url_parse._replace(query=url_new_query)
     new_url = urlunparse(url_parse)
diff --git a/app/templates/formsemestre/etuds_lycees.j2 b/app/templates/formsemestre/etuds_lycees.j2
index 441a1b279845d1e6be59d1da47c7c814eafc0883..b2c75f8048ce801f585b2a377747e206e887a7e8 100644
--- a/app/templates/formsemestre/etuds_lycees.j2
+++ b/app/templates/formsemestre/etuds_lycees.j2
@@ -29,4 +29,5 @@ Page non fonctionnelle dans cette version (modif. API Google)
 <script src="{{scu.STATIC_DIR}}/libjs/jquery.ui.map.full.min.js"></script>
 <script src="https://maps.google.com/maps/api/js"></script>
 <script src="{{scu.STATIC_DIR}}/js/map_lycees.js"></script>
+<script src="{{scu.STATIC_DIR}}/js/groups_view.js"></script>
 {% endblock %}
\ No newline at end of file