From f2e86622aeb89c48d47ff60af27191f69e9bcc99 Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet <emmanuel.viennet@gmail.com>
Date: Wed, 16 Jun 2021 12:02:43 +0200
Subject: [PATCH] =?UTF-8?q?WIP:=20commence=20=C3=A0=20prendre=20forme?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/decorators.py            | 23 +++++++++++++-----
 app/scodoc/sco_formations.py |  1 +
 app/views/absences.py        | 23 ++++++++++--------
 app/views/entreprises.py     | 14 +++++------
 app/views/notes.py           | 46 ++++++++++++++++++++----------------
 app/views/scolar.py          | 28 ++++++++++++----------
 6 files changed, 78 insertions(+), 57 deletions(-)

diff --git a/app/decorators.py b/app/decorators.py
index a14c3aff3..3d90aa212 100644
--- a/app/decorators.py
+++ b/app/decorators.py
@@ -4,6 +4,8 @@
 import functools
 from functools import wraps
 import inspect
+import types
+import logging
 
 import flask
 from flask import g
@@ -88,9 +90,9 @@ def permission_required(permission):
             if "scodoc_dept" in kwargs:
                 g.scodoc_dept = kwargs["scodoc_dept"]
                 del kwargs["scodoc_dept"]
-            current_app.logger.info(
-                "permission_required: %s in %s" % (permission, g.scodoc_dept)
-            )
+            # current_app.logger.info(
+            #    "permission_required: %s in %s" % (permission, g.scodoc_dept)
+            # )
             if not current_user.has_permission(permission, g.scodoc_dept):
                 abort(403)
             return f(*args, **kwargs)
