From f9c2eb0e94ea2f4d62774d97473e025349ac8d61 Mon Sep 17 00:00:00 2001 From: IDK Date: Sat, 23 Jan 2021 23:42:03 +0100 Subject: [PATCH] Restored 2 ScoDoc7 API points + clean imports --- ZEntreprises.py | 6 +- ZNotes.py | 181 ++++++++++++++++++++++++++-------------------- sco_moduleimpl.py | 4 +- 3 files changed, 107 insertions(+), 84 deletions(-) diff --git a/ZEntreprises.py b/ZEntreprises.py index 56f16eaac..9df285a1e 100644 --- a/ZEntreprises.py +++ b/ZEntreprises.py @@ -38,11 +38,9 @@ import re import time import calendar -from sco_zope import * +from sco_zope import * # pylint: disable=unused-wildcard-import + from sco_permissions import ScoEntrepriseView, ScoEntrepriseChange - -# --------------- - from notes_log import log from scolog import logdb from sco_utils import SCO_ENCODING diff --git a/ZNotes.py b/ZNotes.py index 9ba881748..c962e6b67 100644 --- a/ZNotes.py +++ b/ZNotes.py @@ -27,16 +27,39 @@ """Interface Zope <-> Notes """ +import time +import datetime +import jaxml +import pprint -from sco_zope import * +from sco_zope import * # pylint: disable=unused-wildcard-import # --------------- - -from notesdb import * +# from sco_utils import * +import sco_utils as scu +import notesdb as ndb from notes_log import log, sendAlarm import scolog from scolog import logdb -from sco_utils import * +from sco_permissions import ( + ScoView, + ScoEnsView, + ScoImplement, + ScoChangeFormation, + ScoObservateur, + ScoEtudInscrit, + ScoEtudChangeGroups, + ScoEtudChangeAdr, + ScoEtudSupprAnnotations, + ScoEditAllEvals, + ScoEditAllNotes, + ScoEditFormationTags, + ScoEditApo, + ScoSuperAdmin, +) +from sco_exceptions import ScoValueError, ScoLockedFormError, ScoGenError, AccessDenied + +from TrivialFormulator import TrivialFormulator import htmlutils import sco_excel @@ -64,6 +87,7 @@ import sco_edit_module import sco_tag_module import sco_bulletins import sco_bulletins_pdf +import sco_compute_moy import sco_recapcomplet import sco_liste_notes import sco_saisie_notes @@ -94,7 +118,8 @@ import sco_export_results import sco_formsemestre_exterieurs from sco_pdf import PDFLOCK -from notes_table import * +import notes_table +from notes_table import NOTES_CACHE_INST, CacheNotesTable import VERSION # @@ -222,7 +247,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl "essai gloups" return "" # return pdfbulletins.essaipdf(REQUEST) - # return sendPDFFile(REQUEST, pdfbulletins.pdftrombino(0,0), 'toto.pdf' ) + # return scu.sendPDFFile(REQUEST, pdfbulletins.pdftrombino(0,0), 'toto.pdf' ) # Python methods: security.declareProtected(ScoView, "formsemestre_status") @@ -338,15 +363,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl def index_html(self, REQUEST=None): "Page accueil formations" - lockicon = icontag( - "lock32_img", title="Comporte des semestres verrouillés", border="0" - ) - suppricon = icontag( - "delete_small_img", border="0", alt="supprimer", title="Supprimer" - ) - editicon = icontag( - "edit_img", border="0", alt="modifier", title="Modifier titres et code" - ) editable = REQUEST.AUTHENTICATED_USER.has_permission(ScoChangeFormation, self) @@ -377,7 +393,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl # -------------------------------------------------------------------- # --- Formations - _formationEditor = EditableTable( + _formationEditor = ndb.EditableTable( "notes_formations", "formation_id", ( @@ -464,7 +480,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl cnx = self.GetDBConnexion() r = self._formationEditor.list(cnx, args=args) # log('%d formations found' % len(r)) - return sendResult(REQUEST, r, name="formation", format=format) + return scu.sendResult(REQUEST, r, name="formation", format=format) security.declareProtected(ScoView, "formation_export") @@ -508,9 +524,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl elif tf[0] == -1: return REQUEST.RESPONSE.redirect(REQUEST.URL1) else: - formation_id, junk, junk = self.formation_import_xml( - tf[2]["xmlfile"], REQUEST - ) + formation_id, _, _ = self.formation_import_xml(tf[2]["xmlfile"], REQUEST) return ( "\n".join(H) @@ -547,7 +561,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl return new_id, modules_old2new, ues_old2new # --- UE - _ueEditor = EditableTable( + _ueEditor = ndb.EditableTable( "notes_ue", "ue_id", ( @@ -564,11 +578,11 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl "coefficient", ), sortkey="numero", - input_formators={"type": int_null_is_zero}, + input_formators={"type": ndb.int_null_is_zero}, output_formators={ - "numero": int_null_is_zero, - "ects": float_null_is_null, - "coefficient": float_null_is_zero, + "numero": ndb.int_null_is_zero, + "ects": ndb.float_null_is_null, + "coefficient": ndb.float_null_is_zero, }, ) @@ -628,7 +642,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl ) if delete_validations: log("deleting all validations of UE %s" % ue_id) - SimpleQuery( + ndb.SimpleQuery( self, "DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s", {"ue_id": ue_id}, @@ -639,12 +653,12 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl for mat in mats: self.do_matiere_delete(mat["matiere_id"], REQUEST) # delete uecoef and events - SimpleQuery( + ndb.SimpleQuery( self, "DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s", {"ue_id": ue_id}, ) - SimpleQuery( + ndb.SimpleQuery( self, "DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue_id} ) cnx = self.GetDBConnexion() @@ -675,12 +689,12 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl return self._ueEditor.list(cnx, *args, **kw) # --- Matieres - _matiereEditor = EditableTable( + _matiereEditor = ndb.EditableTable( "notes_matieres", "matiere_id", ("matiere_id", "ue_id", "numero", "titre"), sortkey="numero", - output_formators={"numero": int_null_is_zero}, + output_formators={"numero": ndb.int_null_is_zero}, ) security.declareProtected(ScoChangeFormation, "do_matiere_create") @@ -749,7 +763,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl cnx = self.GetDBConnexion() # check mat = self.do_matiere_list({"matiere_id": args[0]["matiere_id"]})[0] - ue = self.do_ue_list({"ue_id": mat["ue_id"]})[0] if self.matiere_is_locked(mat["matiere_id"]): raise ScoLockedFormError() # edit @@ -761,7 +774,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl def do_matiere_formation_id(self, matiere_id): "get formation_id from matiere" cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( "select UE.formation_id from notes_matieres M, notes_ue UE where M.matiere_id = %(matiere_id)s and M.ue_id = UE.ue_id", {"matiere_id": matiere_id}, @@ -770,7 +783,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl return res[0][0] # --- Modules - _moduleEditor = EditableTable( + _moduleEditor = ndb.EditableTable( "notes_modules", "module_id", ( @@ -793,13 +806,13 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl ), sortkey="numero, code, titre", output_formators={ - "heures_cours": float_null_is_zero, - "heures_td": float_null_is_zero, - "heures_tp": float_null_is_zero, - "numero": int_null_is_zero, - "coefficient": float_null_is_zero, - "module_type": int_null_is_zero - #'ects' : float_null_is_null + "heures_cours": ndb.float_null_is_zero, + "heures_td": ndb.float_null_is_zero, + "heures_tp": ndb.float_null_is_zero, + "numero": ndb.int_null_is_zero, + "coefficient": ndb.float_null_is_zero, + "module_type": ndb.int_null_is_zero + #'ects' : ndb.float_null_is_null }, ) @@ -918,7 +931,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl """True if UE should not be modified (used in a locked formsemestre) """ - r = SimpleDictFetch( + r = ndb.SimpleDictFetch( self, """SELECT mi.* from notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi WHERE mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id @@ -934,7 +947,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl """True if matiere should not be modified (contains modules used in a locked formsemestre) """ - r = SimpleDictFetch( + r = ndb.SimpleDictFetch( self, """SELECT ma.* from notes_matieres ma, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi WHERE ma.matiere_id = mod.matiere_id AND mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id @@ -950,7 +963,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl """True if module should not be modified (contains modules used in a locked formsemestre) """ - r = SimpleDictFetch( + r = ndb.SimpleDictFetch( self, """SELECT ue.* FROM notes_ue ue, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi WHERE ue.ue_id = mod.ue_id @@ -1091,7 +1104,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl args[argname] = L[argname] sems = sco_formsemestre.do_formsemestre_list(self, args=args) # log('formsemestre_list: format="%s", %s semestres found' % (format,len(sems))) - return sendResult(REQUEST, sems, name="formsemestre", format=format) + return scu.sendResult(REQUEST, sems, name="formsemestre", format=format) security.declareProtected(ScoView, "XMLgetFormsemestres") @@ -1106,8 +1119,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl if formsemestre_id: args["formsemestre_id"] = formsemestre_id if REQUEST: - REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) - doc = jaxml.XML_document(encoding=SCO_ENCODING) + REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE) + doc = jaxml.XML_document(encoding=scu.SCO_ENCODING) doc.formsemestrelist() for sem in sco_formsemestre.do_formsemestre_list(self, args=args): doc._push() @@ -1488,8 +1501,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl self, moduleimpl_id=moduleimpl_id )[0] sem = sco_formsemestre.get_formsemestre(self, M["formsemestre_id"]) - debut_sem = DateDMYtoISO(sem["date_debut"]) - fin_sem = DateDMYtoISO(sem["date_fin"]) + debut_sem = ndb.DateDMYtoISO(sem["date_debut"]) + fin_sem = ndb.DateDMYtoISO(sem["date_fin"]) list_insc = sco_moduleimpl.do_moduleimpl_listeetuds(self, moduleimpl_id) T = [] @@ -1542,7 +1555,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl rows=T, html_class="table_leftalign", base_url="%s?moduleimpl_id=%s" % (REQUEST.URL0, moduleimpl_id), - filename="absmodule_" + make_filename(M["module"]["titre"]), + filename="absmodule_" + scu.make_filename(M["module"]["titre"]), caption="Absences dans le module %s" % M["module"]["titre"], preferences=self.get_preferences(), ) @@ -1647,15 +1660,15 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl sem_ens[ensd["ens_id"]]["mods"].append(mod) # compte les absences ajoutées par chacun dans tout le semestre cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) for ens in sem_ens: cursor.execute( "select * from scolog L, notes_formsemestre_inscription I where method='AddAbsence' and authenticated_user=%(authenticated_user)s and L.etudid = I.etudid and I.formsemestre_id=%(formsemestre_id)s and date > %(date_debut)s and date < %(date_fin)s", { "authenticated_user": ens, "formsemestre_id": formsemestre_id, - "date_debut": DateDMYtoISO(sem["date_debut"]), - "date_fin": DateDMYtoISO(sem["date_fin"]), + "date_debut": ndb.DateDMYtoISO(sem["date_debut"]), + "date_fin": ndb.DateDMYtoISO(sem["date_fin"]), }, ) @@ -1691,7 +1704,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl rows=sem_ens_list, html_sortable=True, html_class="table_leftalign", - filename=make_filename("Enseignants-" + sem["titreannee"]), + filename=scu.make_filename("Enseignants-" + sem["titreannee"]), html_title=self.html_sem_header( REQUEST, "Enseignants du semestre", sem, with_page_header=False ), @@ -1707,7 +1720,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl def edit_enseignants_form_delete(self, REQUEST, moduleimpl_id, ens_id): "remove ens" - M, sem = sco_moduleimpl.can_change_ens(self, REQUEST, moduleimpl_id) + M, _ = sco_moduleimpl.can_change_ens(self, REQUEST, moduleimpl_id) # search ens_id ok = False for ens in M["ens"]: @@ -1722,7 +1735,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl ) # --- Gestion des inscriptions aux modules - _formsemestre_inscriptionEditor = EditableTable( + _formsemestre_inscriptionEditor = ndb.EditableTable( "notes_formsemestre_inscription", "formsemestre_inscription_id", ("formsemestre_inscription_id", "etudid", "formsemestre_id", "etat", "etape"), @@ -1928,7 +1941,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl insem = insem[0] # -- desinscription de tous les modules cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( "select moduleimpl_inscription_id from notes_moduleimpl_inscription Im, notes_moduleimpl M where Im.etudid=%(etudid)s and Im.moduleimpl_id = M.moduleimpl_id and M.formsemestre_id = %(formsemestre_id)s", {"etudid": etudid, "formsemestre_id": formsemestre_id}, @@ -2040,7 +2053,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl ) # --- Evaluations - _evaluationEditor = EditableTable( + _evaluationEditor = ndb.EditableTable( "notes_evaluation", "evaluation_id", ( @@ -2059,15 +2072,15 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl ), sortkey="numero desc, jour desc, heure_debut desc", # plus recente d'abord output_formators={ - "jour": DateISOtoDMY, + "jour": ndb.DateISOtoDMY, "visibulletin": str, "publish_incomplete": str, - "numero": int_null_is_zero, + "numero": ndb.int_null_is_zero, }, input_formators={ - "jour": DateDMYtoISO, - "heure_debut": TimetoISO8601, # converti par do_evaluation_list - "heure_fin": TimetoISO8601, # converti par do_evaluation_list + "jour": ndb.DateDMYtoISO, + "heure_debut": ndb.TimetoISO8601, # converti par do_evaluation_list + "heure_fin": ndb.TimetoISO8601, # converti par do_evaluation_list "visibulletin": int, "publish_incomplete": int, }, @@ -2135,9 +2148,15 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl ) if args["jour"]: next_eval = None - t = (DateDMYtoISO(args["jour"]), TimetoISO8601(args["heure_debut"])) + t = ( + ndb.DateDMYtoISO(args["jour"]), + ndb.TimetoISO8601(args["heure_debut"]), + ) for e in ModEvals: - if (DateDMYtoISO(e["jour"]), TimetoISO8601(e["heure_debut"])) > t: + if ( + ndb.DateDMYtoISO(e["jour"]), + ndb.TimetoISO8601(e["heure_debut"]), + ) > t: next_eval = e break if next_eval: @@ -2209,8 +2228,8 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl date_debut = datetime.date(y, m, d) d, m, y = [int(x) for x in sem["date_fin"].split("/")] date_fin = datetime.date(y, m, d) - # passe par DateDMYtoISO pour avoir date pivot - y, m, d = [int(x) for x in DateDMYtoISO(jour).split("-")] + # passe par ndb.DateDMYtoISO pour avoir date pivot + y, m, d = [int(x) for x in ndb.DateDMYtoISO(jour).split("-")] jour = datetime.date(y, m, d) if (jour > date_fin) or (jour < date_debut): raise ScoValueError( @@ -2223,7 +2242,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl args["heure_fin"] = heure_fin if jour and ((not heure_debut) or (not heure_fin)): raise ScoValueError("Les heures doivent être précisées") - d = TimeDuration(heure_debut, heure_fin) + d = ndb.TimeDuration(heure_debut, heure_fin) if d and ((d < 0) or (d > 60 * 12)): raise ScoValueError("Heures de l'évaluation incohérentes !") @@ -2323,11 +2342,11 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl 8, 00 ) # au cas ou pas d'heure (note externe?) heure_fin_dt = e["heure_fin"] or datetime.time(8, 00) - e["heure_debut"] = TimefromISO8601(e["heure_debut"]) - e["heure_fin"] = TimefromISO8601(e["heure_fin"]) - e["jouriso"] = DateDMYtoISO(e["jour"]) + e["heure_debut"] = ndb.TimefromISO8601(e["heure_debut"]) + e["heure_fin"] = ndb.TimefromISO8601(e["heure_fin"]) + e["jouriso"] = ndb.DateDMYtoISO(e["jour"]) heure_debut, heure_fin = e["heure_debut"], e["heure_fin"] - d = TimeDuration(heure_debut, heure_fin) + d = ndb.TimeDuration(heure_debut, heure_fin) if d is not None: m = d % 60 e["duree"] = "%dh" % (d / 60) @@ -2357,7 +2376,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl def do_evaluation_list_in_formsemestre(self, formsemestre_id): "list evaluations in this formsemestre" - cnx = self.GetDBConnexion() mods = sco_moduleimpl.do_moduleimpl_list(self, formsemestre_id=formsemestre_id) evals = [] for mod in mods: @@ -2519,7 +2537,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl if r != None: return r cnx = self.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cond = " where evaluation_id=%(evaluation_id)s" if by_uid: cond += " and uid=%(by_uid)s" @@ -2532,7 +2550,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl d = {} if filter_suppressed: for x in res: - if x["value"] != NOTES_SUPPRESS: + if x["value"] != scu.NOTES_SUPPRESS: d[x["etudid"]] = x else: for x in res: @@ -2551,7 +2569,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf( self, formsemestre_id, REQUEST, version=version ) - return sendPDFFile(REQUEST, pdfdoc, filename) + return scu.sendPDFFile(REQUEST, pdfdoc, filename) security.declareProtected(ScoView, "etud_bulletins_pdf") @@ -2560,7 +2578,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl pdfdoc, filename = sco_bulletins_pdf.get_etud_bulletins_pdf( self, etudid, REQUEST, version=version ) - return sendPDFFile(REQUEST, pdfdoc, filename) + return scu.sendPDFFile(REQUEST, pdfdoc, filename) security.declareProtected(ScoView, "formsemestre_bulletins_pdf_choice") formsemestre_bulletins_pdf_choice = sco_bulletins.formsemestre_bulletins_pdf_choice @@ -2582,7 +2600,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl ): "envoi a chaque etudiant (inscrit et ayant un mail) son bulletin" prefer_mail_perso = int(prefer_mail_perso) - sem = sco_formsemestre.get_formsemestre(self, formsemestre_id) nt = self._getNotesCache().get_NotesTable( self, formsemestre_id ) # > get_etudids @@ -2608,7 +2625,7 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl # Make each bulletin nb_send = 0 for etudid in etudids: - h, i = sco_bulletins.do_formsemestre_bulletinetud( + h, _ = sco_bulletins.do_formsemestre_bulletinetud( self, formsemestre_id, etudid, @@ -3181,7 +3198,6 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl def check_form_integrity(self, formation_id, fix=False, REQUEST=None): "debug" log("check_form_integrity: formation_id=%s fix=%s" % (formation_id, fix)) - F = self.formation_list(args={"formation_id": formation_id})[0] ues = self.do_ue_list(args={"formation_id": formation_id}) bad = [] for ue in ues: @@ -3282,3 +3298,10 @@ class ZNotes(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Impl ) # -------------------------------------------------------------------- + # Support for legacy ScoDoc 7 API + # -------------------------------------------------------------------- + security.declareProtected(ScoView, "do_moduleimpl_list") + do_moduleimpl_list = sco_moduleimpl.do_moduleimpl_list + + security.declareProtected(ScoView, "do_moduleimpl_withmodule_list") + do_moduleimpl_withmodule_list = sco_moduleimpl.do_moduleimpl_withmodule_list diff --git a/sco_moduleimpl.py b/sco_moduleimpl.py index df92a442c..daa6851fe 100644 --- a/sco_moduleimpl.py +++ b/sco_moduleimpl.py @@ -120,7 +120,9 @@ def do_moduleimpl_withmodule_list( context, moduleimpl_id=None, formsemestre_id=None, module_id=None, REQUEST=None ): """Liste les moduleimpls et ajoute dans chacun le module correspondant - Tri la liste par semestre/UE/numero_matiere/numero_module + Tri la liste par semestre/UE/numero_matiere/numero_module. + + Attention: Cette fonction fait partie de l'API ScoDoc 7 et est publiée. """ args = locals() del args["context"]