From f9c2eb0e94ea2f4d62774d97473e025349ac8d61 Mon Sep 17 00:00:00 2001 From: IDK Date: Sat, 23 Jan 2021 23:42:03 +0100 Subject: [PATCH 1/5] 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 56f16eaa..9df285a1 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 9ba88174..c962e6b6 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 df92a442..daa6851f 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"] From 73cabb0039495e5aa9f297633e9ad1bcd0388a0c Mon Sep 17 00:00:00 2001 From: IDK Date: Thu, 28 Jan 2021 23:02:18 +0100 Subject: [PATCH 2/5] Enhanced tests + some code cleaning --- ZScoDoc.py | 8 ++----- ZScolar.py | 3 ++- sco_modalites.py | 18 ++++++++-------- scotests/sco_fake_gen.py | 19 ++++++++++++++--- scotests/test_capitalisation.py | 38 ++++++++++++++++++++++++++++++--- 5 files changed, 64 insertions(+), 22 deletions(-) diff --git a/ZScoDoc.py b/ZScoDoc.py index 4b567877..4dad4ba3 100644 --- a/ZScoDoc.py +++ b/ZScoDoc.py @@ -34,10 +34,7 @@ import time, string, glob, re, inspect import urllib, urllib2, cgi, xml -try: - from cStringIO import StringIO -except: - from StringIO import StringIO +from cStringIO import StringIO from zipfile import ZipFile import os.path, glob import traceback @@ -48,9 +45,8 @@ from email.MIMEBase import MIMEBase from email.Header import Header from email import Encoders -from sco_zope import * +from sco_zope import * # pylint: disable=unused-wildcard-import -# try: import Products.ZPsycopgDA.DA as ZopeDA except: diff --git a/ZScolar.py b/ZScolar.py index 683fa7eb..71414d48 100644 --- a/ZScolar.py +++ b/ZScolar.py @@ -87,7 +87,8 @@ import ZNotes import ZAbsences import ZEntreprises import ZScoUsers -import sco_modalites + +# import sco_modalites import ImportScolars import sco_abs import sco_portal_apogee diff --git a/sco_modalites.py b/sco_modalites.py index fb9dcf7a..396184c6 100644 --- a/sco_modalites.py +++ b/sco_modalites.py @@ -33,8 +33,8 @@ Elle n'est pas utilisée pour les parcours, ni pour rien d'autre (c'est donc un attribut "cosmétique"). """ -from notesdb import * -from sco_utils import * +import notesdb as ndb +import sco_utils as scu from notes_log import log from TrivialFormulator import TrivialFormulator, TF import sco_codes_parcours @@ -56,7 +56,7 @@ def group_sems_by_modalite(context, sems): """Given the list of fromsemestre, group them by modalite, sorted in each one by semestre id and date """ - sems_by_mod = DictDefault(defaultvalue=[]) + sems_by_mod = scu.DictDefault(defaultvalue=[]) modalites = list_formsemestres_modalites(context, sems) for modalite in modalites: for sem in sems: @@ -75,12 +75,12 @@ def group_sems_by_modalite(context, sems): # ------ Low level interface (database) ------ -_modaliteEditor = EditableTable( +_modaliteEditor = ndb.EditableTable( "notes_form_modalites", "form_modalite_id", ("form_modalite_id", "modalite", "titre", "numero"), sortkey="numero", - output_formators={"numero": int_null_is_zero}, + output_formators={"numero": ndb.int_null_is_zero}, ) @@ -90,23 +90,23 @@ def do_modalite_list(context, *args, **kw): return _modaliteEditor.list(cnx, *args, **kw) -def do_modalite_create(context, args, REQUEST): +def do_modalite_create(context, args, REQUEST=None): "create a modalite" - cnx = self.GetDBConnexion() + cnx = context.GetDBConnexion() r = _modaliteEditor.create(cnx, args) return r def do_modalite_delete(context, oid, REQUEST=None): "delete a modalite" - cnx = self.GetDBConnexion() + cnx = context.GetDBConnexion() log("do_modalite_delete: form_modalite_id=%s" % oid) _modaliteEditor.delete(cnx, oid) def do_modalite_edit(context, *args, **kw): "edit a modalite" - cnx = self.GetDBConnexion() + cnx = context.GetDBConnexion() # check m = do_modalite_list(context, {"form_modalite_id": args[0]["form_modalite_id"]})[0] _modaliteEditor.edit(cnx, *args, **kw) diff --git a/scotests/sco_fake_gen.py b/scotests/sco_fake_gen.py index 9caaaa77..2a066de0 100644 --- a/scotests/sco_fake_gen.py +++ b/scotests/sco_fake_gen.py @@ -132,7 +132,7 @@ class ScoFake: code_specialite=None, ): """Crée une formation""" - if acronyme == "": + if not acronyme: acronyme = "TEST" + str(random.randint(100000, 999999)) oid = self.context.do_formation_create(locals(), REQUEST=REQUEST) oids = self.context.formation_list(args={"formation_id": oid}) @@ -295,7 +295,14 @@ class ScoFake: comment=comment, ) - def setup_formation(self, nb_semestre=1, nb_ue_per_semestre=2, nb_module_per_ue=2): + def setup_formation( + self, + nb_semestre=1, + nb_ue_per_semestre=2, + nb_module_per_ue=2, + acronyme=None, + titre=None, + ): """Création d'une formation, avec UE, modules et évaluations. Formation avec `nb_semestre` comportant chacun `nb_ue_per_semestre` UE @@ -304,7 +311,7 @@ class ScoFake: Returns: formation (dict), liste d'ue (dicts), liste de modules. """ - f = self.create_formation(acronyme="") + f = self.create_formation(acronyme=acronyme, titre=titre) ue_list = [] mod_list = [] for semestre_id in range(1, nb_semestre + 1): @@ -337,6 +344,9 @@ class ScoFake: date_debut="01/01/2020", date_fin="30/06/2020", nb_evaluations_per_module=1, + titre=None, + responsables=["bach"], + modalite=None, ): """Création semestre, avec modules et évaluations.""" sem = self.create_formsemestre( @@ -344,6 +354,9 @@ class ScoFake: semestre_id=semestre_id, date_debut=date_debut, date_fin=date_fin, + titre=titre, + responsables=responsables, + modalite=modalite, ) eval_list = [] for mod in mod_list: diff --git a/scotests/test_capitalisation.py b/scotests/test_capitalisation.py index 31c67c32..cd2a2280 100644 --- a/scotests/test_capitalisation.py +++ b/scotests/test_capitalisation.py @@ -14,6 +14,7 @@ Utiliser comme: import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error import sco_utils import sco_codes_parcours +import sco_modalites G = sco_fake_gen.ScoFake(context.Notes) # pylint: disable=undefined-variable G.verbose = False @@ -21,8 +22,29 @@ G.verbose = False # --- Création d'étudiants etuds = [G.create_etud(code_nip=None) for _ in range(10)] +# Deux modalités +# sco_modalites.do_modalite_create(context, {}) + # --- Mise en place formation 4 semestres -f, ue_list, mod_list = G.setup_formation(nb_semestre=4) +form_dut, ue_list, mod_list = G.setup_formation( + nb_semestre=4, titre="DUT RT", acronyme="DUT-RT" +) + +# --- et une formation LP en 2 semestres +form_lp, ue_list_lp, mod_list_lp = G.setup_formation( + nb_semestre=2, titre="Licence Pro Ingéniérie Pédagogique", acronyme="LP IP" +) +# et un semestre que l'on ne va pas utiliser ici: +_, _ = G.setup_formsemestre( + form_lp, + mod_list_lp, + semestre_id=1, + date_debut="01/09/2021", + date_fin="15/01/2022", + titre="Licence Pro Ingéniérie Pédagogique", + responsables=["callas"], + modalite="FAP", +) # --- Crée les 4 semestres et affecte des notes aléatoires sems, evals = [], [] @@ -33,7 +55,12 @@ for semestre_id, date_debut, date_fin in [ (4, "16/01/2021", "30/06/2021"), ]: sem, eval_list = G.setup_formsemestre( - f, mod_list, semestre_id=semestre_id, date_debut=date_debut, date_fin=date_fin + form_dut, + mod_list, + semestre_id=semestre_id, + date_debut=date_debut, + date_fin=date_fin, + titre="BUT Réseaux et Tests", ) sems.append(sem) evals.append(eval_list) # liste des listes d'evaluations @@ -55,7 +82,12 @@ for semestre_id, date_debut, date_fin in [ (4, "16/01/2023", "30/06/2023"), ]: sem, eval_list = G.setup_formsemestre( - f, mod_list, semestre_id=semestre_id, date_debut=date_debut, date_fin=date_fin + form_dut, + mod_list, + semestre_id=semestre_id, + date_debut=date_debut, + date_fin=date_fin, + titre="BUT Réseaux et Tests", ) sems.append(sem) evals.append(eval_list) # liste des listes d'evaluations From dc88238f6340722a649fcf19fc5587a6e0e5dc0c Mon Sep 17 00:00:00 2001 From: IDK Date: Thu, 28 Jan 2021 23:28:48 +0100 Subject: [PATCH 3/5] code cleaning --- ZScoDoc.py | 86 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/ZScoDoc.py b/ZScoDoc.py index 4dad4ba3..3a2d328b 100644 --- a/ZScoDoc.py +++ b/ZScoDoc.py @@ -31,19 +31,29 @@ Chaque departement est géré par un ZScolar sous ZScoDoc. """ -import time, string, glob, re, inspect -import urllib, urllib2, cgi, xml +import time +import datetime +import string +import glob +import re +import inspect +import urllib +import urllib2 +import cgi +import xml from cStringIO import StringIO from zipfile import ZipFile -import os.path, glob +import os.path import traceback -from email.MIMEMultipart import MIMEMultipart -from email.MIMEText import MIMEText -from email.MIMEBase import MIMEBase -from email.Header import Header -from email import Encoders +from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error + MIMEMultipart, +) +from email.MIMEText import MIMEText # pylint: disable=no-name-in-module,import-error +from email.MIMEBase import MIMEBase # pylint: disable=no-name-in-module,import-error +from email.Header import Header # pylint: disable=no-name-in-module,import-error +from email import Encoders # pylint: disable=no-name-in-module,import-error from sco_zope import * # pylint: disable=unused-wildcard-import @@ -52,10 +62,28 @@ try: except: import ZPsycopgDA.DA as ZopeDA # interp.py -from sco_utils import * +import sco_utils as scu +import VERSION from notes_log import log import sco_find_etud import sco_users +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 class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Implicit): @@ -101,7 +129,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp def _check_users_folder(self, REQUEST=None): """Vérifie UserFolder et le crée s'il le faut""" try: - udb = self.UsersDB + _ = self.UsersDB return "" except: e = self._check_admin_perm(REQUEST) @@ -154,7 +182,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp pass # add missing getAuthFailedMessage (bug in exUserFolder ?) try: - x = self.getAuthFailedMessage + _ = self.getAuthFailedMessage except: log("adding getAuthFailedMessage to Zope install") parent = self.aq_parent @@ -177,7 +205,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp da = ZopeDA.Connection( oid, "Cnx bd utilisateurs", - SCO_DEFAULT_SQL_USERS_CNX, + scu.SCO_DEFAULT_SQL_USERS_CNX, False, check=1, tilevel=2, @@ -254,7 +282,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp r = [] for folder in folders: try: - s = folder.Scolarite + _ = folder.Scolarite r.append(folder) except: pass @@ -352,10 +380,10 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp def scodoc_top_html_header(self, REQUEST, page_title="ScoDoc"): H = [ self._html_begin - % {"page_title": "ScoDoc: bienvenue", "encoding": SCO_ENCODING}, + % {"page_title": "ScoDoc: bienvenue", "encoding": scu.SCO_ENCODING}, self._top_level_css, """""", - CUSTOM_HTML_HEADER_CNX, + scu.CUSTOM_HTML_HEADER_CNX, ] return "\n".join(H) @@ -468,7 +496,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp E. Viennet (Université Paris 13).