@@ -201,8 +203,17 @@ class ScoDoc7Context(object):
     Mainly used to call published methods, as context.function(...)
     """
 
-    def __init__(self, globals_dict):
-        self.__dict__ = globals_dict
+    def __init__(self, name=""):
+        self.name = name
+        logging.getLogger(__name__).info("created %s" % self)
+
+    def populate(self, globals_dict):
+        logging.getLogger(__name__).info("populating context %s" % self)
+        for k in globals_dict:
+            if (not k.startswith("_")) and (
+                type(globals_dict[k]) == types.FunctionType
+            ):
+                setattr(self, k, globals_dict[k].__get__(self))
 
     def __repr__(self):
-        return "ScoDoc7Context()"
+        return "ScoDoc7Context('%s')" % self.name
diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py
index eedfd4828..c936ee0f9 100644
--- a/app/scodoc/sco_formations.py
+++ b/app/scodoc/sco_formations.py
@@ -37,6 +37,7 @@ from notes_log import log
 import sco_codes_parcours
 import sco_formsemestre
 import sco_tag_module
+import sco_preferences
 from gen_tables import GenTable
 from sco_exceptions import ScoValueError
 from sco_permissions import ScoChangeFormation
diff --git a/app/views/absences.py b/app/views/absences.py
index 65b34050c..3bea31900 100644
--- a/app/views/absences.py
+++ b/app/views/absences.py
@@ -95,7 +95,7 @@ from app.scodoc.sco_abs import ddmmyyyy
 CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS
 
 
-context = ScoDoc7Context(globals())
+context = ScoDoc7Context("absences")
 
 
 def sco_publish(route, function, permission):
@@ -706,7 +706,7 @@ def SignaleAbsenceGrHebdo(
                 context, page_title="Saisie des absences", REQUEST=REQUEST
             )
             + "<h3>Aucun étudiant !</h3>"
-            + context.sco_footer(REQUEST)
+            + html_sco_header.sco_footer(REQUEST)
         )
 
     base_url = "SignaleAbsenceGrHebdo?datelundi=%s&%s&destination=%s" % (
@@ -850,7 +850,7 @@ def SignaleAbsenceGrHebdo(
         etuds, datessem, destination, moduleimpl_id, require_module
     )
 
-    H.append(context.sco_footer(REQUEST))
+    H.append(html_sco_header.sco_footer(REQUEST))
     return "\n".join(H)
 
 
@@ -877,7 +877,7 @@ def SignaleAbsenceGrSemestre(
                 context, page_title="Saisie des absences", REQUEST=REQUEST
             )
             + "<h3>Aucun étudiant !</h3>"
-            + context.sco_footer(REQUEST)
+            + html_sco_header.sco_footer(REQUEST)
         )
     formsemestre_id = groups_infos.formsemestre_id
     require_module = sco_preferences.get_preference(
@@ -1043,7 +1043,7 @@ onchange="document.location='%(url)s&moduleimpl_id='+document.getElementById('mo
     H += context._gen_form_saisie_groupe(
         etuds, dates, destination, moduleimpl_id, require_module
     )
-    H.append(context.sco_footer(REQUEST))
+    H.append(html_sco_header.sco_footer(REQUEST))
     return "\n".join(H)
 
 
@@ -1582,7 +1582,7 @@ def EtatAbsencesDate(
             % REQUEST.HTTP_REFERER
         )
 
-    return "\n".join(H) + context.sco_footer(REQUEST)
+    return "\n".join(H) + html_sco_header.sco_footer(REQUEST)
 
 
 # ----- Gestion des "billets d'absence": signalement par les etudiants eux mêmes (à travers le portail)
@@ -1673,7 +1673,7 @@ def AddBilletAbsenceForm(context, etudid, REQUEST=None):
         ),
     )
     if tf[0] == 0:
-        return "\n".join(H) + tf[1] + context.sco_footer(REQUEST)
+        return "\n".join(H) + tf[1] + html_sco_header.sco_footer(REQUEST)
     elif tf[0] == -1:
         return REQUEST.RESPONSE.redirect(scu.ScoURL())
     else:
@@ -1808,7 +1808,7 @@ def listeBillets(context, REQUEST=None):
         submitbutton=False,
     )
     if tf[0] == 0:
-        return "\n".join(H) + tf[1] + T + context.sco_footer(REQUEST)
+        return "\n".join(H) + tf[1] + T + html_sco_header.sco_footer(REQUEST)
     else:
         return REQUEST.RESPONSE.redirect(
             "ProcessBilletAbsenceForm?billet_id=" + tf[2]["billet_id"]
@@ -1951,7 +1951,7 @@ def ProcessBilletAbsenceForm(context, billet_id, REQUEST=None):
         )
         F += '<p><a class="stdlink" href="listeBillets">Liste de tous les billets en attente</a></p>'
 
-        return "\n".join(H) + "<br/>" + tf[1] + F + context.sco_footer(REQUEST)
+        return "\n".join(H) + "<br/>" + tf[1] + F + html_sco_header.sco_footer(REQUEST)
     elif tf[0] == -1:
         return REQUEST.RESPONSE.redirect(scu.ScoURL())
     else:
@@ -1976,7 +1976,7 @@ def ProcessBilletAbsenceForm(context, billet_id, REQUEST=None):
         billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]})
         tab = context._tableBillets(billets, etud=etud)
         H.append(tab.html())
-        return "\n".join(H) + context.sco_footer(REQUEST)
+        return "\n".join(H) + html_sco_header.sco_footer(REQUEST)
 
 
 @bp.route("/XMLgetAbsEtud")
@@ -2011,3 +2011,6 @@ def XMLgetAbsEtud(context, beg_date="", end_date="", REQUEST=None):
     doc._pop()
     log("XMLgetAbsEtud (%gs)" % (time.time() - t0))
     return repr(doc)
+
+
+context.populate(globals())
diff --git a/app/views/entreprises.py b/app/views/entreprises.py
index c8b810b5b..5e0f7703e 100644
--- a/app/views/entreprises.py
+++ b/app/views/entreprises.py
@@ -537,7 +537,7 @@ def entreprise_contact_edit(context, entreprise_contact_id, REQUEST=None):
         initvalues=c,
         submitlabel="Modifier les valeurs",
         readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
-            ScoEntrepriseChange
+            Permission.ScoEntrepriseChange
         ),
     )
 
@@ -665,7 +665,7 @@ def entreprise_correspondant_edit(context, entreprise_corresp_id, REQUEST=None):
         initvalues=c,
         submitlabel="Modifier les valeurs",
         readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
-            ScoEntrepriseChange
+            Permission.ScoEntrepriseChange
         ),
     )
     if tf[0] == 0:
@@ -776,7 +776,7 @@ def entreprise_contact_create(context, entreprise_id, REQUEST=None):
         cancelbutton="Annuler",
         submitlabel="Ajouter ce contact",
         readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
-            ScoEntrepriseChange
+            Permission.ScoEntrepriseChange
         ),
     )
     if tf[0] == 0:
@@ -926,7 +926,7 @@ def entreprise_correspondant_create(context, entreprise_id, REQUEST=None):
         cancelbutton="Annuler",
         submitlabel="Ajouter ce correspondant",
         readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
-            ScoEntrepriseChange
+            Permission.ScoEntrepriseChange
         ),
     )
     if tf[0] == 0:
@@ -960,7 +960,7 @@ def entreprise_correspondant_delete(context, entreprise_corresp_id, REQUEST=None
         submitlabel="Confirmer la suppression",
         cancelbutton="Annuler",
         readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
-            ScoEntrepriseChange
+            Permission.ScoEntrepriseChange
         ),
     )
     if tf[0] == 0:
@@ -1020,7 +1020,7 @@ def entreprise_delete(context, entreprise_id, REQUEST=None):
         submitlabel="Confirmer la suppression",
         cancelbutton="Annuler",
         readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
-            ScoEntrepriseChange
+            Permission.ScoEntrepriseChange
         ),
     )
     if tf[0] == 0:
@@ -1117,7 +1117,7 @@ def entreprise_create(context, REQUEST=None):
         cancelbutton="Annuler",
         submitlabel="Ajouter cette entreprise",
         readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
-            ScoEntrepriseChange
+            Permission.ScoEntrepriseChange
         ),
     )
     if tf[0] == 0:
diff --git a/app/views/notes.py b/app/views/notes.py
index c621c763d..2d8d50d4a 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -128,7 +128,7 @@ from app.scodoc import sco_ue_external
 from app.scodoc import sco_undo_notes
 from app.scodoc import scolars
 
-context = ScoDoc7Context(globals())
+context = ScoDoc7Context("notes")
 
 
 def sco_publish(route, function, permission):
@@ -389,7 +389,7 @@ def index_html(context, REQUEST=None):
         """
         )
 
