From 3e225c9fda13f046c846273256a0bd344afbf741 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 17 Jun 2021 00:08:37 +0200 Subject: [PATCH] WIP fixing imports (still broken!) --- .pylintrc | 2 + app/scodoc/ImportScolars.py | 17 +- app/scodoc/debug.py | 18 +- app/scodoc/gen_tables.py | 3 +- app/scodoc/html_sco_header.py | 4 +- app/scodoc/html_sidebar.py | 2 +- app/scodoc/htmlutils.py | 6 +- app/scodoc/mails.py | 1 + app/scodoc/notes_log.py | 1 + app/scodoc/notes_table.py | 237 ++++---------------------- app/scodoc/pe_avislatex.py | 1 + app/scodoc/pe_view.py | 2 + app/scodoc/sco_abs.py | 5 +- app/scodoc/sco_abs_notification.py | 2 + app/scodoc/sco_abs_views.py | 10 +- app/scodoc/sco_apogee_compare.py | 2 + app/scodoc/sco_apogee_csv.py | 11 +- app/scodoc/sco_archives.py | 16 +- app/scodoc/sco_archives_etud.py | 4 +- app/scodoc/sco_bulletins.py | 31 ++-- app/scodoc/sco_bulletins_generator.py | 12 +- app/scodoc/sco_bulletins_json.py | 14 +- app/scodoc/sco_bulletins_legacy.py | 32 ++-- app/scodoc/sco_bulletins_pdf.py | 8 +- app/scodoc/sco_bulletins_signature.py | 2 +- app/scodoc/sco_bulletins_standard.py | 4 +- app/scodoc/sco_bulletins_ucac.py | 5 +- app/scodoc/sco_bulletins_xml.py | 13 +- app/scodoc/sco_compute_moy.py | 19 ++- app/scodoc/sco_core.py | 214 +++++++++++++++++++++-- app/scodoc/sco_edit_formation.py | 68 ++++++++ app/scodoc/sco_edit_ue.py | 31 ++-- app/scodoc/sco_evaluations.py | 150 ++++++++++++++-- app/scodoc/sco_formsemestre_edit.py | 36 ++-- app/scodoc/sco_formsemestre_status.py | 53 +++--- app/scodoc/sco_liste_notes.py | 10 +- app/scodoc/sco_moduleimpl.py | 19 ++- app/scodoc/sco_moduleimpl_status.py | 4 +- app/scodoc/sco_placement.py | 6 +- app/scodoc/sco_saisie_notes.py | 43 +++-- app/scodoc/sco_ue_external.py | 2 +- app/scodoc/sco_undo_notes.py | 2 +- app/views/notes.py | 210 ++--------------------- scotests/test_basic.py | 2 +- 44 files changed, 724 insertions(+), 610 deletions(-) create mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 00000000..1bda9b54 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,2 @@ +[TYPECHECK] +ignored-classes=Permission diff --git a/app/scodoc/ImportScolars.py b/app/scodoc/ImportScolars.py index 64914f74..20e0180d 100644 --- a/app/scodoc/ImportScolars.py +++ b/app/scodoc/ImportScolars.py @@ -39,13 +39,6 @@ import re import sco_utils as scu import notesdb as ndb from notes_log import log -import scolars -import sco_formsemestre -import sco_groups -import sco_excel -import sco_groups_view -import sco_news -from sco_news import NEWS_INSCR, NEWS_NOTE, NEWS_FORM, NEWS_SEM, NEWS_MISC from sco_formsemestre_inscriptions import do_formsemestre_inscription_with_modules from gen_tables import GenTable from sco_exceptions import ( @@ -57,6 +50,14 @@ from sco_exceptions import ( ScoLockedFormError, ScoGenError, ) +import html_sco_header +import scolars +import sco_formsemestre +import sco_groups +import sco_excel +import sco_groups_view +import sco_news +import sco_preferences # format description (relative to Product directory)) FORMAT_FILE = "misc/format_import_etudiants.txt" @@ -476,7 +477,7 @@ def scolars_import_excel_file( sco_news.add( context, REQUEST, - typ=NEWS_INSCR, + typ=sco_news.NEWS_INSCR, text="Inscription de %d étudiants" # peuvent avoir ete inscrits a des semestres differents % len(created_etudids), object=formsemestre_id, diff --git a/app/scodoc/debug.py b/app/scodoc/debug.py index feb315ee..55605211 100644 --- a/app/scodoc/debug.py +++ b/app/scodoc/debug.py @@ -5,13 +5,13 @@ Lancer ScoDoc ainsi: (comme root) - /opt/scodoc/bin/zopectl debug + /opt/scodoc/bin/zopectl debug Puis from debug import * -context = go(app) -# ou +context = go(app) +# ou context = go_dept(app, 'CJ') authuser = app.acl_users.getUserById('admin') @@ -31,14 +31,14 @@ nt = context.Notes._getNotesCache().get_NotesTable(context.Notes, formsemestre_i """ -import pdb +import pdb # pylint: disable=unused-import import pprint -import notesdb as ndb -from notesdb import * # pylint: disable=unused-wildcard-import -from notes_log import log -import sco_utils as scu -from sco_utils import * # pylint: disable=unused-wildcard-import +import app.scodoc.notesdb as ndb +from app.scodoc.notesdb import * # pylint: disable=unused-wildcard-import +from app.scodoc.notes_log import log +import app.scodoc.sco_utils as scu +from app.scodoc.sco_utils import * # pylint: disable=unused-wildcard-import from gen_tables import GenTable import sco_archives diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py index 3ec9d565..08523794 100644 --- a/app/scodoc/gen_tables.py +++ b/app/scodoc/gen_tables.py @@ -626,7 +626,8 @@ class GenTable: if with_html_headers: H.append( self.html_header - or html_sco_header.sco_header(context, + or html_sco_header.sco_header( + context, REQUEST, page_title=page_title, javascripts=javascripts, diff --git a/app/scodoc/html_sco_header.py b/app/scodoc/html_sco_header.py index ca50b177..77a20ec0 100644 --- a/app/scodoc/html_sco_header.py +++ b/app/scodoc/html_sco_header.py @@ -125,8 +125,8 @@ _HTML_BEGIN = """ def scodoc_top_html_header(context, REQUEST, page_title="ScoDoc"): H = [ - HTML_BEGIN % {"page_title": "ScoDoc: bienvenue", "encoding": scu.SCO_ENCODING}, - TOP_LEVEL_CSS, + _HTML_BEGIN % {"page_title": "ScoDoc: bienvenue", "encoding": scu.SCO_ENCODING}, + _TOP_LEVEL_CSS, """""", scu.CUSTOM_HTML_HEADER_CNX, ] diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py index ef397345..1343d079 100644 --- a/app/scodoc/html_sidebar.py +++ b/app/scodoc/html_sidebar.py @@ -29,7 +29,7 @@ import sco_utils as scu import sco_preferences from sco_abs import getAbsSemEtud from app.scodoc.sco_permissions import Permission - +import scolars """ Génération de la "sidebar" (marge gauche des pages HTML) diff --git a/app/scodoc/htmlutils.py b/app/scodoc/htmlutils.py index c608020e..98704af0 100644 --- a/app/scodoc/htmlutils.py +++ b/app/scodoc/htmlutils.py @@ -98,9 +98,11 @@ def make_menu(title, items, css_class="", alone=False): li_id = 'id="%s" ' % the_id else: li_id = "" - if "endpoint" in items: + if "endpoint" in item: args = item.get("args", {}) - item["urlq"] = url_for(endpoint, scodoc_dept=g.scodoc_dept, **args) + item["urlq"] = url_for( + item["endpoint"], scodoc_dept=g.scodoc_dept, **args + ) else: item["urlq"] = "#" item["attr"] = item.get("attr", "") diff --git a/app/scodoc/mails.py b/app/scodoc/mails.py index 69d4b63c..f99a17f9 100644 --- a/app/scodoc/mails.py +++ b/app/scodoc/mails.py @@ -30,6 +30,7 @@ # XXX WIP: à ré-écrire pour ScoDoc 8 (étaient des méthodes de ZScoDoc) import os +import time from email.MIMEMultipart import ( # pylint: disable=no-name-in-module,import-error MIMEMultipart, ) diff --git a/app/scodoc/notes_log.py b/app/scodoc/notes_log.py index 2f19c320..6bdbc7b6 100644 --- a/app/scodoc/notes_log.py +++ b/app/scodoc/notes_log.py @@ -113,6 +113,7 @@ def retreive_dept(): def sendAlarm(context, subj, txt): import sco_utils import mails + import sco_preferences msg = MIMEMultipart() subj = Header(subj, sco_utils.SCO_ENCODING) diff --git a/app/scodoc/notes_table.py b/app/scodoc/notes_table.py index f8b7d438..162c2f00 100644 --- a/app/scodoc/notes_table.py +++ b/app/scodoc/notes_table.py @@ -32,22 +32,9 @@ import time import pdb import inspect -import sco_core -import scolars -import sco_groups -from notes_log import log, logCallStack import sco_utils as scu import notesdb as ndb -import sco_codes_parcours -from sco_codes_parcours import DEF, UE_SPORT, UE_is_fondamentale, UE_is_professionnelle -from sco_parcours_dut import formsemestre_get_etud_capitalisation -from sco_parcours_dut import list_formsemestre_utilisateurs_uecap -import sco_parcours_dut -import sco_formsemestre -from sco_formsemestre import formsemestre_uecoef_list, formsemestre_uecoef_create -import sco_moduleimpl -import sco_evaluations -import sco_compute_moy +from notes_log import log, logCallStack from sco_formulas import NoteVector from sco_exceptions import ( AccessDenied, @@ -55,6 +42,25 @@ from sco_exceptions import ( ScoException, ScoValueError, ) +from sco_formsemestre import formsemestre_uecoef_list, formsemestre_uecoef_create +from sco_codes_parcours import DEF, UE_SPORT, UE_is_fondamentale, UE_is_professionnelle +from sco_parcours_dut import formsemestre_get_etud_capitalisation +import sco_codes_parcours +import sco_compute_moy +import sco_core +import sco_edit_matiere +import sco_edit_module +import sco_edit_ue +import sco_evaluations +import sco_formations +import sco_formsemestre +import sco_formsemestre_inscriptions +import sco_groups +import sco_moduleimpl +import sco_parcours_dut +import sco_preferences +import scolars + # Support for old user-written "bonus" functions with 2 args: BONUS_TWO_ARGS = len(inspect.getargspec(scu.CONFIG.compute_bonus)[0]) == 2 @@ -100,7 +106,9 @@ def get_sem_ues_modimpls(context, formsemestre_id, modimpls=None): ) uedict = {} for modimpl in modimpls: - mod = sco_edit_module.do_module_list(context, args={"module_id": modimpl["module_id"]})[0] + mod = sco_edit_module.do_module_list( + context, args={"module_id": modimpl["module_id"]} + )[0] modimpl["module"] = mod if not mod["ue_id"] in uedict: ue = sco_edit_ue.do_ue_list(context, args={"ue_id": mod["ue_id"]})[0] @@ -177,8 +185,8 @@ class NotesTable: context, "use_ue_coefs", formsemestre_id ) # Infos sur les etudiants - self.inscrlist = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(context, - args={"formsemestre_id": formsemestre_id} + self.inscrlist = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( + context, args={"formsemestre_id": formsemestre_id} ) # infos identite etudiant # xxx sous-optimal: 1/select par etudiant -> 0.17" pour identdict sur GTR1 ! @@ -224,13 +232,15 @@ class NotesTable: ue = uedict[mod["ue_id"]] modimpl["ue"] = ue # add ue dict to moduleimpl self._matmoys[mod["matiere_id"]] = {} - mat = sco_edit_matiere.do_matiere_list(context, args={"matiere_id": mod["matiere_id"]})[0] + mat = sco_edit_matiere.do_matiere_list( + context, args={"matiere_id": mod["matiere_id"]} + )[0] modimpl["mat"] = mat # add matiere dict to moduleimpl # calcul moyennes du module et stocke dans le module # nb_inscrits, nb_notes, nb_abs, nb_neutre, moy, median, last_modif= - self.formation = sco_formations.formation_list(context, - args={"formation_id": self.sem["formation_id"]} + self.formation = sco_formations.formation_list( + context, args={"formation_id": self.sem["formation_id"]} )[0] self.parcours = sco_codes_parcours.get_parcours_from_code( self.formation["type_parcours"] @@ -1175,7 +1185,7 @@ class NotesTable: "comp_ue_capitalisees: recomputing UE moy (etudid=%s, ue_id=%s formsemestre_id=%s)" % (etudid, ue_cap["ue_id"], ue_cap["formsemestre_id"]) ) - nt_cap = self.sco_core.get_notes_cache(context).get_NotesTable( + nt_cap = sco_core.get_notes_cache(context).get_NotesTable( self.context, ue_cap["formsemestre_id"] ) # > UE capitalisees par un etud moy_ue_cap = nt_cap.get_etud_ue_status(etudid, ue_cap["ue_id"])[ @@ -1327,188 +1337,3 @@ class NotesTable: for e in self.get_evaluations_etats() if e["moduleimpl_id"] == moduleimpl_id ] - - -import thread - - -class CacheNotesTable: - """gestion rudimentaire de cache pour les NotesTables""" - - def __init__(self): - log("new CacheTable (id=%s)" % id(self)) - # - self.lock = thread.allocate_lock() - self.owner_thread = None # thread owning this cache - self.nref = 0 - # Cache des NotesTables - self.cache = {} # { formsemestre_id : NoteTable instance } - # Cache des classeur PDF (bulletins) - self.pdfcache = {} # { formsemestre_id : (filename, pdfdoc) } - # Listeners: - self.listeners = scu.DictDefault( - defaultvalue={} - ) # {formsemestre_id : {listener_id : callback }} - - def acquire(self): - "If this thread does not own the cache, acquire the lock" - if thread.get_ident() != self.owner_thread: - if self.lock.locked(): - log( - "acquire: ident=%s waiting for lock" % thread.get_ident() - ) # XXX debug - self.lock.acquire() - self.owner_thread = thread.get_ident() - if self.owner_thread is None: # bug catching - log("WARNING: None thread id !") - self.nref += 1 - # log('nref=%d' % self.nref) - - def release(self): - "Release the lock" - cur_owner_thread = self.owner_thread - # log('release: ident=%s (nref=%d)' % (thread.get_ident(), self.nref)) - self.nref -= 1 - if self.nref == 0: - self.lock.release() - self.owner_thread = None - # Debug: - if thread.get_ident() != cur_owner_thread: - log( - "WARNING: release: ident=%s != owner=%s nref=%d" - % (thread.get_ident(), cur_owner_thread, self.nref) - ) - raise NoteProcessError("problem with notes cache") - - def get_NotesTable(self, context, formsemestre_id): # > - try: - self.acquire() - if self.cache.has_key(formsemestre_id): - # log('cache hit %s (id=%s, thread=%s)' - # % (formsemestre_id, id(self), thread.get_ident())) - return self.cache[formsemestre_id] - else: - t0 = time.time() - nt = NotesTable(context, formsemestre_id) - dt = time.time() - t0 - self.cache[formsemestre_id] = nt - log( - "caching formsemestre_id=%s (id=%s) (%gs)" - % (formsemestre_id, id(self), dt) - ) - return nt - finally: - self.release() - - def get_cached_formsemestre_ids(self): - "List of currently cached formsemestre_id" - return self.cache.keys() - - def inval_cache(self, context, formsemestre_id=None, pdfonly=False): # > - "expire cache pour un semestre (ou tous si pas d'argument)" - log( - "inval_cache, formsemestre_id=%s pdfonly=%s (id=%s)" - % (formsemestre_id, pdfonly, id(self)) # > - ) - try: - self.acquire() - if not hasattr(self, "pdfcache"): - self.pdfcache = {} # fix for old zope instances... - if formsemestre_id is None: - # clear all caches - log("----- inval_cache: clearing all caches -----") - # log('cache was containing ' + str(self.cache.keys())) - # logCallStack() # >>> DEBUG <<< - if not pdfonly: - self.cache = {} - self.pdfcache = {} - self._call_all_listeners() - sco_core.get_evaluations_cache( - context, - ).inval_cache() - else: - # formsemestre_id modifié: - # on doit virer formsemestre_id et tous les semestres - # susceptibles d'utiliser des UE capitalisées de ce semestre. - to_trash = [formsemestre_id] + list_formsemestre_utilisateurs_uecap( - context, formsemestre_id - ) - if not pdfonly: - for formsemestre_id in to_trash: - if self.cache.has_key(formsemestre_id): - log( - "delete %s from cache (id=%s)" - % (formsemestre_id, id(self)) - ) - del self.cache[formsemestre_id] - self._call_listeners(formsemestre_id) - sco_core.get_evaluations_cache( - context, - ).inval_cache() - - for formsemestre_id in to_trash: - for ( - cached_formsemestre_id, - cached_version, - ) in self.pdfcache.keys(): - if cached_formsemestre_id == formsemestre_id: - log( - "delete pdfcache[(%s,%s)]" - % (formsemestre_id, cached_version) - ) - del self.pdfcache[(formsemestre_id, cached_version)] - finally: - self.release() - - def store_bulletins_pdf(self, formsemestre_id, version, filename, pdfdoc): - "cache pdf data" - log( - "caching PDF formsemestre_id=%s version=%s (id=%s)" - % (formsemestre_id, version, id(self)) - ) - try: - self.acquire() - self.pdfcache[(formsemestre_id, version)] = (filename, pdfdoc) - finally: - self.release() - - def get_bulletins_pdf(self, formsemestre_id, version): - "returns cached PDF, or None if not in the cache" - try: - self.acquire() - if not hasattr(self, "pdfcache"): - self.pdfcache = {} # fix for old zope instances... - r = self.pdfcache.get((formsemestre_id, version), None) - if r: - log( - "get_bulletins_pdf(%s): cache hit %s (id=%s, thread=%s)" - % (version, formsemestre_id, id(self), thread.get_ident()) - ) - return r - finally: - self.release() - - def add_listener(self, callback, formsemestre_id, listener_id): - """Add a "listener": a function called each time a formsemestre is modified""" - self.listeners[formsemestre_id][listener_id] = callback - - def remove_listener(self, formsemestre_id, listener_id): - """Remove a listener. - May raise exception if does not exists. - """ - del self.listeners[formsemestre_id][listener_id] - - def _call_listeners(self, formsemestre_id): - for listener_id, callback in self.listeners[formsemestre_id].items(): - callback(listener_id) - - def _call_all_listeners(self): - for formsemestre_id in self.listeners: - self._call_listeners(formsemestre_id) - - -# -# Cache global: chaque instance, repérée par sa connexion a la DB, a un cache -# qui est recréé à la demande (voir ZNotes._getNotesCache() ) -# -NOTES_CACHE_INST = {} # { db_cnx_string : CacheNotesTable instance } diff --git a/app/scodoc/pe_avislatex.py b/app/scodoc/pe_avislatex.py index a7c2ea8d..9f4f59ca 100644 --- a/app/scodoc/pe_avislatex.py +++ b/app/scodoc/pe_avislatex.py @@ -40,6 +40,7 @@ import sco_utils as scu import notesdb as ndb from notes_log import log import scolars +import sco_preferences import pe_jurype, pe_tagtable, pe_tools from gen_tables import GenTable, SeqGenTable diff --git a/app/scodoc/pe_view.py b/app/scodoc/pe_view.py index dc090261..2021acb7 100644 --- a/app/scodoc/pe_view.py +++ b/app/scodoc/pe_view.py @@ -41,7 +41,9 @@ import sco_formsemestre import sco_formsemestre_status import notes_table from gen_tables import GenTable +import html_sco_header import sco_codes_parcours +import sco_preferences import pe_tools from pe_tools import PE_LATEX_ENCODING diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py index 0bd38dff..f65f4389 100644 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -38,9 +38,12 @@ import calendar import cgi import notesdb +from scodoc_manager import sco_mgr from sco_exceptions import ScoValueError, ScoInvalidDateError import sco_formsemestre import sco_compute_moy +import sco_preferences +import scolars def is_work_saturday(context): @@ -589,7 +592,7 @@ def getAbsSemEtud(context, sem, etudid): def getAbsSemEtuds(context, sem): - u = context.GetDBConnexionString() # identifie le dept de facon fiable + u = sco_mgr.get_db_uri() # identifie le dept de facon fiable if not u in ABS_CACHE_INST: ABS_CACHE_INST[u] = {} C = ABS_CACHE_INST[u] diff --git a/app/scodoc/sco_abs_notification.py b/app/scodoc/sco_abs_notification.py index c37c0730..03a18644 100644 --- a/app/scodoc/sco_abs_notification.py +++ b/app/scodoc/sco_abs_notification.py @@ -44,6 +44,8 @@ from notes_log import log from scolog import logdb import sco_bulletins import sco_formsemestre +import sco_preferences +import scolars def abs_notify(context, etudid, date): diff --git a/app/scodoc/sco_abs_views.py b/app/scodoc/sco_abs_views.py index 3775784f..d539cf89 100644 --- a/app/scodoc/sco_abs_views.py +++ b/app/scodoc/sco_abs_views.py @@ -37,15 +37,17 @@ from gen_tables import GenTable from notesdb import DateISOtoDMY import sco_utils as scu from sco_exceptions import ScoValueError -from sco_permissions import ScoAbsChange +from sco_permissions import Permission from notes_log import log -import sco_groups +import html_sco_header +import sco_abs import sco_find_etud import sco_formsemestre +import sco_groups import sco_moduleimpl import sco_photos - -import sco_abs +import sco_preferences +import scolars def doSignaleAbsence( diff --git a/app/scodoc/sco_apogee_compare.py b/app/scodoc/sco_apogee_compare.py index acfb3408..dee459fb 100644 --- a/app/scodoc/sco_apogee_compare.py +++ b/app/scodoc/sco_apogee_compare.py @@ -51,6 +51,8 @@ from notes_log import log import sco_apogee_csv from gen_tables import GenTable from sco_exceptions import ScoValueError +import html_sco_header +import sco_preferences _help_txt = """
diff --git a/app/scodoc/sco_apogee_csv.py b/app/scodoc/sco_apogee_csv.py index e8117e0c..c474bdfa 100644 --- a/app/scodoc/sco_apogee_csv.py +++ b/app/scodoc/sco_apogee_csv.py @@ -99,14 +99,15 @@ import sco_utils as scu import notesdb as ndb from notes_log import log from sco_exceptions import ScoValueError, FormatError -import sco_formsemestre +from gen_tables import GenTable from sco_formsemestre import ApoEtapeVDI -import sco_formsemestre_status -import sco_parcours_dut -import sco_codes_parcours from sco_codes_parcours import code_semestre_validant from sco_codes_parcours import ATT, ATB, ADM, ADC, ADJ, ATJ, ATB, AJ, CMP, NAR, RAT, DEF -from gen_tables import GenTable +import sco_codes_parcours +import sco_formsemestre +import sco_formsemestre_status +import sco_parcours_dut +import scolars APO_PORTAL_ENCODING = ( "utf8" # encodage du fichier CSV Apogée (était 'ISO-8859-1' avant jul. 2016) diff --git a/app/scodoc/sco_archives.py b/app/scodoc/sco_archives.py index ddfae603..b4140967 100644 --- a/app/scodoc/sco_archives.py +++ b/app/scodoc/sco_archives.py @@ -56,18 +56,20 @@ import sco_utils as scu from config import Config import notesdb as ndb from notes_log import log -import sco_formsemestre -import sco_pvjury -import sco_excel -import sco_pvpdf -import sco_groups -import sco_groups_view from sco_recapcomplet import make_formsemestre_recapcomplet -import sco_bulletins_pdf from TrivialFormulator import TrivialFormulator from sco_exceptions import ( AccessDenied, ) +import html_sco_header +import sco_bulletins_pdf +import sco_excel +import sco_formsemestre +import sco_groups +import sco_groups_view +import sco_permissions +import sco_pvjury +import sco_pvpdf class BaseArchiver: diff --git a/app/scodoc/sco_archives_etud.py b/app/scodoc/sco_archives_etud.py index f376f516..dd35c24e 100644 --- a/app/scodoc/sco_archives_etud.py +++ b/app/scodoc/sco_archives_etud.py @@ -38,9 +38,11 @@ import sco_groups import sco_trombino import sco_excel import sco_archives -from sco_permissions import ScoEtudAddAnnotations +from sco_permissions import Permission from sco_exceptions import AccessDenied from TrivialFormulator import TrivialFormulator +import html_sco_header +import scolars class EtudsArchiver(sco_archives.BaseArchiver): diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index 6b2a116a..84261654 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -45,22 +45,23 @@ import mails import sco_utils as scu import notesdb as ndb from notes_log import log -import scolars -from sco_permissions import ScoImplement, ScoEtudInscrit +from sco_permissions import Permission from sco_exceptions import AccessDenied -import sco_codes_parcours -import sco_formsemestre -import sco_groups -import sco_pvjury -import sco_formsemestre_status -import sco_photos import sco_abs import sco_abs_views -import sco_preferences -import sco_codes_parcours import sco_bulletins_generator -import sco_bulletins_xml import sco_bulletins_json +import sco_bulletins_xml +import sco_codes_parcours +import sco_core +import sco_formations +import sco_formsemestre +import sco_formsemestre_status +import sco_groups +import sco_photos +import sco_preferences +import sco_pvjury +import scolars def make_context_dict(context, sem, etud): @@ -131,8 +132,8 @@ def formsemestre_bulletinetud_dict( I["server_name"] = "" # Formation et parcours - I["formation"] = sco_formations.formation_list(context, - args={"formation_id": I["sem"]["formation_id"]} + I["formation"] = sco_formations.formation_list( + context, args={"formation_id": I["sem"]["formation_id"]} )[0] I["parcours"] = sco_codes_parcours.get_parcours_from_code( I["formation"]["type_parcours"] @@ -568,8 +569,8 @@ def _ue_mod_bulletin(context, etudid, formsemestre_id, ue_id, modimpls, nt, vers context, "bul_show_all_evals", formsemestre_id ): complete_eval_ids = set([e["evaluation_id"] for e in evals]) - all_evals = context.do_evaluation_list( - args={"moduleimpl_id": modimpl["moduleimpl_id"]} + all_evals = sco_evaluations.do_evaluation_list( + context, args={"moduleimpl_id": modimpl["moduleimpl_id"]} ) all_evals.reverse() # plus ancienne d'abord for e in all_evals: diff --git a/app/scodoc/sco_bulletins_generator.py b/app/scodoc/sco_bulletins_generator.py index 1d91697a..51238935 100644 --- a/app/scodoc/sco_bulletins_generator.py +++ b/app/scodoc/sco_bulletins_generator.py @@ -87,7 +87,9 @@ def bulletin_get_class(class_name): def bulletin_get_class_name_displayed(context, formsemestre_id): """Le nom du générateur utilisé, en clair""" - bul_class_name = sco_preferences.get_preference(context, "bul_class_name", formsemestre_id) + bul_class_name = sco_preferences.get_preference( + context, "bul_class_name", formsemestre_id + ) try: gen_class = bulletin_get_class(bul_class_name) return gen_class.description @@ -223,8 +225,8 @@ class BulletinGenerator: margins=self.margins, server_name=self.server_name, filigranne=self.filigranne, - preferences=self.sco_preferences.SemPreferences( - context, formsemestre_id + preferences=sco_preferences.SemPreferences( + self.context, formsemestre_id ), ) ) @@ -275,7 +277,9 @@ def make_formsemestre_bulletinetud( raise ValueError("invalid version code !") formsemestre_id = infos["formsemestre_id"] - bul_class_name = sco_preferences.get_preference(context, "bul_class_name", formsemestre_id) + bul_class_name = sco_preferences.get_preference( + context, "bul_class_name", formsemestre_id + ) try: gen_class = bulletin_get_class(bul_class_name) except: diff --git a/app/scodoc/sco_bulletins_json.py b/app/scodoc/sco_bulletins_json.py index 3098303a..2d343398 100644 --- a/app/scodoc/sco_bulletins_json.py +++ b/app/scodoc/sco_bulletins_json.py @@ -33,12 +33,16 @@ import json import sco_utils as scu import notesdb as ndb -import scolars +import sco_abs +import sco_bulletins +import sco_core +import sco_edit_ue +import sco_evaluations import sco_formsemestre import sco_groups import sco_photos -import sco_abs -import sco_bulletins +import sco_preferences +import scolars # -------- Bulletin en JSON @@ -293,8 +297,8 @@ def formsemestre_bulletinetud_published_dict( if sco_preferences.get_preference( context, "bul_show_all_evals", formsemestre_id ): - all_evals = context.do_evaluation_list( - args={"moduleimpl_id": modimpl["moduleimpl_id"]} + all_evals = sco_evaluations.do_evaluation_list( + context, args={"moduleimpl_id": modimpl["moduleimpl_id"]} ) all_evals.reverse() # plus ancienne d'abord for e in all_evals: diff --git a/app/scodoc/sco_bulletins_legacy.py b/app/scodoc/sco_bulletins_legacy.py index 653f68f2..10b8a51f 100644 --- a/app/scodoc/sco_bulletins_legacy.py +++ b/app/scodoc/sco_bulletins_legacy.py @@ -35,10 +35,8 @@ """ -import traceback, re - import sco_utils as scu -from sco_permissions import ScoEtudInscrit +from sco_permissions import Permission import sco_formsemestre import sco_pdf from sco_pdf import Color, Paragraph, Spacer, Table @@ -90,8 +88,8 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): formsemestre_id = self.infos["formsemestre_id"] context = self.context - bul_show_abs_modules = sco_preferences.get_preference(context, - "bul_show_abs_modules", formsemestre_id + bul_show_abs_modules = sco_preferences.get_preference( + context, "bul_show_abs_modules", formsemestre_id ) sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) @@ -131,7 +129,9 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): if mod["mod_moy_txt"] == "NI": continue # saute les modules où on n'est pas inscrit H.append('' % rowstyle) - if sco_preferences.get_preference(context, "bul_show_minmax_mod", formsemestre_id): + if sco_preferences.get_preference( + context, "bul_show_minmax_mod", formsemestre_id + ): rang_minmax = '%s [%s, %s]' % ( mod["mod_rang_txt"], scu.fmt_note(mod["stats"]["min"]), @@ -178,7 +178,9 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): rowstyle = "" plusminus = minuslink # if ue["ue_status"]["is_capitalized"]: - if sco_preferences.get_preference(context, "bul_show_ue_cap_details", formsemestre_id): + if sco_preferences.get_preference( + context, "bul_show_ue_cap_details", formsemestre_id + ): plusminus = minuslink hide = "" else: @@ -208,7 +210,9 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): ) H.append('') - if sco_preferences.get_preference(context, "bul_show_minmax", formsemestre_id): + if sco_preferences.get_preference( + context, "bul_show_minmax", formsemestre_id + ): moy_txt = ( '%s [%s, %s]' % ( @@ -444,8 +448,8 @@ def _bulletin_pdf_table_legacy(context, I, version="long"): S = BulTableStyle() P = [] # elems pour gen. pdf formsemestre_id = I["formsemestre_id"] - bul_show_abs_modules = sco_preferences.get_preference(context, - "bul_show_abs_modules", formsemestre_id + bul_show_abs_modules = sco_preferences.get_preference( + context, "bul_show_abs_modules", formsemestre_id ) if sco_preferences.get_preference(context, "bul_show_minmax", formsemestre_id): @@ -470,7 +474,9 @@ def _bulletin_pdf_table_legacy(context, I, version="long"): if mod["mod_moy_txt"] == "NI": continue # saute les modules où on n'est pas inscrit S.modline(ue_type=ue_type) - if sco_preferences.get_preference(context, "bul_show_minmax_mod", formsemestre_id): + if sco_preferences.get_preference( + context, "bul_show_minmax_mod", formsemestre_id + ): rang_minmax = '%s [%s, %s]' % ( mod["mod_rang_txt"], scu.fmt_note(mod["stats"]["min"]), @@ -510,7 +516,9 @@ def _bulletin_pdf_table_legacy(context, I, version="long"): coef_ue = "" ue_descr = "(en cours, non prise en compte)" S.ueline() - if sco_preferences.get_preference(context, "bul_show_ue_cap_details", formsemestre_id): + if sco_preferences.get_preference( + context, "bul_show_ue_cap_details", formsemestre_id + ): list_modules(ue["modules_capitalized"]) ue_type = "cur" diff --git a/app/scodoc/sco_bulletins_pdf.py b/app/scodoc/sco_bulletins_pdf.py index 31b73faf..32145b3b 100644 --- a/app/scodoc/sco_bulletins_pdf.py +++ b/app/scodoc/sco_bulletins_pdf.py @@ -55,17 +55,19 @@ import time import pprint import traceback import re +import os import cStringIO from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate import VERSION import sco_utils as scu from notes_log import log -import sco_formsemestre import sco_bulletins - +import sco_core +import sco_formsemestre import sco_pdf -import os +import sco_preferences +import scolars def pdfassemblebulletins( diff --git a/app/scodoc/sco_bulletins_signature.py b/app/scodoc/sco_bulletins_signature.py index 3f5fd611..4e888a7b 100644 --- a/app/scodoc/sco_bulletins_signature.py +++ b/app/scodoc/sco_bulletins_signature.py @@ -31,7 +31,7 @@ XXX en projet, non finalisé: on peut utiliser en attendant les paramétrages des bulletins (malcommodes). -Il ne s'agit pas d'une "signature" électronique, mais simplement d'une image +Il ne s'agit pas d'une "signature" électronique, mais simplement d'une image que l'on intègre dans les documents pdf générés (bulletins, classeur des bulletins, envois par mail). diff --git a/app/scodoc/sco_bulletins_standard.py b/app/scodoc/sco_bulletins_standard.py index d081d124..4fc0ba0c 100644 --- a/app/scodoc/sco_bulletins_standard.py +++ b/app/scodoc/sco_bulletins_standard.py @@ -59,7 +59,7 @@ from sco_pdf import blue, cm, mm from sco_pdf import SU import sco_preferences from notes_log import log -from sco_permissions import ScoEtudInscrit +from sco_permissions import Permission from sco_codes_parcours import ( UE_COLORS, UE_DEFAULT_COLOR, @@ -105,7 +105,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): columns_ids=colkeys, pdf_table_style=pdf_style, pdf_col_widths=[colWidths[k] for k in colkeys], - preferences=self.sco_preferences.SemPreferences(context, formsemestre_id), + preferences=sco_preferences.SemPreferences(self.context, formsemestre_id), html_class="notes_bulletin", html_class_ignore_default=True, html_with_td_classes=True, diff --git a/app/scodoc/sco_bulletins_ucac.py b/app/scodoc/sco_bulletins_ucac.py index 66f99352..52439997 100644 --- a/app/scodoc/sco_bulletins_ucac.py +++ b/app/scodoc/sco_bulletins_ucac.py @@ -32,7 +32,6 @@ On redéfini la table centrale du bulletin de note et hérite de tout le reste d E. Viennet, juillet 2011 """ -import traceback import sco_utils as scu import sco_formsemestre @@ -195,7 +194,9 @@ class BulletinGeneratorUCAC(sco_bulletins_standard.BulletinGeneratorStandard): ue_type = None # --- UE capitalisée: if ue["ue_status"]["is_capitalized"]: - if sco_preferences.get_preference(context, "bul_show_ue_cap_details", formsemestre_id): + if sco_preferences.get_preference( + context, "bul_show_ue_cap_details", formsemestre_id + ): nb_modules = len(ue["modules_capitalized"]) hidden = False cssstyle = "" diff --git a/app/scodoc/sco_bulletins_xml.py b/app/scodoc/sco_bulletins_xml.py index 849e9cfe..25b65a2a 100644 --- a/app/scodoc/sco_bulletins_xml.py +++ b/app/scodoc/sco_bulletins_xml.py @@ -43,13 +43,16 @@ import jaxml import sco_utils as scu import notesdb as ndb from notes_log import log -import scolars +import sco_abs +import sco_bulletins import sco_codes_parcours +import sco_core +import sco_evaluations import sco_formsemestre import sco_groups import sco_photos -import sco_abs -import sco_bulletins +import sco_preferences +import scolars # -------- Bulletin en XML # (fonction séparée: n'utilise pas formsemestre_bulletinetud_dict() @@ -289,8 +292,8 @@ def make_xml_formsemestre_bulletinetud( if sco_preferences.get_preference( context, "bul_show_all_evals", formsemestre_id ): - all_evals = context.do_evaluation_list( - args={"moduleimpl_id": modimpl["moduleimpl_id"]} + all_evals = sco_evaluations.do_evaluation_list( + context, args={"moduleimpl_id": modimpl["moduleimpl_id"]} ) all_evals.reverse() # plus ancienne d'abord for e in all_evals: diff --git a/app/scodoc/sco_compute_moy.py b/app/scodoc/sco_compute_moy.py index 2b6f72c1..bf396765 100644 --- a/app/scodoc/sco_compute_moy.py +++ b/app/scodoc/sco_compute_moy.py @@ -33,6 +33,7 @@ import pprint from types import FloatType import sco_utils as scu +import notesdb as ndb from sco_utils import ( NOTES_ATTENTE, NOTES_NEUTRALISE, @@ -43,12 +44,14 @@ from sco_utils import ( from sco_exceptions import ScoException from notesdb import EditableTable, quote_html from notes_log import log, sendAlarm -import sco_formsemestre -import sco_moduleimpl -import sco_groups -import sco_evaluations -import sco_formulas import sco_abs +import sco_edit_module +import sco_evaluations +import sco_formsemestre +import sco_formulas +import sco_groups +import sco_moduleimpl +import scolars def moduleimpl_has_expression(context, mod): @@ -376,7 +379,7 @@ def do_formsemestre_moyennes(context, nt, formsemestre_id): liste des moduleimpls avec notes en attente. """ # sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) - # inscr = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(context, + # inscr = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(context, # args={"formsemestre_id": formsemestre_id} # ) # etudids = [x["etudid"] for x in inscr] @@ -390,7 +393,9 @@ def do_formsemestre_moyennes(context, nt, formsemestre_id): mods_att = [] expr_diags = [] for modimpl in modimpls: - mod = sco_edit_module.do_module_list(context, args={"module_id": modimpl["module_id"]})[0] + mod = sco_edit_module.do_module_list( + context, args={"module_id": modimpl["module_id"]} + )[0] modimpl["module"] = mod # add module dict to moduleimpl (used by nt) moduleimpl_id = modimpl["moduleimpl_id"] assert not D.has_key(moduleimpl_id) diff --git a/app/scodoc/sco_core.py b/app/scodoc/sco_core.py index 9c46c0f1..24f2417f 100644 --- a/app/scodoc/sco_core.py +++ b/app/scodoc/sco_core.py @@ -4,14 +4,19 @@ """essai: ceci serait un module scodoc/sco_xxx.py """ +import time +import thread import types from flask import url_for import sco_utils as scu -from notes_table import NOTES_CACHE_INST, CacheNotesTable +from notes_log import log from scodoc_manager import sco_mgr -from sco_exceptions import ScoInvalidDept +from sco_exceptions import ScoInvalidDept, NoteProcessError +from sco_parcours_dut import list_formsemestre_utilisateurs_uecap +import sco_cache +import sco_core def sco_get_version(context, REQUEST=None): @@ -19,30 +24,32 @@ def sco_get_version(context, REQUEST=None): return """

%s

""" % scu.SCOVERSION -def test_refactor(context, x=1): - x = context.toto() - y = ("context=" + sco_edit_module.module_is_locked(context, "alpha")) + "23" - z = html_sco_header.sco_header( - context, - a_long_argument_hahahahaha=1, - another_very_long_arggggggggggggg=2, - z=6, - u=99, - kkkkkk=1, - ) +# def test_refactor(context, x=1): +# x = context.toto() +# y = ("context=" + sco_edit_module.module_is_locked(context, "alpha")) + "23" +# z = html_sco_header.sco_header( +# context, +# a_long_argument_hahahahaha=1, +# another_very_long_arggggggggggggg=2, +# z=6, +# u=99, +# kkkkkk=1, +# ) # # Cache global: chaque instance, repérée par sa connexion db, a un cache # qui est recréé à la demande # +NOTES_CACHE_INST = {} # { db_cnx_string : CacheNotesTable instance } + CACHE_formsemestre_inscription = {} CACHE_evaluations = {} # cache notes evaluations def get_evaluations_cache(context): """returns cache for evaluations""" - u = context.GetDBConnexionString() + u = sco_mgr.get_db_uri() if CACHE_evaluations.has_key(u): return CACHE_evaluations[u] else: @@ -51,6 +58,183 @@ def get_evaluations_cache(context): return CACHE_evaluations[u] +class CacheNotesTable: + """gestion rudimentaire de cache pour les NotesTables""" + + def __init__(self): + log("new CacheTable (id=%s)" % id(self)) + # + self.lock = thread.allocate_lock() + self.owner_thread = None # thread owning this cache + self.nref = 0 + # Cache des NotesTables + self.cache = {} # { formsemestre_id : NoteTable instance } + # Cache des classeur PDF (bulletins) + self.pdfcache = {} # { formsemestre_id : (filename, pdfdoc) } + # Listeners: + self.listeners = scu.DictDefault( + defaultvalue={} + ) # {formsemestre_id : {listener_id : callback }} + + def acquire(self): + "If this thread does not own the cache, acquire the lock" + if thread.get_ident() != self.owner_thread: + if self.lock.locked(): + log( + "acquire: ident=%s waiting for lock" % thread.get_ident() + ) # XXX debug + self.lock.acquire() + self.owner_thread = thread.get_ident() + if self.owner_thread is None: # bug catching + log("WARNING: None thread id !") + self.nref += 1 + # log('nref=%d' % self.nref) + + def release(self): + "Release the lock" + cur_owner_thread = self.owner_thread + # log('release: ident=%s (nref=%d)' % (thread.get_ident(), self.nref)) + self.nref -= 1 + if self.nref == 0: + self.lock.release() + self.owner_thread = None + # Debug: + if thread.get_ident() != cur_owner_thread: + log( + "WARNING: release: ident=%s != owner=%s nref=%d" + % (thread.get_ident(), cur_owner_thread, self.nref) + ) + raise NoteProcessError("problem with notes cache") + + def get_NotesTable(self, context, formsemestre_id): # > + import notes_table + + try: + self.acquire() + if self.cache.has_key(formsemestre_id): + # log('cache hit %s (id=%s, thread=%s)' + # % (formsemestre_id, id(self), thread.get_ident())) + return self.cache[formsemestre_id] + else: + t0 = time.time() + nt = notes_table.NotesTable(context, formsemestre_id) + dt = time.time() - t0 + self.cache[formsemestre_id] = nt + log( + "caching formsemestre_id=%s (id=%s) (%gs)" + % (formsemestre_id, id(self), dt) + ) + return nt + finally: + self.release() + + def get_cached_formsemestre_ids(self): + "List of currently cached formsemestre_id" + return self.cache.keys() + + def inval_cache(self, context, formsemestre_id=None, pdfonly=False): # > + "expire cache pour un semestre (ou tous si pas d'argument)" + log( + "inval_cache, formsemestre_id=%s pdfonly=%s (id=%s)" + % (formsemestre_id, pdfonly, id(self)) # > + ) + try: + self.acquire() + if not hasattr(self, "pdfcache"): + self.pdfcache = {} # fix for old zope instances... + if formsemestre_id is None: + # clear all caches + log("----- inval_cache: clearing all caches -----") + # log('cache was containing ' + str(self.cache.keys())) + # logCallStack() # >>> DEBUG <<< + if not pdfonly: + self.cache = {} + self.pdfcache = {} + self._call_all_listeners() + sco_core.get_evaluations_cache( + context, + ).inval_cache() + else: + # formsemestre_id modifié: + # on doit virer formsemestre_id et tous les semestres + # susceptibles d'utiliser des UE capitalisées de ce semestre. + to_trash = [formsemestre_id] + list_formsemestre_utilisateurs_uecap( + context, formsemestre_id + ) + if not pdfonly: + for formsemestre_id in to_trash: + if self.cache.has_key(formsemestre_id): + log( + "delete %s from cache (id=%s)" + % (formsemestre_id, id(self)) + ) + del self.cache[formsemestre_id] + self._call_listeners(formsemestre_id) + sco_core.get_evaluations_cache( + context, + ).inval_cache() + + for formsemestre_id in to_trash: + for ( + cached_formsemestre_id, + cached_version, + ) in self.pdfcache.keys(): + if cached_formsemestre_id == formsemestre_id: + log( + "delete pdfcache[(%s,%s)]" + % (formsemestre_id, cached_version) + ) + del self.pdfcache[(formsemestre_id, cached_version)] + finally: + self.release() + + def store_bulletins_pdf(self, formsemestre_id, version, filename, pdfdoc): + "cache pdf data" + log( + "caching PDF formsemestre_id=%s version=%s (id=%s)" + % (formsemestre_id, version, id(self)) + ) + try: + self.acquire() + self.pdfcache[(formsemestre_id, version)] = (filename, pdfdoc) + finally: + self.release() + + def get_bulletins_pdf(self, formsemestre_id, version): + "returns cached PDF, or None if not in the cache" + try: + self.acquire() + if not hasattr(self, "pdfcache"): + self.pdfcache = {} # fix for old zope instances... + r = self.pdfcache.get((formsemestre_id, version), None) + if r: + log( + "get_bulletins_pdf(%s): cache hit %s (id=%s, thread=%s)" + % (version, formsemestre_id, id(self), thread.get_ident()) + ) + return r + finally: + self.release() + + def add_listener(self, callback, formsemestre_id, listener_id): + """Add a "listener": a function called each time a formsemestre is modified""" + self.listeners[formsemestre_id][listener_id] = callback + + def remove_listener(self, formsemestre_id, listener_id): + """Remove a listener. + May raise exception if does not exists. + """ + del self.listeners[formsemestre_id][listener_id] + + def _call_listeners(self, formsemestre_id): + for listener_id, callback in self.listeners[formsemestre_id].items(): + callback(listener_id) + + def _call_all_listeners(self): + for formsemestre_id in self.listeners: + self._call_listeners(formsemestre_id) + + def get_notes_cache(context): "returns CacheNotesTable instance for us" u = sco_mgr.get_db_uri() # identifie le dept de facon unique @@ -81,7 +265,7 @@ def inval_cache( # Cache inscriptions semestres def get_formsemestre_inscription_cache(context, format=None): - u = context.GetDBConnexionString() + u = sco_mgr.get_db_uri() if CACHE_formsemestre_inscription.has_key(u): return CACHE_formsemestre_inscription[u] else: diff --git a/app/scodoc/sco_edit_formation.py b/app/scodoc/sco_edit_formation.py index 28e6f849..13ae110d 100644 --- a/app/scodoc/sco_edit_formation.py +++ b/app/scodoc/sco_edit_formation.py @@ -33,6 +33,8 @@ import sco_utils as scu from notes_log import log from TrivialFormulator import TrivialFormulator, TF, tf_error_message import sco_codes_parcours +import sco_edit_module +import sco_edit_ue import sco_formsemestre from sco_exceptions import ScoValueError import sco_formations @@ -305,3 +307,69 @@ def do_formation_edit(context, args): sco_core.inval_cache( context, formsemestre_id=sem["formsemestre_id"] ) # > formation modif. + + +def module_move(context, module_id, after=0, REQUEST=None, redirect=1): + """Move before/after previous one (decrement/increment numero)""" + module = sco_edit_module.do_module_list(context, {"module_id": module_id})[0] + redirect = int(redirect) + after = int(after) # 0: deplace avant, 1 deplace apres + if after not in (0, 1): + raise ValueError('invalid value for "after"') + formation_id = module["formation_id"] + others = sco_edit_module.do_module_list( + context, {"matiere_id": module["matiere_id"]} + ) + # log('others=%s' % others) + if len(others) > 1: + idx = [p["module_id"] for p in others].index(module_id) + # log('module_move: after=%s idx=%s' % (after, idx)) + neigh = None # object to swap with + if after == 0 and idx > 0: + neigh = others[idx - 1] + elif after == 1 and idx < len(others) - 1: + neigh = others[idx + 1] + if neigh: # + # swap numero between partition and its neighbor + # log('moving module %s' % module_id) + cnx = ndb.GetDBConnexion() + module["numero"], neigh["numero"] = neigh["numero"], module["numero"] + if module["numero"] == neigh["numero"]: + neigh["numero"] -= 2 * after - 1 + sco_edit_module._moduleEditor.edit(cnx, module) + sco_edit_module._moduleEditor.edit(cnx, neigh) + + # redirect to ue_list page: + if redirect: + return REQUEST.RESPONSE.redirect("ue_list?formation_id=" + formation_id) + + +def ue_move(context, ue_id, after=0, REQUEST=None, redirect=1): + """Move UE before/after previous one (decrement/increment numero)""" + o = sco_edit_ue.do_ue_list(context, {"ue_id": ue_id})[0] + # log('ue_move %s (#%s) after=%s' % (ue_id, o['numero'], after)) + redirect = int(redirect) + after = int(after) # 0: deplace avant, 1 deplace apres + if after not in (0, 1): + raise ValueError('invalid value for "after"') + formation_id = o["formation_id"] + others = sco_edit_ue.do_ue_list(context, {"formation_id": formation_id}) + if len(others) > 1: + idx = [p["ue_id"] for p in others].index(ue_id) + neigh = None # object to swap with + if after == 0 and idx > 0: + neigh = others[idx - 1] + elif after == 1 and idx < len(others) - 1: + neigh = others[idx + 1] + if neigh: # + # swap numero between partition and its neighbor + # log('moving ue %s (neigh #%s)' % (ue_id, neigh['numero'])) + cnx = ndb.GetDBConnexion() + o["numero"], neigh["numero"] = neigh["numero"], o["numero"] + if o["numero"] == neigh["numero"]: + neigh["numero"] -= 2 * after - 1 + sco_edit_ue._ueEditor.edit(cnx, o) + sco_edit_ue._ueEditor.edit(cnx, neigh) + # redirect to ue_list page + if redirect: + return REQUEST.RESPONSE.redirect("ue_list?formation_id=" + o["formation_id"]) \ No newline at end of file diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index c39763a2..5317759f 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -77,13 +77,13 @@ def do_ue_create(context, args, REQUEST): "create an ue" cnx = ndb.GetDBConnexion() # check duplicates - ues = do_ue_list(context, - {"formation_id": args["formation_id"], "acronyme": args["acronyme"]} + ues = do_ue_list( + context, {"formation_id": args["formation_id"], "acronyme": args["acronyme"]} ) if ues: raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"]) # create - r = context._ueEditor.create(cnx, args) + r = _ueEditor.create(cnx, args) # news F = sco_formations.formation_list( @@ -148,7 +148,7 @@ def do_ue_delete(context, ue_id, delete_validations=False, REQUEST=None, force=F context, "DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue_id} ) cnx = ndb.GetDBConnexion() - context._ueEditor.delete(cnx, ue_id) + _ueEditor.delete(cnx, ue_id) # > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement utilisé: acceptable de tout invalider ?): sco_core.inval_cache(context) # news @@ -332,12 +332,15 @@ def ue_edit(context, ue_id=None, create=False, formation_id=None, REQUEST=None): ue_id = do_ue_create(context, tf[2], REQUEST) if parcours.UE_IS_MODULE or tf[2]["create_matiere"]: - matiere_id = sco_edit_matiere.do_matiere_create(context, - {"ue_id": ue_id, "titre": tf[2]["titre"], "numero": 1}, REQUEST + matiere_id = sco_edit_matiere.do_matiere_create( + context, + {"ue_id": ue_id, "titre": tf[2]["titre"], "numero": 1}, + REQUEST, ) if parcours.UE_IS_MODULE: # dans ce mode, crée un (unique) module dans l'UE: - _ = sco_edit_module.do_module_create(context, + _ = sco_edit_module.do_module_create( + context, { "titre": tf[2]["titre"], "code": tf[2]["acronyme"], @@ -625,7 +628,9 @@ Si vous souhaitez modifier cette formation (par exemple pour y ajouter un module H.append("") H.append('
""", ] - evals = context.do_evaluation_list_in_formsemestre(formsemestre_id) + evals = sco_evaluations.do_evaluation_list_in_formsemestre(context, formsemestre_id) if evals: H.append( """

Attention: il y a %d évaluations dans ce semestre (sa suppression entrainera l'effacement définif des notes) !

""" @@ -1324,7 +1336,9 @@ def do_formsemestre_delete(context, formsemestre_id, REQUEST): mods = context.do_moduleimpl_list(formsemestre_id=formsemestre_id) for mod in mods: # evaluations - evals = context.do_evaluation_list(args={"moduleimpl_id": mod["moduleimpl_id"]}) + evals = sco_evaluations.do_evaluation_list( + context, args={"moduleimpl_id": mod["moduleimpl_id"]} + ) for e in evals: ndb.SimpleQuery( context, diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 8073a3f4..6d1437d0 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -29,31 +29,32 @@ """ # Rewritten from ancient DTML code +from flask import current_app from notes_log import log import sco_utils as scu import notesdb as ndb -from sco_permissions import ( - ScoImplement, - ScoChangeFormation, - ScoEtudInscrit, - ScoView, - ScoEtudChangeAdr, -) +from sco_permissions import Permission from sco_exceptions import ScoValueError import VERSION import htmlutils from sco_formsemestre_custommenu import formsemestre_custommenu_html from gen_tables import GenTable +import html_sco_header import sco_archives -import sco_groups +import sco_bulletins +import sco_codes_parcours +import sco_core +import sco_compute_moy import sco_evaluations +import sco_formations import sco_formsemestre import sco_formsemestre_edit +import sco_groups import sco_moduleimpl -import sco_compute_moy -import sco_codes_parcours -import sco_bulletins +import sco_formsemestre_inscriptions +import sco_permissions +import sco_preferences # H = [ """