""" - % (SCO_WEBSITE,) + % (scu.SCO_WEBSITE,) ) H.append("""""") @@ -581,8 +609,8 @@ E. Viennet (Université Paris 13).

%s""" % ( - SCO_ENCODING, - CUSTOM_HTML_HEADER_CNX, + scu.SCO_ENCODING, + scu.CUSTOM_HTML_HEADER_CNX, ) security.declareProtected("View", "standard_html_footer") @@ -594,8 +622,8 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa

Problèmes et suggestions sur le logiciel: %s

ScoDoc est un logiciel libre développé par Emmanuel Viennet.

""" % ( - SCO_USERS_LIST, - SCO_USERS_LIST, + scu.SCO_USERS_LIST, + scu.SCO_USERS_LIST, ) # sendEmail is not used through the web @@ -640,8 +668,8 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa **kv ): "Recuperation des exceptions Zope" - sco_exc_mail = SCO_EXC_MAIL - sco_dev_mail = SCO_DEV_MAIL + sco_exc_mail = scu.SCO_EXC_MAIL + sco_dev_mail = scu.SCO_DEV_MAIL # neat (or should I say dirty ?) hack to get REQUEST # in fact, our caller (probably SimpleItem.py) has the REQUEST variable # that we'd like to use for our logs, but does not pass it as an argument. @@ -728,7 +756,7 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa pass # --- Mail: - error_traceback_txt = scodoc_html2txt(error_tb) + error_traceback_txt = scu.scodoc_html2txt(error_tb) txt = ( """ ErrorType: %(error_type)s @@ -762,7 +790,7 @@ ErrorType: %(error_type)s HTTP_USER_AGENT = "na" form = REQUEST.get("form", "") HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "") - svn_version = get_svn_version(self.file_path) + svn_version = scu.get_svn_version(self.file_path) SCOVERSION = VERSION.SCOVERSION txt = ( @@ -792,7 +820,7 @@ subversion: %(svn_version)s def send_debug_alert(self, txt, REQUEST=None): """Send an alert email (bug report) to ScoDoc developpers""" - if not SCO_EXC_MAIL: + if not scu.SCO_EXC_MAIL: log("send_debug_alert: email disabled") return if REQUEST: @@ -801,13 +829,13 @@ subversion: %(svn_version)s else: URL = "send_debug_alert" msg = MIMEMultipart() - subj = Header("[scodoc] exc %s" % URL, SCO_ENCODING) + subj = Header("[scodoc] exc %s" % URL, scu.SCO_ENCODING) msg["Subject"] = subj - recipients = [SCO_EXC_MAIL] + recipients = [scu.SCO_EXC_MAIL] msg["To"] = " ,".join(recipients) msg["From"] = "scodoc-alert" msg.epilogue = "" - msg.attach(MIMEText(txt, "plain", SCO_ENCODING)) + msg.attach(MIMEText(txt, "plain", scu.SCO_ENCODING)) self.sendEmailFromException(msg) log("Sent mail alert:\n" + txt) @@ -881,7 +909,7 @@ subversion: %(svn_version)s """Liste de id de departements definis par create_dept.sh (fichiers depts/*.cfg) """ - filenames = glob.glob(SCODOC_VAR_DIR + "/config/depts/*.cfg") + filenames = glob.glob(scu.SCODOC_VAR_DIR + "/config/depts/*.cfg") ids = [os.path.split(os.path.splitext(f)[0])[1] for f in filenames] return ids From 6605f02d6bbc4cf2a9b76b95c557b53a51a1e16c Mon Sep 17 00:00:00 2001 From: IDK Date: Fri, 29 Jan 2021 07:01:04 +0100 Subject: [PATCH 4/5] code cleaning --- ZScoDoc.py | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/ZScoDoc.py b/ZScoDoc.py index 3a2d328b..56204368 100644 --- a/ZScoDoc.py +++ b/ZScoDoc.py @@ -186,7 +186,7 @@ class ZScoDoc(ObjectManager, PropertyManager, RoleManager, Item, Persistent, Imp except: log("adding getAuthFailedMessage to Zope install") parent = self.aq_parent - from OFS.DTMLMethod import addDTMLMethod + from OFS.DTMLMethod import addDTMLMethod # pylint: disable=import-error addDTMLMethod(parent, "getAuthFailedMessage", file="Identification") @@ -661,15 +661,13 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa def standard_error_message( self, error_value=None, - error_message=None, + error_message=None, # unused ? error_type=None, error_traceback=None, error_tb=None, **kv ): "Recuperation des exceptions Zope" - sco_exc_mail = scu.SCO_EXC_MAIL - sco_dev_mail = scu.SCO_DEV_MAIL # neat (or should I say dirty ?) hack to get REQUEST # in fact, our caller (probably SimpleItem.py) has the REQUEST variable # that we'd like to use for our logs, but does not pass it as an argument. @@ -682,9 +680,6 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa # Authentication uses exceptions, pass them up HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "") if error_type == "LoginRequired": - # raise 'LoginRequired', '' # copied from exuserFolder (beurk, old style exception...) - # if REQUEST: - # REQUEST.response.setStatus( 401, "Unauthorized") # ?????? log("LoginRequired from %s" % HTTP_X_FORWARDED_FOR) self.login_page = error_value return error_value @@ -695,6 +690,14 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa log("exception caught: %s" % error_type) log(traceback.format_exc()) + params = { + "error_type": error_type, + "error_value": error_value, + "error_tb": error_tb, + "sco_exc_mail": scu.SCO_EXC_MAIL, + "sco_dev_mail": scu.SCO_DEV_MAIL, + } + if error_type == "ScoGenError": return "

" + str(error_value) + "

" elif error_type in ("ScoValueError", "FormatError"): @@ -732,11 +735,11 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa

""" - % vars() + % params ) # display error traceback (? may open a security risk via xss attack ?) # log('exc B') - txt_html = self._report_request(REQUEST, fmt="html") + params["txt_html"] = self._report_request(REQUEST, fmt="html") H.append( """

Zope Traceback (à envoyer par mail à %(sco_dev_mail)s)

%(error_tb)s @@ -747,7 +750,7 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa

Merci de votre patience !

""" - % vars() + % params ) try: H.append(self.standard_html_footer(REQUEST)) @@ -756,14 +759,14 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa pass # --- Mail: - error_traceback_txt = scu.scodoc_html2txt(error_tb) + params["error_traceback_txt"] = scu.scodoc_html2txt(error_tb) txt = ( """ ErrorType: %(error_type)s %(error_traceback_txt)s """ - % vars() + % params ) self.send_debug_alert(txt, REQUEST=REQUEST) @@ -774,25 +777,29 @@ ErrorType: %(error_type)s def _report_request(self, REQUEST, fmt="txt"): """string describing current request for bug reports""" - AUTHENTICATED_USER = REQUEST.get("AUTHENTICATED_USER", "") - dt = time.asctime() - URL = REQUEST.get("URL", "") QUERY_STRING = REQUEST.get("QUERY_STRING", "") if QUERY_STRING: QUERY_STRING = "?" + QUERY_STRING - METHOD = REQUEST.get("REQUEST_METHOD", "") - if fmt == "txt": REFERER = REQUEST.get("HTTP_REFERER", "") HTTP_USER_AGENT = REQUEST.get("HTTP_USER_AGENT", "") else: REFERER = "na" HTTP_USER_AGENT = "na" - form = REQUEST.get("form", "") - HTTP_X_FORWARDED_FOR = REQUEST.get("HTTP_X_FORWARDED_FOR", "") - svn_version = scu.get_svn_version(self.file_path) - SCOVERSION = VERSION.SCOVERSION + params = dict( + AUTHENTICATED_USER=REQUEST.get("AUTHENTICATED_USER", ""), + dt=time.asctime(), + URL=REQUEST.get("URL", ""), + QUERY_STRING=QUERY_STRING, + METHOD=REQUEST.get("REQUEST_METHOD", ""), + REFERER=REFERER, + HTTP_USER_AGENT=HTTP_USER_AGENT, + form=REQUEST.get("form", ""), + HTTP_X_FORWARDED_FOR=REQUEST.get("HTTP_X_FORWARDED_FOR", ""), + svn_version=scu.get_svn_version(self.file_path), + SCOVERSION=VERSION.SCOVERSION, + ) txt = ( """ Version: %(SCOVERSION)s @@ -808,7 +815,7 @@ Agent: %(HTTP_USER_AGENT)s subversion: %(svn_version)s """ - % vars() + % params ) if fmt == "html": txt = txt.replace("\n", "
") From 0d0c391547fc4c417a74f6c35ac46a0cd46f63e9 Mon Sep 17 00:00:00 2001 From: IDK Date: Fri, 29 Jan 2021 07:36:26 +0100 Subject: [PATCH 5/5] test: demissions --- notes_table.py | 2 +- scotests/test_demissions.py | 61 +++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 scotests/test_demissions.py diff --git a/notes_table.py b/notes_table.py index 004c60d9..550b65b9 100644 --- a/notes_table.py +++ b/notes_table.py @@ -139,7 +139,7 @@ class NotesTable: - identdict: { etudid : ident } - sem : le formsemestre get_table_moyennes_triees: [ (moy_gen, moy_ue1, moy_ue2, ... moy_ues, moy_mod1, ..., moy_modn, etudid) ] - (où toutes les valeurs sont soit des nombrs soit des chaines spéciales comme 'NA', 'NI'), + (où toutes les valeurs sont soit des nombres soit des chaines spéciales comme 'NA', 'NI'), incluant les UE de sport - bonus[etudid] : valeur du bonus "sport". diff --git a/scotests/test_demissions.py b/scotests/test_demissions.py new file mode 100644 index 00000000..6028a05d --- /dev/null +++ b/scotests/test_demissions.py @@ -0,0 +1,61 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +"""Test inscriptions / demissions / affichages notes + + - Création 2 étudiants, puis formation en 1 semestre. + - Saisie de 2 notes + - Demission d'un étudiant + - bulletins + +Utiliser comme: + scotests/scointeractive.sh -r TEST00 scotests/test_demissions.py + +""" + +import scotests.sco_fake_gen as sco_fake_gen # pylint: disable=import-error +import sco_utils +import sco_bulletins + +G = sco_fake_gen.ScoFake(context.Notes) # pylint: disable=undefined-variable +G.verbose = False + +# --- Création d'étudiants +etuds = [G.create_etud(code_nip=None) for _ in range(2)] +# --- Mise en place formation +form, ue_list, mod_list = G.setup_formation( + nb_semestre=1, titre="Essai 1", acronyme="ESS01" +) +# Mise en place semestre +sem, eval_list = G.setup_formsemestre( + form, + mod_list, + semestre_id=1, + date_debut="01/01/2021", + date_fin="31/12/2021", + titre="Essai démissions", +) +# Inscriptions +for etud in etuds: + G.inscrit_etudiant(sem, etud) +# Notes +G.set_etud_notes_sem(sem, eval_list, etuds) + +# Bulletins +bul = sco_bulletins.formsemestre_bulletinetud_dict( + context.Notes, sem["formsemestre_id"], etuds[0]["etudid"] +) +print(bul["moy_gen"]) +assert bul["ins"][0]["etat"] == "I" + +# Démission: +context.doDemEtudiant( + etuds[0]["etudid"], sem["formsemestre_id"], event_date="15/12/2021" +) +bul = sco_bulletins.formsemestre_bulletinetud_dict( + context.Notes, sem["formsemestre_id"], etuds[0]["etudid"] +) +print(bul["moy_gen"]) + +assert bul["moy_gen"] == "NA" +assert bul["ins"][0]["etat"] == "D"