-    H.append(context.sco_footer(REQUEST))
+    H.append(html_sco_header.sco_footer(context, REQUEST))
     return "\n".join(H)
 
 
@@ -491,7 +491,7 @@ def formation_import_xml_form(context, REQUEST):
     à partir un fichier XML (réservé aux utilisateurs avertis)</p>
     """,
     ]
-    footer = context.sco_footer(REQUEST)
+    footer = html_sco_header.sco_footer(context, REQUEST)
     tf = TrivialFormulator(
         REQUEST.URL0,
         REQUEST.form,
@@ -1143,7 +1143,7 @@ def _check_access_diretud(
     header = html_sco_header.sco_header(
         context, page_title="Accès interdit", REQUEST=REQUEST
     )
-    footer = context.sco_footer(REQUEST)
+    footer = html_sco_header.sco_footer(context, REQUEST)
     if (str(authuser) not in sem["responsables"]) and not authuser.has_permission(
         required_permission, context
     ):
@@ -1192,7 +1192,7 @@ def edit_enseignants_form(context, REQUEST, moduleimpl_id):
         cssstyles=["css/autosuggest_inquisitor.css"],
         bodyOnLoad="init_tf_form('')",
     )
-    footer = context.sco_footer(REQUEST)
+    footer = html_sco_header.sco_footer(context, REQUEST)
 
     # Liste des enseignants avec forme pour affichage / saisie avec suggestion
     userlist = context.Users.get_userlist()
@@ -1345,7 +1345,9 @@ def edit_moduleimpl_resp(context, REQUEST, moduleimpl_id):
         initvalues=initvalues,
     )
     if tf[0] == 0:
-        return "\n".join(H) + tf[1] + help + context.sco_footer(REQUEST)
+        return (
+            "\n".join(H) + tf[1] + help + html_sco_header.sco_footer(context, REQUEST)
+        )
     elif tf[0] == -1:
         return REQUEST.RESPONSE.redirect(
             "moduleimpl_status?moduleimpl_id=" + moduleimpl_id
@@ -1440,7 +1442,7 @@ def edit_moduleimpl_expr(context, REQUEST, moduleimpl_id):
         initvalues=initvalues,
     )
     if tf[0] == 0:
-        return "\n".join(H) + tf[1] + context.sco_footer(REQUEST)
+        return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
     elif tf[0] == -1:
         return REQUEST.RESPONSE.redirect(
             "moduleimpl_status?moduleimpl_id=" + moduleimpl_id
@@ -1514,7 +1516,7 @@ def view_module_abs(context, REQUEST, moduleimpl_id, format="html"):
         return (
             "\n".join(H)
             + "<p>Aucune absence signalée</p>"
-            + context.sco_footer(REQUEST)
+            + html_sco_header.sco_footer(context, REQUEST)
         )
 
     tab = GenTable(
@@ -1538,7 +1540,7 @@ def view_module_abs(context, REQUEST, moduleimpl_id, format="html"):
     if format != "html":
         return tab.make_page(context, format=format, REQUEST=REQUEST)
 
-    return "\n".join(H) + tab.html() + context.sco_footer(REQUEST)
+    return "\n".join(H) + tab.html() + html_sco_header.sco_footer(context, REQUEST)
 
 
 @bp.route("/edit_ue_expr")
@@ -1593,7 +1595,7 @@ def edit_ue_expr(context, REQUEST, formsemestre_id, ue_id):
         initvalues=initvalues,
     )
     if tf[0] == 0:
-        return "\n".join(H) + tf[1] + context.sco_footer(REQUEST)
+        return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
     elif tf[0] == -1:
         return REQUEST.RESPONSE.redirect(
             "formsemestre_status?formsemestre_id=" + formsemestre_id
@@ -1899,7 +1901,7 @@ def formsemestre_desinscription(
         html_sco_header.sco_header(context, REQUEST)
         + '<p>Etudiant désinscrit !</p><p><a class="stdlink" href="%s/ficheEtud?etudid=%s">retour à la fiche</a>'
         % (scu.ScoURL(), etudid)
-        + context.sco_footer(REQUEST)
+        + html_sco_header.sco_footer(context, REQUEST)
     )
 
 
@@ -2283,7 +2285,7 @@ def evaluation_delete(context, REQUEST, evaluation_id):
             """<p>Suppression impossible (effacer les notes d'abord)</p><p><a class="stdlink" href="moduleimpl_status?moduleimpl_id=%s">retour au tableau de bord du module</a></p></div>"""
             % E["moduleimpl_id"]
         )
-        return "\n".join(H) + context.sco_footer(REQUEST)
+        return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
     if warning:
         H.append("""</div>""")
 
@@ -2296,7 +2298,7 @@ def evaluation_delete(context, REQUEST, evaluation_id):
         cancelbutton="Annuler",
     )
     if tf[0] == 0:
-        return "\n".join(H) + tf[1] + context.sco_footer(REQUEST)
+        return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
     elif tf[0] == -1:
         return REQUEST.RESPONSE.redirect(
             scu.ScoURL()
@@ -2314,7 +2316,7 @@ def evaluation_delete(context, REQUEST, evaluation_id):
                 + "/Notes/moduleimpl_status?moduleimpl_id="
                 + E["moduleimpl_id"]
             )
-            + context.sco_footer(REQUEST)
+            + html_sco_header.sco_footer(context, REQUEST)
         )
 
 
@@ -2438,7 +2440,7 @@ def evaluation_listenotes(context, REQUEST=None):
             javascripts=["js/etud_info.js"],
             init_qtip=True,
         )
-        F = context.sco_footer(REQUEST)
+        F = html_sco_header.sco_footer(context, REQUEST)
     else:
         H, F = "", ""
     B = context.do_evaluation_listenotes(REQUEST)
@@ -2702,7 +2704,7 @@ def formsemestre_bulletins_mailetuds(
         html_sco_header.sco_header(context, REQUEST)
         + '<p>%d bulletins sur %d envoyés par mail !</p><p><a class="stdlink" href="formsemestre_status?formsemestre_id=%s">continuer</a></p>'
         % (nb_send, len(etudids), formsemestre_id)
-        + context.sco_footer(REQUEST)
+        + html_sco_header.sco_footer(context, REQUEST)
     )
 
 
@@ -2766,7 +2768,7 @@ def appreciation_add_form(
         html_sco_header.sco_header(context, REQUEST)
         + "<h2>%s d'une appréciation sur %s</h2>" % (a, etud["nomprenom"])
     ]
-    F = context.sco_footer(REQUEST)
+    F = html_sco_header.sco_footer(context, REQUEST)
     descr = [
         ("edit", {"input_type": "hidden", "default": edit}),
         ("etudid", {"input_type": "hidden"}),
@@ -3298,7 +3300,7 @@ def check_sem_integrity(context, formsemestre_id, REQUEST):
         + "<br/>".join([str(x) for x in bad_ue])
         + "<h2>Inconsistent SEM/MOD:</h2>"
         + "<br/>".join([str(x) for x in bad_sem])
-        + context.sco_footer(REQUEST)
+        + html_sco_header.sco_footer(context, REQUEST)
     )
 
 
@@ -3339,7 +3341,7 @@ def check_form_integrity(context, formation_id, fix=False, REQUEST=None):
     return (
         html_sco_header.sco_header(context, REQUEST=REQUEST)
         + txth
-        + context.sco_footer(REQUEST)
+        + html_sco_header.sco_footer(context, REQUEST)
     )
 
 
@@ -3391,7 +3393,7 @@ def check_formsemestre_integrity(context, formsemestre_id, REQUEST=None):
     return (
         html_sco_header.sco_header(context, REQUEST=REQUEST)
         + "<br/>".join(diag)
-        + context.sco_footer(REQUEST)
+        + html_sco_header.sco_footer(context, REQUEST)
     )
 
 
@@ -3409,7 +3411,7 @@ def check_integrity_all(context, REQUEST=None):
     return (
         html_sco_header.sco_header(context, REQUEST=REQUEST)
         + "<p>empty page: see logs and mails</p>"
-        + context.sco_footer(REQUEST)
+        + html_sco_header.sco_footer(context, REQUEST)
     )
 
 
@@ -3424,3 +3426,5 @@ sco_publish(
     sco_moduleimpl.do_moduleimpl_withmodule_list,
     Permission.ScoView,
 )
+
+context.populate(globals())
diff --git a/app/views/scolar.py b/app/views/scolar.py
index f04832f18..57ff3721a 100644
--- a/app/views/scolar.py
+++ b/app/views/scolar.py
@@ -145,7 +145,7 @@ from app.scodoc import sco_dept
 from app.scodoc import sco_dump_db
 
 
-context = ScoDoc7Context(globals())
+context = ScoDoc7Context("scolar")
 
 
 def sco_publish(route, function, permission):
@@ -197,7 +197,7 @@ def about(context, REQUEST):
         html_sco_header.sco_header(context, REQUEST)
         + "\n".join(H)
         + d
-        + context.sco_footer(REQUEST)
+        + html_sco_header.sco_footer(REQUEST)
     )
 
 
@@ -615,7 +615,7 @@ def formChangeCoordonnees(context, etudid, REQUEST):
     )
     dest_url = scu.ScoURL() + "/ficheEtud?etudid=" + etudid
     if tf[0] == 0:
-        return header + "\n".join(H) + tf[1] + context.sco_footer(REQUEST)
+        return header + "\n".join(H) + tf[1] + html_sco_header.sco_footer(REQUEST)
     elif tf[0] == -1:
         return REQUEST.RESPONSE.redirect(dest_url)
     else:
@@ -688,7 +688,7 @@ def etud_photo_orig_page(context, etudid=None, REQUEST=None):
         '<div><a href="ficheEtud?etudid=%s">' % etudid,
         sco_photos.etud_photo_orig_html(context, etud),
         "</a></div>",
-        context.sco_footer(REQUEST),
+        html_sco_header.sco_footer(REQUEST),
     ]
     return "\n".join(H)
 
@@ -736,7 +736,7 @@ def formChangePhoto(context, etudid=None, REQUEST=None):
             + tf[1]
             + '<p><a class="stdlink" href="formSuppressPhoto?etudid=%s">Supprimer cette photo</a></p>'
             % etudid
-            + context.sco_footer(REQUEST)
+            + html_sco_header.sco_footer(REQUEST)
         )
     elif tf[0] == -1:
         return REQUEST.RESPONSE.redirect(dest_url)
@@ -747,7 +747,7 @@ def formChangePhoto(context, etudid=None, REQUEST=None):
             return REQUEST.RESPONSE.redirect(dest_url)
         else:
             H.append('<p class="warning">Erreur:' + diag + "</p>")
-    return "\n".join(H) + context.sco_footer(REQUEST)
+    return "\n".join(H) + html_sco_header.sco_footer(REQUEST)
 
 
 @bp.route("/formSuppressPhoto")
@@ -847,7 +847,7 @@ def _formDem_of_Def(
 </form>"""
         % etud
     )
-    return header + "\n".join(H) + context.sco_footer(REQUEST)
+    return header + "\n".join(H) + html_sco_header.sco_footer(REQUEST)
 
 
 @bp.route("/doDemEtudiant")
@@ -1046,7 +1046,7 @@ def etudident_edit_form(context, REQUEST=None):
 def _etudident_create_or_edit_form(context, REQUEST, edit):
     "Le formulaire HTML"
     H = [html_sco_header.sco_header(context, REQUEST, init_jquery_ui=True)]
-    F = context.sco_footer(REQUEST)
+    F = html_sco_header.sco_footer(REQUEST)
     etudid = REQUEST.form.get("etudid", None)
     cnx = ndb.GetDBConnexion()
     descr = []
@@ -1642,7 +1642,7 @@ def check_group_apogee(
         )
     )
 
-    return "\n".join(H) + context.sco_footer(REQUEST)
+    return "\n".join(H) + html_sco_header.sco_footer(REQUEST)
 
 
 @bp.route("/form_students_import_excel")
@@ -1715,7 +1715,7 @@ def form_students_import_excel(context, REQUEST, formsemestre_id=None):
     <li>"""
     )
 
-    F = context.sco_footer(REQUEST)
+    F = html_sco_header.sco_footer(REQUEST)
     tf = TrivialFormulator(
         REQUEST.URL0,
         REQUEST.form,
@@ -1829,7 +1829,7 @@ def import_generate_admission_sample(context, REQUEST, formsemestre_id):
 def form_students_import_infos_admissions(context, REQUEST, formsemestre_id=None):
     "formulaire import xls"
     authuser = REQUEST.AUTHENTICATED_USER
-    F = context.sco_footer(REQUEST)
+    F = html_sco_header.sco_footer(REQUEST)
     if not authuser.has_permission(Permission.ScoEtudInscrit, context):
         # autorise juste l'export
         H = [
@@ -1958,7 +1958,7 @@ def _students_import_admission(
         if diag:
             H.append("<p>Diagnostic: <ul><li>%s</li></ul></p>" % "</li><li>".join(diag))
 
-        return "\n".join(H) + context.sco_footer(REQUEST)
+        return "\n".join(H) + html_sco_header.sco_footer(REQUEST)
 
 
 @bp.route("/formsemestre_import_etud_admission")
@@ -1994,7 +1994,7 @@ def formsemestre_import_etud_admission(
                 "%s: <tt>%s</tt> devient <tt>%s</tt><br/>"
                 % (info["nom"], info["email"], new_mail)
             )
-    return "\n".join(H) + context.sco_footer(REQUEST)
+    return "\n".join(H) + html_sco_header.sco_footer(REQUEST)
 
 
 sco_publish(
@@ -2037,3 +2037,5 @@ sco_publish(
     sco_formsemestre_edit.formsemestre_edit_uecoefs,
     Permission.ScoView,
 )
+
+context.populate(globals())
-- 
GitLab