From c1d13d60891a17c46b5560a51df42e0c131b87a6 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 11 Oct 2021 22:22:42 +0200 Subject: [PATCH 01/27] =?UTF-8?q?Python=203:=20n'utilise=20plus=20six.=20U?= =?UTF-8?q?tilise=20syst=C3=A9matiquement=20with=20avec=20open.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/pe/pe_tools.py | 3 +- app/scodoc/gen_tables.py | 6 ++- app/scodoc/intervals.py | 3 -- app/scodoc/notes_table.py | 13 ++---- app/scodoc/sco_archives.py | 13 ++++-- app/scodoc/sco_codes_parcours.py | 1 - app/scodoc/sco_dump_db.py | 3 +- app/scodoc/sco_edt_cal.py | 6 +-- app/scodoc/sco_etud.py | 4 +- app/scodoc/sco_formsemestre_edit.py | 1 - app/scodoc/sco_formsemestre_validation.py | 5 +- app/scodoc/sco_groups.py | 6 --- app/scodoc/sco_pdf.py | 1 - app/scodoc/sco_portal_apogee.py | 4 +- app/scodoc/sco_report.py | 4 +- app/views/entreprises.py | 5 +- misc/change_enseignant.py | 57 ----------------------- requirements-3.9.txt | 1 - 18 files changed, 33 insertions(+), 103 deletions(-) delete mode 100755 misc/change_enseignant.py diff --git a/app/pe/pe_tools.py b/app/pe/pe_tools.py index aef083982..46e706eec 100644 --- a/app/pe/pe_tools.py +++ b/app/pe/pe_tools.py @@ -44,7 +44,6 @@ import unicodedata import app.scodoc.sco_utils as scu from app import log -import six PE_DEBUG = 0 @@ -145,7 +144,7 @@ def escape_for_latex(s): } exp = re.compile( "|".join( - re.escape(six.text_type(key)) + re.escape(key) for key in sorted(list(conv.keys()), key=lambda item: -len(item)) ) ) diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py index f6ac6c343..a986494ed 100644 --- a/app/scodoc/gen_tables.py +++ b/app/scodoc/gen_tables.py @@ -752,6 +752,8 @@ if __name__ == "__main__": ) document.build(objects) data = doc.getvalue() - open("/tmp/gen_table.pdf", "wb").write(data) + with open("/tmp/gen_table.pdf", "wb") as f: + f.write(data) p = T.make_page(format="pdf") - open("toto.pdf", "wb").write(p) + with open("toto.pdf", "wb") as f: + f.write(p) diff --git a/app/scodoc/intervals.py b/app/scodoc/intervals.py index 9c9b59cc5..f4159bea7 100644 --- a/app/scodoc/intervals.py +++ b/app/scodoc/intervals.py @@ -4,11 +4,8 @@ # Code from http://code.activestate.com/recipes/457411/ -from __future__ import print_function from bisect import bisect_left, bisect_right -from six.moves import zip - class intervalmap(object): """ diff --git a/app/scodoc/notes_table.py b/app/scodoc/notes_table.py index 3ded58683..a06c2ccd5 100644 --- a/app/scodoc/notes_table.py +++ b/app/scodoc/notes_table.py @@ -27,10 +27,7 @@ """Calculs sur les notes et cache des resultats """ -import inspect -import os -import pdb -import time + from operator import itemgetter from flask import g, url_for @@ -40,12 +37,8 @@ import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app import log from app.scodoc.sco_formulas import NoteVector -from app.scodoc.sco_exceptions import ( - AccessDenied, - NoteProcessError, - ScoException, - ScoValueError, -) +from app.scodoc.sco_exceptions import ScoValueError + from app.scodoc.sco_formsemestre import ( formsemestre_uecoef_list, formsemestre_uecoef_create, diff --git a/app/scodoc/sco_archives.py b/app/scodoc/sco_archives.py index 8ebab00aa..f747d9127 100644 --- a/app/scodoc/sco_archives.py +++ b/app/scodoc/sco_archives.py @@ -203,7 +203,9 @@ class BaseArchiver(object): def get_archive_description(self, archive_id): """Return description of archive""" self.initialize() - return open(os.path.join(archive_id, "_description.txt")).read() + with open(os.path.join(archive_id, "_description.txt")) as f: + descr = f.read() + return descr def create_obj_archive(self, oid: int, description: str): """Creates a new archive for this object and returns its id.""" @@ -232,9 +234,8 @@ class BaseArchiver(object): try: scu.GSL.acquire() fname = os.path.join(archive_id, filename) - f = open(fname, "wb") - f.write(data) - f.close() + with open(fname, "wb") as f: + f.write(data) finally: scu.GSL.release() return filename @@ -247,7 +248,9 @@ class BaseArchiver(object): raise ValueError("invalid filename") fname = os.path.join(archive_id, filename) log("reading archive file %s" % fname) - return open(fname, "rb").read() + with open(fname, "rb") as f: + data = f.read() + return data def get_archived_file(self, oid, archive_name, filename): """Recupere donnees du fichier indiqué et envoie au client""" diff --git a/app/scodoc/sco_codes_parcours.py b/app/scodoc/sco_codes_parcours.py index 251a7adaf..ce0ab664b 100644 --- a/app/scodoc/sco_codes_parcours.py +++ b/app/scodoc/sco_codes_parcours.py @@ -28,7 +28,6 @@ """Semestres: Codes gestion parcours (constantes) """ import collections -from six.moves import range NOTES_TOLERANCE = 0.00499999999999 # si note >= (BARRE-TOLERANCE), considere ok # (permet d'eviter d'afficher 10.00 sous barre alors que la moyenne vaut 9.999) diff --git a/app/scodoc/sco_dump_db.py b/app/scodoc/sco_dump_db.py index 126d27832..8fa2e209e 100644 --- a/app/scodoc/sco_dump_db.py +++ b/app/scodoc/sco_dump_db.py @@ -167,7 +167,8 @@ def _anonymize_db(ano_db_name): def _get_scodoc_serial(): try: - return int(open(os.path.join(scu.SCODOC_VERSION_DIR, "scodoc.sn")).read()) + with open(os.path.join(scu.SCODOC_VERSION_DIR, "scodoc.sn")) as f: + return int(f.read()) except: return 0 diff --git a/app/scodoc/sco_edt_cal.py b/app/scodoc/sco_edt_cal.py index 312a6a4fc..f526c092f 100644 --- a/app/scodoc/sco_edt_cal.py +++ b/app/scodoc/sco_edt_cal.py @@ -33,10 +33,10 @@ XXX incompatible avec les ics HyperPlanning Paris 13 (était pour GPU). """ -import six.moves.urllib.request, six.moves.urllib.error, six.moves.urllib.parse -import traceback import icalendar import pprint +import traceback +import urllib import app.scodoc.sco_utils as scu from app import log @@ -80,7 +80,7 @@ def formsemestre_load_ics(sem): ics_data = "" else: log("Loading edt from %s" % ics_url) - f = six.moves.urllib.request.urlopen( + f = urllib.request.urlopen( ics_url, timeout=5 ) # 5s TODO: add config parameter, eg for slow networks ics_data = f.read() diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index 53d68f963..a2f2aebc9 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -830,8 +830,8 @@ appreciations_edit = _appreciationsEditor.edit def read_etablissements(): filename = os.path.join(scu.SCO_TOOLS_DIR, scu.CONFIG.ETABL_FILENAME) log("reading %s" % filename) - f = open(filename) - L = [x[:-1].split(";") for x in f] + with open(filename) as f: + L = [x[:-1].split(";") for x in f] E = {} for l in L[1:]: E[l[0]] = { diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 2e238fe57..e66ca28d6 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -58,7 +58,6 @@ from app.scodoc import sco_permissions_check from app.scodoc import sco_portal_apogee from app.scodoc import sco_preferences from app.scodoc import sco_users -import six def _default_sem_title(F): diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py index 7c49c792a..5319ee2a4 100644 --- a/app/scodoc/sco_formsemestre_validation.py +++ b/app/scodoc/sco_formsemestre_validation.py @@ -27,7 +27,7 @@ """Semestres: validation semestre et UE dans parcours """ -import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error, time, datetime +import time import flask from flask import url_for, g, request @@ -494,7 +494,7 @@ def formsemestre_recap_parcours_table( with_links=False, with_all_columns=True, a_url="", - sem_info={}, + sem_info=None, show_details=False, ): """Tableau HTML recap parcours @@ -502,6 +502,7 @@ def formsemestre_recap_parcours_table( sem_info = { formsemestre_id : txt } permet d'ajouter des informations associées à chaque semestre with_all_columns: si faux, pas de colonne "assiduité". """ + sem_info = sem_info or {} H = [] linktmpl = '%s' minuslink = linktmpl % scu.icontag("minus_img", border="0", alt="-") diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index 0a776813c..dac989248 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -59,8 +59,6 @@ from app.scodoc.sco_exceptions import ScoException, AccessDenied, ScoValueError from app.scodoc.sco_permissions import Permission from app.scodoc.TrivialFormulator import TrivialFormulator -import six - def checkGroupName( groupName, @@ -732,10 +730,6 @@ def setGroups( group_name = fs[0].strip() if not group_name: continue - # ajax arguments are encoded in utf-8: - # group_name = six.text_type(group_name, "utf-8").encode( - # scu.SCO_ENCODING - # ) # #py3 #sco8 group_id = createGroup(partition_id, group_name) # Place dans ce groupe les etudiants indiqués: for etudid in fs[1:-1]: diff --git a/app/scodoc/sco_pdf.py b/app/scodoc/sco_pdf.py index c504ae176..77e6f4e1b 100755 --- a/app/scodoc/sco_pdf.py +++ b/app/scodoc/sco_pdf.py @@ -350,7 +350,6 @@ def pdf_basic_page( # Gestion du lock pdf -import threading, time, six.moves.queue, six.moves._thread class PDFLock(object): diff --git a/app/scodoc/sco_portal_apogee.py b/app/scodoc/sco_portal_apogee.py index e79b35312..07006affd 100644 --- a/app/scodoc/sco_portal_apogee.py +++ b/app/scodoc/sco_portal_apogee.py @@ -39,7 +39,6 @@ import app.scodoc.sco_utils as scu from app import log from app.scodoc.sco_exceptions import ScoValueError from app.scodoc import sco_preferences -import six SCO_CACHE_ETAPE_FILENAME = os.path.join(scu.SCO_TMP_DIR, "last_etapes.xml") @@ -386,7 +385,8 @@ def get_etapes_apogee(): # cache le resultat (utile si le portail repond de façon intermitente) if infos: log("get_etapes_apogee: caching result") - open(SCO_CACHE_ETAPE_FILENAME, "w").write(doc) + with open(SCO_CACHE_ETAPE_FILENAME, "w") as f: + f.write(doc) except: log("invalid XML response from getEtapes Web Service\n%s" % etapes_url) # Avons nous la copie d'une réponse récente ? diff --git a/app/scodoc/sco_report.py b/app/scodoc/sco_report.py index 7d9a80821..078da6697 100644 --- a/app/scodoc/sco_report.py +++ b/app/scodoc/sco_report.py @@ -31,7 +31,6 @@ """ import os import tempfile -import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error import re import time import datetime @@ -1399,7 +1398,8 @@ def graph_parcours( # Genere graphe _, path = tempfile.mkstemp(".gr") g.write(path=path, format=format) - data = open(path, "rb").read() + with open(path, "rb") as f: + data = f.read() log("dot generated %d bytes in %s format" % (len(data), format)) if not data: log("graph.to_string=%s" % g.to_string()) diff --git a/app/views/entreprises.py b/app/views/entreprises.py index 9d1e76a06..5b8cec66a 100644 --- a/app/views/entreprises.py +++ b/app/views/entreprises.py @@ -35,6 +35,7 @@ Note: Code très ancien, porté de Zope/DTML, peu utilisable import time +import urllib from flask import request from flask_login import current_user @@ -166,14 +167,14 @@ def index_html(etud_nom=None, limit=50, offset="", format="html"): if offset: webparams["offset"] = max((offset or 0) - limit, 0) prev_lnk = 'précédentes' % ( - request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams) + request.base_url + "?" + urllib.parse.urlencode(webparams) ) else: prev_lnk = "" if len(entreprises) >= limit: webparams["offset"] = (offset or 0) + limit next_lnk = 'suivantes' % ( - request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams) + request.base_url + "?" + urllib.parse.urlencode(webparams) ) else: next_lnk = "" diff --git a/misc/change_enseignant.py b/misc/change_enseignant.py deleted file mode 100755 index 438adef6b..000000000 --- a/misc/change_enseignant.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -"""Change un identifiant d'enseignant (pour corriger une erreur, typiquement un doublon) - -(à lancer en tant qu'utilisateur postgres) -Emmanuel Viennet, 2007 - 2014 -""" - -from __future__ import print_function -import pdb, os, sys -import psycopg2 -from six.moves import input - -if len(sys.argv) != 4: - print("Usage: %s database ancien_utilisateur nouvel_utilisateur" % sys.argv[0]) - print("Exemple: change_enseignant.py SCOGEII toto tata") - sys.exit(1) - -dbname = sys.argv[1] -OLD_ID = sys.argv[2] -NEW_ID = sys.argv[3] - -DBCNXSTRING = "dbname=%s" % dbname - -# Confirmation -ans = input( - "Remplacer le l'utilisateur %s par %s dans toute la base du departement %s ?" - % (OLD_ID, NEW_ID, dbname) -).strip() -if not ans or ans[0].lower() not in "oOyY": - print("annulation") - sys.exit(-1) - - -cnx = psycopg2.connect(DBCNXSTRING) - -cursor = cnx.cursor() -req = "update %s set %s=%%(new_id)s where %s=%%(old_id)s" -args = {"old_id": OLD_ID, "new_id": NEW_ID} - -tables_attr = { - "notes_formsemestre": "responsable_id", - "entreprise_contact": "enseignant", - "admissions": "rapporteur", - "notes_moduleimpl": "responsable_id", - "notes_modules_enseignants": "ens_id", - "notes_notes": "uid", - "notes_notes_log": "uid", - "notes_appreciations": "author", -} - -for (table, attr) in tables_attr.items(): - cursor.execute(req % (table, attr, attr), args) - print("table %s: %s" % (table, cursor.statusmessage)) - -cnx.commit() diff --git a/requirements-3.9.txt b/requirements-3.9.txt index 682b05053..4c83a175a 100755 --- a/requirements-3.9.txt +++ b/requirements-3.9.txt @@ -58,7 +58,6 @@ redis==3.5.3 reportlab==3.6.1 requests==2.26.0 rq==1.9.0 -six==1.16.0 SQLAlchemy==1.4.22 toml==0.10.2 urllib3==1.26.6 From 6e1bc9665dc7b63710bbabe5a4980117e8533f39 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 12 Oct 2021 16:05:50 +0200 Subject: [PATCH 02/27] renamed some group mgt methods --- app/scodoc/notesdb.py | 4 +- app/scodoc/sco_edit_ue.py | 2 +- app/scodoc/sco_formsemestre.py | 2 +- app/scodoc/sco_formsemestre_edit.py | 30 +++---------- app/scodoc/sco_groups.py | 38 ++++++----------- app/scodoc/sco_groups_copy.py | 66 +++++++++++++++++++++++++++++ app/scodoc/sco_groups_edit.py | 2 +- app/scodoc/sco_moduleimpl_status.py | 2 +- app/views/scolar.py | 13 +++++- tests/unit/test_abs_demijournee.py | 4 +- 10 files changed, 103 insertions(+), 60 deletions(-) create mode 100644 app/scodoc/sco_groups_copy.py diff --git a/app/scodoc/notesdb.py b/app/scodoc/notesdb.py index 5a53c544c..6fa29fb98 100644 --- a/app/scodoc/notesdb.py +++ b/app/scodoc/notesdb.py @@ -96,7 +96,7 @@ def DBInsertDict( convert_empty_to_nulls=1, return_id=True, ignore_conflicts=False, -): +) -> int: """insert into table values in dict 'vals' Return: id de l'object créé """ @@ -327,7 +327,7 @@ class EditableTable(object): self.sql_default_values = None self.insert_ignore_conflicts = insert_ignore_conflicts - def create(self, cnx, args): + def create(self, cnx, args) -> int: "create object in table" vals = dictfilter(args, self.dbfields, self.filter_nulls) if self.id_name in vals: diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index dc1b4f84e..8cf7c3c05 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -461,7 +461,7 @@ def ue_list(formation_id=None, msg=""): else: lockicon = "" - arrow_up, arrow_down, arrow_none = sco_groups.getArrowIconsTags() + arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags() delete_icon = scu.icontag( "delete_small_img", title="Supprimer (module inutilisé)", alt="supprimer" ) diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index e65a92059..6d29b8cf2 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -246,7 +246,7 @@ def do_formsemestre_create(args, silent=False): default=True, redirect=0, ) - _group_id = sco_groups.createGroup(partition_id, default=True) + _group_id = sco_groups.create_group(partition_id, default=True) # news if "titre" not in args: diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index e66ca28d6..fb6c6a54b 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -51,6 +51,7 @@ from app.scodoc import sco_etud from app.scodoc import sco_evaluations from app.scodoc import sco_formations from app.scodoc import sco_formsemestre +from app.scodoc import sco_groups_copy from app.scodoc import sco_modalites from app.scodoc import sco_moduleimpl from app.scodoc import sco_parcours_dut @@ -1073,32 +1074,11 @@ def do_formsemestre_clone( args["formsemestre_id"] = formsemestre_id _ = sco_compute_moy.formsemestre_ue_computation_expr_create(cnx, args) - # 5- Copy partitions + # 5- Copy partitions and groups if clone_partitions: - listgroups = [] - listnamegroups = [] - # Création des partitions: - for part in sco_groups.get_partitions_list(orig_formsemestre_id): - if part["partition_name"] != None: - partname = part["partition_name"] - new_partition_id = sco_groups.partition_create( - formsemestre_id, - partition_name=partname, - redirect=0, - ) - for g in sco_groups.get_partition_groups(part): - if g["group_name"] != None: - listnamegroups.append(g["group_name"]) - listgroups.append([new_partition_id, listnamegroups]) - listnamegroups = [] - - # Création des groupes dans les nouvelles partitions: - for newpart in sco_groups.get_partitions_list(formsemestre_id): - for g in listgroups: - if newpart["partition_id"] == g[0]: - part_id = g[0] - for group_name in g[1]: - _ = sco_groups.createGroup(part_id, group_name=group_name) + sco_groups_copy.clone_partitions_and_groups( + orig_formsemestre_id, formsemestre_id + ) return formsemestre_id diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index dac989248..dd1de1f33 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -60,18 +60,6 @@ from app.scodoc.sco_permissions import Permission from app.scodoc.TrivialFormulator import TrivialFormulator -def checkGroupName( - groupName, -): # XXX unused: now allow any string as a group or partition name - "Raises exception if not a valid group name" - if groupName and ( - not re.match(r"^\w+$", groupName) - or (scu.simplesqlquote(groupName) != groupName) - ): - log("!!! invalid group name: " + groupName) - raise ValueError("invalid group name: " + groupName) - - partitionEditor = ndb.EditableTable( "partition", "partition_id", @@ -217,7 +205,7 @@ def get_default_group(formsemestre_id, fix_if_missing=False): partition_id = partition_create( formsemestre_id, default=True, redirect=False ) - group_id = createGroup(partition_id, default=True) + group_id = create_group(partition_id, default=True) return group_id # debug check if len(r) != 1: @@ -722,7 +710,7 @@ def setGroups( # Supprime les groupes indiqués comme supprimés: for group_id in groupsToDelete: - suppressGroup(group_id, partition_id=partition_id) + delete_group(group_id, partition_id=partition_id) # Crée les nouveaux groupes for line in groupsToCreate.split("\n"): # for each group_name (one per line) @@ -730,7 +718,7 @@ def setGroups( group_name = fs[0].strip() if not group_name: continue - group_id = createGroup(partition_id, group_name) + group_id = create_group(partition_id, group_name) # Place dans ce groupe les etudiants indiqués: for etudid in fs[1:-1]: change_etud_group_in_partition(etudid, group_id, partition) @@ -743,7 +731,7 @@ def setGroups( return response -def createGroup(partition_id, group_name="", default=False): +def create_group(partition_id, group_name="", default=False) -> int: """Create a new group in this partition""" partition = get_partition(partition_id) formsemestre_id = partition["formsemestre_id"] @@ -763,12 +751,12 @@ def createGroup(partition_id, group_name="", default=False): group_id = groupEditor.create( cnx, {"partition_id": partition_id, "group_name": group_name} ) - log("createGroup: created group_id=%s" % group_id) + log("create_group: created group_id=%s" % group_id) # return group_id -def suppressGroup(group_id, partition_id=None): +def delete_group(group_id, partition_id=None): """form suppression d'un groupe. (ne desinscrit pas les etudiants, change juste leur affectation aux groupes) @@ -785,7 +773,7 @@ def suppressGroup(group_id, partition_id=None): if not sco_permissions_check.can_change_groups(partition["formsemestre_id"]): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") log( - "suppressGroup: group_id=%s group_name=%s partition_name=%s" + "delete_group: group_id=%s group_name=%s partition_name=%s" % (group_id, group["group_name"], partition["partition_name"]) ) group_delete(group) @@ -834,7 +822,7 @@ def partition_create( return partition_id -def getArrowIconsTags(): +def get_arrow_icons_tags(): """returns html tags for arrows""" # arrow_up = scu.icontag("arrow_up", title="remonter") @@ -850,7 +838,7 @@ def editPartitionForm(formsemestre_id=None): if not sco_permissions_check.can_change_groups(formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") partitions = get_partitions_list(formsemestre_id) - arrow_up, arrow_down, arrow_none = getArrowIconsTags() + arrow_up, arrow_down, arrow_none = get_arrow_icons_tags() suppricon = scu.icontag( "delete_small_img", border="0", alt="supprimer", title="Supprimer" ) @@ -1153,7 +1141,7 @@ def partition_set_name(partition_id, partition_name, redirect=1): ) -def group_set_name(group_id, group_name, redirect=1): +def group_set_name(group_id, group_name, redirect=True): """Set group name""" if group_name: group_name = group_name.strip() @@ -1223,7 +1211,7 @@ def group_rename(group_id): ) else: # form submission - return group_set_name(group_id, tf[2]["group_name"], redirect=1) + return group_set_name(group_id, tf[2]["group_name"]) def groups_auto_repartition(partition_id=None): @@ -1298,7 +1286,7 @@ def groups_auto_repartition(partition_id=None): # except: # H.append('

Nom de groupe invalide: %s

'%group_name) # return '\n'.join(H) + tf[1] + html_sco_header.sco_footer() - group_ids.append(createGroup(partition_id, group_name)) + group_ids.append(create_group(partition_id, group_name)) # nt = sco_cache.NotesTableCache.get(formsemestre_id) # > identdict identdict = nt.identdict @@ -1382,7 +1370,7 @@ def create_etapes_partition(formsemestre_id, partition_name="apo_etapes"): groups_by_names = {g["group_name"]: g for g in groups} for etape in etapes: if not (etape in groups_by_names): - gid = createGroup(pid, etape) + gid = create_group(pid, etape) g = get_group(gid) groups_by_names[etape] = g # Place les etudiants dans les groupes diff --git a/app/scodoc/sco_groups_copy.py b/app/scodoc/sco_groups_copy.py new file mode 100644 index 000000000..77d6e1d02 --- /dev/null +++ b/app/scodoc/sco_groups_copy.py @@ -0,0 +1,66 @@ +from app import db + +from app.scodoc import sco_groups +import app.scodoc.notesdb as ndb + + +def clone_partitions_and_groups( + orig_formsemestre_id: int, formsemestre_id: int, inscrit_etuds=False +): + """Crée dans le semestre formsemestre_id les mêmes partitions et groupes que ceux + de orig_formsemestre_id. + Si inscrit_etuds, inscrit les mêmes étudiants (rarement souhaité). + """ + list_groups_per_part = [] + list_groups = [] + groups_old2new = {} # old group_id : new_group_id + # Création des partitions: + for part in sco_groups.get_partitions_list(orig_formsemestre_id): + if part["partition_name"] is not None: + partname = part["partition_name"] + new_partition_id = sco_groups.partition_create( + formsemestre_id, + partition_name=partname, + numero=part["numero"], + redirect=False, + ) + for group in sco_groups.get_partition_groups(part): + if group["group_name"] != None: + list_groups.append(group) + list_groups_per_part.append([new_partition_id, list_groups]) + list_groups = [] + + # Création des groupes dans les nouvelles partitions: + for newpart in sco_groups.get_partitions_list(formsemestre_id): + for (new_partition_id, list_groups) in list_groups_per_part: + if newpart["partition_id"] == new_partition_id: + for group in list_groups: + new_group_id = sco_groups.create_group( + new_partition_id, group_name=group["group_name"] + ) + groups_old2new[group["group_id"]] = new_group_id + # + if inscrit_etuds: + cnx = ndb.GetDBConnexion() + cursor = cnx.cursor() + for old_group_id, new_group_id in groups_old2new.items(): + cursor.execute( + """ + WITH etuds AS ( + SELECT gm.etudid + FROM group_membership gm, notes_formsemestre_inscription ins + WHERE ins.etudid = gm.etudid + AND ins.formsemestre_id = %(orig_formsemestre_id)s + AND gm.group_id=%(old_group_id)s + ) + INSERT INTO group_membership (etudid, group_id) + SELECT *, %(new_group_id)s FROM etuds + ON CONFLICT DO NOTHING + """, + { + "orig_formsemestre_id": orig_formsemestre_id, + "old_group_id": old_group_id, + "new_group_id": new_group_id, + }, + ) + cnx.commit() diff --git a/app/scodoc/sco_groups_edit.py b/app/scodoc/sco_groups_edit.py index bc4196900..d5e09b76d 100644 --- a/app/scodoc/sco_groups_edit.py +++ b/app/scodoc/sco_groups_edit.py @@ -42,7 +42,7 @@ def affect_groups(partition_id): partition = sco_groups.get_partition(partition_id) formsemestre_id = partition["formsemestre_id"] if not sco_groups.sco_permissions_check.can_change_groups(formsemestre_id): - raise AccessDenied("vous n'avez pas la permission d'effectuer cette opération") + raise AccessDenied("vous n'avez pas la permission de modifier les groupes") return render_template( "scolar/affect_groups.html", sco_header=html_sco_header.sco_header( diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index 633b73684..c358b25c0 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -176,7 +176,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None): current_user, moduleimpl_id, allow_ens=sem["ens_can_edit_eval"] ) caneditnotes = sco_permissions_check.can_edit_notes(current_user, moduleimpl_id) - arrow_up, arrow_down, arrow_none = sco_groups.getArrowIconsTags() + arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags() # module_resp = User.query.get(M["responsable_id"]) H = [ diff --git a/app/views/scolar.py b/app/views/scolar.py index 3e5f3c3d4..0e06d9246 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -669,9 +669,18 @@ sco_publish( sco_publish("/setGroups", sco_groups.setGroups, Permission.ScoView) -sco_publish("/createGroup", sco_groups.createGroup, Permission.ScoView) +sco_publish("/create_group", sco_groups.create_group, Permission.ScoView) + + +@bp.route("/suppressGroup") # backward compat (ScoDoc7 API) +@bp.route("/delete_group") +@scodoc +@permission_required(Permission.ScoView) +@scodoc7func +def delete_group(group_id, partition_id): + sco_groups.delete_group(group_id=group_id, partition_id=partition_id) + return "", 204 -sco_publish("/suppressGroup", sco_groups.suppressGroup, Permission.ScoView) sco_publish( "/group_set_name", diff --git a/tests/unit/test_abs_demijournee.py b/tests/unit/test_abs_demijournee.py index 40a617f9e..46a81bba3 100644 --- a/tests/unit/test_abs_demijournee.py +++ b/tests/unit/test_abs_demijournee.py @@ -119,7 +119,7 @@ def test_abs_basic(test_client): - sco_abs.get_abs_count(etudid, sem) - ListeAbsEtud - partition_create - - createGroup + - create_group - set_group - EtatAbsenceGr - AddBilletAbsence @@ -268,7 +268,7 @@ def test_abs_basic(test_client): partition_name="Eleve", ) li1 = sco_groups.get_partitions_list(sem["formsemestre_id"]) - _ = sco_groups.createGroup(li1[0]["partition_id"], "Groupe 1") + _ = sco_groups.create_group(li1[0]["partition_id"], "Groupe 1") # --- Affectation des élèves dans des groupes From dc26d1edea1a938e56e864aabdc89c3733b8f792 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 13 Oct 2021 10:33:20 +0200 Subject: [PATCH 03/27] =?UTF-8?q?Modif=20mail=20import=20user.=20A=20compl?= =?UTF-8?q?=C3=A9ter=20suivant=20la=20PR=20de=20JMP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_import_users.py | 57 ++++++++++++++-------------------- app/scodoc/sco_users.py | 6 ++-- sco_version.py | 2 +- 3 files changed, 28 insertions(+), 37 deletions(-) diff --git a/app/scodoc/sco_import_users.py b/app/scodoc/sco_import_users.py index f5056b59c..7a779b959 100644 --- a/app/scodoc/sco_import_users.py +++ b/app/scodoc/sco_import_users.py @@ -27,27 +27,23 @@ """Import d'utilisateurs via fichier Excel """ -import random, time -import re +import random +import time from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.header import Header +from flask import g, url_for +from flask_login import current_user -from app import db, Departement +from app import db +from app import email +from app.auth.models import User, UserRole import app.scodoc.sco_utils as scu from app import log -from app.scodoc.sco_exceptions import AccessDenied, ScoValueError, ScoException +from app.scodoc.sco_exceptions import AccessDenied, ScoValueError from app.scodoc import sco_excel from app.scodoc import sco_preferences from app.scodoc import sco_users -from flask import g -from flask_login import current_user -from app.auth.models import User, UserRole - -from app import email - TITLES = ("user_name", "nom", "prenom", "email", "roles", "dept") COMMENTS = ( @@ -90,7 +86,7 @@ def import_excel_file(datafile): """ Import scodoc users from Excel file. This method: - * checks that the current_user has the ability to do so (at the moment only a SuperAdmin). He may thereoff import users with any well formed role into any deprtment (or all) + * checks that the current_user has the ability to do so (at the moment only a SuperAdmin). He may thereoff import users with any well formed role into any department (or all) * Once the check is done ans successfull, build the list of users (does not check the data) * call :func:`import_users` to actually do the job history: scodoc7 with no SuperAdmin every Admin_XXX could import users. @@ -98,7 +94,6 @@ def import_excel_file(datafile): :return: same as import users """ # Check current user privilege - auth_dept = current_user.dept auth_name = str(current_user) if not current_user.is_administrator(): raise AccessDenied("invalid user (%s) must be SuperAdmin" % auth_name) @@ -127,7 +122,8 @@ def import_excel_file(datafile): del cols[tit] if cols or unknown: raise ScoValueError( - "colonnes incorrectes (on attend %d, et non %d)
(colonnes manquantes: %s, colonnes invalides: %s)" + """colonnes incorrectes (on attend %d, et non %d)
+ (colonnes manquantes: %s, colonnes invalides: %s)""" % (len(TITLES), len(fs), list(cols.keys()), unknown) ) # ok, same titles... : build the list of dictionaries @@ -192,9 +188,7 @@ def import_users(users): ) if not user_ok: append_msg("identifiant '%s' %s" % (u["user_name"], msg)) - # raise ScoValueError( - # "données invalides pour %s: %s" % (u["user_name"], msg) - # ) + u["passwd"] = generate_password() # # check identifiant @@ -224,7 +218,7 @@ def import_users(users): import_ok = False except ScoValueError as value_error: log("import_users: exception: abort create %s" % str(created.keys())) - raise ScoValueError(msg) # re-raise exception + raise ScoValueError(msg) from value_error if import_ok: for u in created.values(): # Création de l'utilisateur (via SQLAlchemy) @@ -244,7 +238,7 @@ def import_users(users): ALPHABET = r"""ABCDEFGHIJKLMNPQRSTUVWXYZ123456789123456789AEIOU""" -PASSLEN = 6 +PASSLEN = 8 RNG = random.Random(time.time()) @@ -259,23 +253,18 @@ def generate_password(): return "".join(RNG.sample(l, PASSLEN)) -def mail_password(u, context=None, reset=False): +def mail_password(user: dict, reset=False) -> None: "Send password by email" - if not u["email"]: + if not user["email"]: return - u[ - "url" - ] = ( - scu.ScoURL() - ) # TODO set auth page URL ? (shared by all departments) ../auth/login - + user["url"] = url_for("scodoc.index") txt = ( """ Bonjour %(prenom)s %(nom)s, """ - % u + % user ) if reset: txt += ( @@ -285,10 +274,10 @@ votre mot de passe ScoDoc a été ré-initialisé. Le nouveau mot de passe est: %(passwd)s Votre nom d'utilisateur est %(user_name)s -Vous devrez changer ce mot de passe lors de votre première connexion +Vous devrez changer ce mot de passe lors de votre première connexion sur %(url)s """ - % u + % user ) else: txt += ( @@ -303,13 +292,13 @@ Le logiciel est accessible sur: %(url)s Vous êtes invité à changer ce mot de passe au plus vite (cliquez sur votre nom en haut à gauche de la page d'accueil). """ - % u + % user ) txt += ( """ -ScoDoc est un logiciel libre développé à l'Université Paris 13 par Emmanuel Viennet. +ScoDoc est un logiciel libre développé par Emmanuel Viennet et l'association ScoDoc. Pour plus d'informations sur ce logiciel, voir %s """ @@ -321,4 +310,4 @@ Pour plus d'informations sur ce logiciel, voir %s else: subject = "Votre accès ScoDoc" sender = sco_preferences.get_preference("email_from_addr") - email.send_email(subject, sender, [u["email"]], txt) + email.send_email(subject, sender, [user["email"]], txt) diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index 1dd104b6a..74853ca80 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -400,9 +400,11 @@ def check_modif_user( returns (ok, msg) - ok : si vrai, peut continuer avec ces parametres (si ok est faux, l'utilisateur peut quand même forcer la creation) - - msg: message warning a presenter l'utilisateur + - msg: message warning à presenter à l'utilisateur """ - MSG_OPT = """Attention: %s (vous pouvez forcer l'opération en cochant "Ignorer les avertissements" en bas de page)""" + MSG_OPT = """ + (vous pouvez forcer l'opération en cochant "Ignorer les avertissements" en bas de page) + """ # ce login existe ? user = _user_list(user_name) if edit and not user: # safety net, le user_name ne devrait pas changer diff --git a/sco_version.py b/sco_version.py index 1505aa7a3..34c13b803 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.52" +SCOVERSION = "9.0.53" SCONAME = "ScoDoc" From 071c15af79b1f9a20fc1ed3d4a2d4743f99746bb Mon Sep 17 00:00:00 2001 From: Jean-Marie Place Date: Wed, 13 Oct 2021 15:03:41 +0200 Subject: [PATCH 04/27] complements_import_users_from_9.0.52 --- app/scodoc/sco_import_users.py | 3 ++- app/scodoc/sco_users.py | 10 ++++++---- app/views/users.py | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/scodoc/sco_import_users.py b/app/scodoc/sco_import_users.py index 7a779b959..834b8886a 100644 --- a/app/scodoc/sco_import_users.py +++ b/app/scodoc/sco_import_users.py @@ -179,11 +179,12 @@ def import_users(users): user_ok, msg = sco_users.check_modif_user( 0, ignore_optionals=False, + ajout_en_masse=True, user_name=u["user_name"], nom=u["nom"], prenom=u["prenom"], email=u["email"], - roles=u["roles"].split(","), + roles=[r for r in u["roles"].split(",") if r], dept=u["dept"], ) if not user_ok: diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index 74853ca80..f2ca5b829 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -388,6 +388,7 @@ def user_info_page(user_name=None): def check_modif_user( edit, ignore_optionals=False, + ajout_en_masse=False, user_name="", nom="", prenom="", @@ -402,9 +403,10 @@ def check_modif_user( (si ok est faux, l'utilisateur peut quand même forcer la creation) - msg: message warning à presenter à l'utilisateur """ - MSG_OPT = """ - (vous pouvez forcer l'opération en cochant "Ignorer les avertissements" en bas de page) - """ + if ajout_en_masse: + MSG_OPT = """
Impossible de forcer l'opération lors d'une importation en masse""" + else: + MSG_OPT = """
Attention: (vous pouvez forcer l'opération en cochant "Ignorer les avertissements" en bas de page)""" # ce login existe ? user = _user_list(user_name) if edit and not user: # safety net, le user_name ne devrait pas changer @@ -438,7 +440,7 @@ def check_modif_user( and dept != "" and Departement.query.filter_by(acronym=dept).first() is None ): - return False, "département '%s' inexistant" % u["dept"] + MSG_OPT + return False, "département '%s' inexistant" % dept + MSG_OPT if ignore_optionals and not roles: return False, "aucun rôle sélectionné, êtes vous sûr ?" + MSG_OPT # ok diff --git a/app/views/users.py b/app/views/users.py index 0143e4aee..21ea0dea8 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -380,14 +380,14 @@ def create_user_form(user_name=None, edit=0, all_roles=1): if err: H.append(tf_error_message("""Erreur: %s""" % err)) return "\n".join(H) + "\n" + tf[1] + F - ok, msg = sco_users.check_modif_user( edit, - ignore_optionals=force, + ignore_optionals=not force, user_name=user_name, nom=vals["nom"], prenom=vals["prenom"], email=vals["email"], + dept=vals["dept"], roles=vals["roles"], ) if not ok: From feb57c2ac6a958e23a19646d13ed65ba26ef58fe Mon Sep 17 00:00:00 2001 From: Jean-Marie Place Date: Wed, 13 Oct 2021 15:27:19 +0200 Subject: [PATCH 05/27] =?UTF-8?q?redirection=20vers=20all=5Fdepts=20apres?= =?UTF-8?q?=20cr=C3=A9ation=20;=20blackify?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_users.py | 4 +++- app/views/users.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index f2ca5b829..a8d15e963 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -404,7 +404,9 @@ def check_modif_user( - msg: message warning à presenter à l'utilisateur """ if ajout_en_masse: - MSG_OPT = """
Impossible de forcer l'opération lors d'une importation en masse""" + MSG_OPT = ( + """
Impossible de forcer l'opération lors d'une importation en masse""" + ) else: MSG_OPT = """
Attention: (vous pouvez forcer l'opération en cochant "Ignorer les avertissements" en bas de page)""" # ce login existe ? diff --git a/app/views/users.py b/app/views/users.py index 21ea0dea8..93687fce7 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -548,7 +548,7 @@ def import_users_form(): H.append("
  • %s
  • " % d) H.append("") if ok: - dest = url_for("users.index_html", scodoc_dept=g.scodoc_dept) + dest = url_for("users.index_html", scodoc_dept=g.scodoc_dept, all_depts=1) H.append("

    Ok, Import terminé (%s utilisateurs créés)!

    " % nb_created) H.append('

    Continuer

    ' % dest) else: From 1b8186e69b4a889509da55cb69a1f498c19e5c14 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 13 Oct 2021 15:56:24 +0200 Subject: [PATCH 06/27] =?UTF-8?q?am=C3=A9liore=20gestion=20erreur=20saisie?= =?UTF-8?q?s=20de=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_saisie_notes.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index c4c60d816..90dec7307 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -45,6 +45,7 @@ from app.scodoc.sco_exceptions import ( AccessDenied, InvalidNoteValue, NoteProcessError, + ScoGenError, ScoValueError, ) from app.scodoc.sco_permissions import Permission @@ -72,13 +73,16 @@ def convert_note_from_string( note_max, note_min=scu.NOTES_MIN, etudid=None, - absents=[], - tosuppress=[], - invalids=[], + absents=None, + tosuppress=None, + invalids=None, ): """converti une valeur (chaine saisie) vers une note numérique (float) Les listes absents, tosuppress et invalids sont modifiées """ + absents = absents or [] + tosuppress = tosuppress or [] + invalids = invalids or [] invalid = False note_value = None note = note.replace(",", ".") @@ -179,7 +183,6 @@ def do_evaluation_upload_xls(): # Check access # (admin, respformation, and responsable_id) if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]): - # XXX imaginer un redirect + msg erreur raise AccessDenied("Modification des notes impossible pour %s" % authuser) # diag, lines = sco_excel.excel_file_to_list(vals["notefile"]) @@ -573,12 +576,13 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True): except: log("*** exception in _notes_add") if do_it: + cnx.rollback() # abort # inval cache sco_cache.invalidate_formsemestre( formsemestre_id=M["formsemestre_id"] ) # > modif notes (exception) - cnx.rollback() # abort - raise # re-raise exception + sco_cache.EvaluationCache.delete(evaluation_id) + raise ScoGenError("Erreur enregistrement note: merci de ré-essayer") if do_it: cnx.commit() sco_cache.invalidate_formsemestre( @@ -588,7 +592,7 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True): return nb_changed, nb_suppress, existing_decisions -def saisie_notes_tableur(evaluation_id, group_ids=[]): +def saisie_notes_tableur(evaluation_id, group_ids=()): """Saisie des notes via un fichier Excel""" evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id}) if not evals: From 9c528bec7ffc3e4fc29ad5fa5d78fe7b940102c2 Mon Sep 17 00:00:00 2001 From: Jean-Marie Place Date: Wed, 13 Oct 2021 16:32:43 +0200 Subject: [PATCH 07/27] =?UTF-8?q?permet=20de=20lever=20certaines=20v=C3=A9?= =?UTF-8?q?rifications=20lors=20de=20l=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_import_users.py | 9 ++++----- app/scodoc/sco_users.py | 22 ++++++++-------------- app/views/users.py | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/app/scodoc/sco_import_users.py b/app/scodoc/sco_import_users.py index 834b8886a..804cb6ace 100644 --- a/app/scodoc/sco_import_users.py +++ b/app/scodoc/sco_import_users.py @@ -82,7 +82,7 @@ def generate_excel_sample(): ) -def import_excel_file(datafile): +def import_excel_file(datafile, force=""): """ Import scodoc users from Excel file. This method: @@ -134,10 +134,10 @@ def import_excel_file(datafile): d[fs[i]] = line[i] users.append(d) - return import_users(users) + return import_users(users=users, force=force) -def import_users(users): +def import_users(users, force=""): """ Import users from a list of users_descriptors. @@ -178,8 +178,7 @@ def import_users(users): line = line + 1 user_ok, msg = sco_users.check_modif_user( 0, - ignore_optionals=False, - ajout_en_masse=True, + enforce_optionals=not force, user_name=u["user_name"], nom=u["nom"], prenom=u["prenom"], diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index a8d15e963..be43125d9 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -387,8 +387,7 @@ def user_info_page(user_name=None): def check_modif_user( edit, - ignore_optionals=False, - ajout_en_masse=False, + enforce_optionals=False, user_name="", nom="", prenom="", @@ -403,12 +402,7 @@ def check_modif_user( (si ok est faux, l'utilisateur peut quand même forcer la creation) - msg: message warning à presenter à l'utilisateur """ - if ajout_en_masse: - MSG_OPT = ( - """
    Impossible de forcer l'opération lors d'une importation en masse""" - ) - else: - MSG_OPT = """
    Attention: (vous pouvez forcer l'opération en cochant "Ignorer les avertissements" en bas de page)""" + MSG_OPT = """
    Attention: (vous pouvez forcer l'opération en cochant "Ignorer les avertissements" en bas de page)""" # ce login existe ? user = _user_list(user_name) if edit and not user: # safety net, le user_name ne devrait pas changer @@ -423,11 +417,11 @@ def check_modif_user( "identifiant '%s' invalide (pas d'accents ni de caractères spéciaux)" % user_name, ) - if ignore_optionals and len(user_name) > 64: + if enforce_optionals and len(user_name) > 64: return False, "identifiant '%s' trop long (64 caractères)" % user_name - if ignore_optionals and len(nom) > 64: + if enforce_optionals and len(nom) > 64: return False, "nom '%s' trop long (64 caractères)" % nom + MSG_OPT - if ignore_optionals and len(prenom) > 64: + if enforce_optionals and len(prenom) > 64: return False, "prenom '%s' trop long (64 caractères)" % prenom + MSG_OPT # check that tha same user_name has not already been described in this import if not email: @@ -438,12 +432,12 @@ def check_modif_user( return False, "l'adresse mail semble incorrecte" # check département if ( - ignore_optionals + enforce_optionals and dept != "" and Departement.query.filter_by(acronym=dept).first() is None ): return False, "département '%s' inexistant" % dept + MSG_OPT - if ignore_optionals and not roles: + if enforce_optionals and not roles: return False, "aucun rôle sélectionné, êtes vous sûr ?" + MSG_OPT # ok # Des noms/prénoms semblables existent ? @@ -456,7 +450,7 @@ def check_modif_user( minmatch = 1 else: minmatch = 0 - if len(similar_users) > minmatch: + if enforce_optionals and len(similar_users) > minmatch: return ( False, "des utilisateurs proches existent: " diff --git a/app/views/users.py b/app/views/users.py index 93687fce7..cddcf1952 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -382,7 +382,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1): return "\n".join(H) + "\n" + tf[1] + F ok, msg = sco_users.check_modif_user( edit, - ignore_optionals=not force, + enforce_optionals=not force, user_name=user_name, nom=vals["nom"], prenom=vals["prenom"], @@ -531,6 +531,16 @@ def import_users_form(): "xlsfile", {"title": "Fichier Excel:", "input_type": "file", "size": 40}, ), + ( + "force", + { + "title": "Ignorer les avertissements", + "input_type": "checkbox", + "explanation": "passer outre les avertissements (homonymes, etc)", + "labels": ("",), + "allowed_values": ("1",), + }, + ), ("formsemestre_id", {"input_type": "hidden"}), ), submitlabel="Télécharger", @@ -541,7 +551,9 @@ def import_users_form(): return flask.redirect(url_for("scolar.index_html", docodc_dept=g.scodoc_dept)) else: # IMPORT - ok, diag, nb_created = sco_import_users.import_excel_file(tf[2]["xlsfile"]) + ok, diag, nb_created = sco_import_users.import_excel_file( + tf[2]["xlsfile"], tf[2]["force"] + ) H = [html_sco_header.sco_header(page_title="Import utilisateurs")] H.append("
      ") for d in diag: From 9694ba61c4317a019a3fb3f2834dbb7ba3cb5774 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 13 Oct 2021 21:00:03 +0200 Subject: [PATCH 08/27] =?UTF-8?q?Evite=20les=20erreurs=20de=20formulaires?= =?UTF-8?q?=20POST=20quand=20l'utilisateur=20s'est=20d=C3=A9connect=C3=A9?= =?UTF-8?q?=20dans=20un=20autre=20onglet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/auth/routes.py | 5 ++++- app/decorators.py | 17 ++++++++++++----- app/templates/auth/login.html | 5 +++++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/app/auth/routes.py b/app/auth/routes.py index 8f01a0c19..bf7272a75 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -46,7 +46,10 @@ def login(): if not next_page or url_parse(next_page).netloc != "": next_page = url_for("scodoc.index") return redirect(next_page) - return render_template("auth/login.html", title=_("Sign In"), form=form) + message = request.args.get("message", "") + return render_template( + "auth/login.html", title=_("Sign In"), form=form, message=message + ) @bp.route("/logout") diff --git a/app/decorators.py b/app/decorators.py index 65b89905b..df67751ae 100644 --- a/app/decorators.py +++ b/app/decorators.py @@ -10,12 +10,10 @@ import logging import werkzeug from werkzeug.exceptions import BadRequest import flask -from flask import g -from flask import abort, current_app -from flask import request +from flask import g, current_app, request +from flask import abort, url_for, redirect from flask_login import current_user from flask_login import login_required -from flask import current_app import flask_login import app @@ -52,6 +50,15 @@ def scodoc(func): @wraps(func) def scodoc_function(*args, **kwargs): + # interdit les POST si pas loggué + if request.method == "POST" and not current_user.is_authenticated: + current_app.logger.info("POST by non authenticated user") + return redirect( + url_for( + "auth.login", + message="La page a expiré. Identifiez-vous et recommencez l'opération", + ) + ) if "scodoc_dept" in kwargs: dept_acronym = kwargs["scodoc_dept"] # current_app.logger.info("setting dept to " + dept_acronym) @@ -81,7 +88,7 @@ def permission_required(permission): def permission_required_compat_scodoc7(permission): - """Décorateur pour les fonctions utilisée comme API dans ScoDoc 7 + """Décorateur pour les fonctions utilisées comme API dans ScoDoc 7 Comme @permission_required mais autorise de passer directement les informations d'auth en paramètres: __ac_name, __ac_password diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html index 2685383db..d636e053e 100644 --- a/app/templates/auth/login.html +++ b/app/templates/auth/login.html @@ -2,6 +2,11 @@ {% import 'bootstrap/wtf.html' as wtf %} {% block app_content %} + +{% if message %} + +{% endif %} +

      Connexion

      From 46269fcebeb15699236b5308cfe98029c191b135 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 13 Oct 2021 21:49:55 +0200 Subject: [PATCH 09/27] Corrige mail envoi mot de passe utilisateur --- app/scodoc/sco_import_users.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/scodoc/sco_import_users.py b/app/scodoc/sco_import_users.py index 804cb6ace..d7c360a4e 100644 --- a/app/scodoc/sco_import_users.py +++ b/app/scodoc/sco_import_users.py @@ -258,7 +258,7 @@ def mail_password(user: dict, reset=False) -> None: if not user["email"]: return - user["url"] = url_for("scodoc.index") + user["url"] = url_for("scodoc.index", _external=True) txt = ( """ Bonjour %(prenom)s %(nom)s, @@ -289,15 +289,14 @@ Votre mot de passe est: %(passwd)s Le logiciel est accessible sur: %(url)s -Vous êtes invité à changer ce mot de passe au plus vite (cliquez sur -votre nom en haut à gauche de la page d'accueil). +Vous êtes invité à changer ce mot de passe au plus vite (cliquez sur votre nom en haut à gauche de la page d'accueil). """ % user ) txt += ( """ - +_______ ScoDoc est un logiciel libre développé par Emmanuel Viennet et l'association ScoDoc. Pour plus d'informations sur ce logiciel, voir %s From e243fe6bb05148a3403bbc15b3f1da57c9639748 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 14 Oct 2021 11:01:29 +0200 Subject: [PATCH 10/27] installmgr url --- app/scodoc/sco_utils.py | 3 +-- tools/debian/postinst | 2 +- tools/install_debian11.sh | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 705277025..e5dec56b8 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -290,8 +290,7 @@ SCO_DEV_MAIL = "emmanuel.viennet@gmail.com" # SVP ne pas changer # Adresse pour l'envoi des dumps (pour assistance technnique): # ne pas changer (ou vous perdez le support) -SCO_DUMP_UP_URL = "https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/upload-dump" -# SCO_DUMP_UP_URL = "http://192.168.56.1:5000/upload_dump" +SCO_DUMP_UP_URL = "https://scodoc.org/scodoc-installmgr/upload-dump" CSV_FIELDSEP = ";" CSV_LINESEP = "\n" diff --git a/tools/debian/postinst b/tools/debian/postinst index 10c62758f..41441c8c4 100755 --- a/tools/debian/postinst +++ b/tools/debian/postinst @@ -52,7 +52,7 @@ else SN="" fi -CMD="curl --fail --connect-timeout 5 --silent https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=$mode\&release=${SCODOC_RELEASE}\&sn=${SN}" +CMD="curl --fail --connect-timeout 5 --silent https://scodoc.org/scodoc-installmgr/version?mode=$mode\&release=${SCODOC_RELEASE}\&sn=${SN}" SVERSION="$(${CMD})" if [ "$?" == 0 ]; then diff --git a/tools/install_debian11.sh b/tools/install_debian11.sh index 8d94187a7..e267cc38a 100755 --- a/tools/install_debian11.sh +++ b/tools/install_debian11.sh @@ -83,7 +83,7 @@ su -c "(cd $SCODOC_DIR && source venv/bin/activate && pip install wheel && pip i # ------------ SCODOC_RELEASE=$(grep SCOVERSION sco_version.py | awk '{ print substr($3, 2, length($3)-2) }') -SVERSION=$(curl --silent https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/version?mode=install\&release="$SCODOC_RELEASE") +SVERSION=$(curl --silent https://scodoc.org/scodoc-installmgr/version?mode=install\&release="$SCODOC_RELEASE") echo "$SVERSION" > "${SCODOC_VERSION_DIR}/scodoc.sn" From e8e3423193bcec7f0cfdae1df3c2f092dcba5b27 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 15 Oct 2021 14:00:51 +0200 Subject: [PATCH 11/27] Un peu de nettoyage de d'optimisation (gain ~ 30-40% sur calcul NT). --- README.md | 6 +-- app/api/sco_api.py | 2 +- app/scodoc/notes_table.py | 6 +-- app/scodoc/sco_abs.py | 2 +- app/scodoc/sco_abs_views.py | 6 +-- app/scodoc/sco_cache.py | 2 +- app/scodoc/sco_compute_moy.py | 8 ++-- app/scodoc/sco_cost_formation.py | 4 +- app/scodoc/sco_edit_module.py | 4 +- app/scodoc/sco_evaluations.py | 25 +++++------ app/scodoc/sco_formsemestre_edit.py | 14 +++--- app/scodoc/sco_formsemestre_inscriptions.py | 8 ++-- app/scodoc/sco_formsemestre_status.py | 14 +++--- app/scodoc/sco_groups.py | 18 ++++---- app/scodoc/sco_groups_view.py | 2 +- app/scodoc/sco_liste_notes.py | 6 +-- app/scodoc/sco_moduleimpl.py | 44 +++++++++++------- app/scodoc/sco_moduleimpl_inscriptions.py | 6 +-- app/scodoc/sco_moduleimpl_status.py | 4 +- app/scodoc/sco_news.py | 2 +- app/scodoc/sco_permissions_check.py | 6 +-- app/scodoc/sco_placement.py | 2 +- app/scodoc/sco_saisie_notes.py | 26 +++++------ app/scodoc/sco_undo_notes.py | 2 +- app/views/notes.py | 27 ++++++----- bench.py | 13 ++++++ misc/example-api-1.py | 2 +- tests/bench/notes_table.py | 50 +++++++++++++++++++++ tests/unit/sco_fake_gen.py | 2 +- tests/unit/test_formations.py | 28 +++++------- 30 files changed, 194 insertions(+), 147 deletions(-) create mode 100644 bench.py create mode 100644 tests/bench/notes_table.py diff --git a/README.md b/README.md index a757f17e4..e28a92aac 100644 --- a/README.md +++ b/README.md @@ -130,12 +130,12 @@ base de données (tous les départements, et les utilisateurs) avant de commence On utilise SQLAlchemy avec Alembic et Flask-Migrate. - flask db migrate -m "ScoDoc 9.0.x: ..." # ajuster le message ! + flask db migrate -m "message explicatif....." flask db upgrade -Ne pas oublier de commiter les migrations (`git add migrations` ...). +Ne pas oublier de d'ajouter le script de migration à git (`git add migrations/...`). -Mémo pour développeurs: séquence re-création d'une base (vérifiez votre `.env` +**Mémo**: séquence re-création d'une base (vérifiez votre `.env` ou variables d'environnement pour interroger la bonne base !). dropdb SCODOC_DEV diff --git a/app/api/sco_api.py b/app/api/sco_api.py index 46be85a7a..e2619a0b0 100644 --- a/app/api/sco_api.py +++ b/app/api/sco_api.py @@ -29,7 +29,7 @@ """ # PAS ENCORE IMPLEMENTEE, juste un essai # Pour P. Bouron, il faudrait en priorité l'équivalent de -# Scolarite/Notes/do_moduleimpl_withmodule_list +# Scolarite/Notes/moduleimpl_withmodule_list (alias scodoc7 do_moduleimpl_withmodule_list) # Scolarite/Notes/evaluation_create # Scolarite/Notes/evaluation_delete # Scolarite/Notes/formation_list diff --git a/app/scodoc/notes_table.py b/app/scodoc/notes_table.py index a06c2ccd5..5e8f2f190 100644 --- a/app/scodoc/notes_table.py +++ b/app/scodoc/notes_table.py @@ -102,7 +102,7 @@ def get_sem_ues_modimpls(formsemestre_id, modimpls=None): (utilisé quand on ne peut pas construire nt et faire nt.get_ues()) """ if modimpls is None: - modimpls = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) + modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) uedict = {} for modimpl in modimpls: mod = sco_edit_module.do_module_list(args={"module_id": modimpl["module_id"]})[ @@ -212,7 +212,7 @@ class NotesTable(object): valid_evals, mods_att, self.expr_diagnostics, - ) = sco_compute_moy.do_formsemestre_moyennes(self, formsemestre_id) + ) = sco_compute_moy.compute_modimpls_moyennes(self, formsemestre_id) self._mods_att = mods_att # liste des modules avec des notes en attente self._matmoys = {} # moyennes par matieres self._valid_evals = {} # { evaluation_id : eval } @@ -221,7 +221,7 @@ class NotesTable(object): uedict = {} # public member: { ue_id : ue } self.uedict = uedict for modimpl in self._modimpls: - mod = modimpl["module"] # has been added here by do_formsemestre_moyennes + mod = modimpl["module"] # has been added here by compute_modimpls_moyennes if not mod["ue_id"] in uedict: ue = sco_edit_ue.do_ue_list(args={"ue_id": mod["ue_id"]})[0] uedict[ue["ue_id"]] = ue diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py index 5eb7f7f4d..7d5e7976e 100644 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -474,7 +474,7 @@ def _get_abs_description(a, cursor=None): desc = a["description"] if a["moduleimpl_id"] and a["moduleimpl_id"] != "NULL": # Trouver le nom du module - Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( + Mlist = sco_moduleimpl.moduleimpl_withmodule_list( moduleimpl_id=a["moduleimpl_id"] ) if Mlist: diff --git a/app/scodoc/sco_abs_views.py b/app/scodoc/sco_abs_views.py index d4632bbc1..50984e04e 100644 --- a/app/scodoc/sco_abs_views.py +++ b/app/scodoc/sco_abs_views.py @@ -115,7 +115,7 @@ def doSignaleAbsence( J = "NON " M = "" if moduleimpl_id and moduleimpl_id != "NULL": - mod = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + mod = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] formsemestre_id = mod["formsemestre_id"] nt = sco_cache.NotesTableCache.get(formsemestre_id) ues = nt.get_ues(etudid=etudid) @@ -939,7 +939,7 @@ def _tables_abs_etud( return "" ex = [] for ev in a["evals"]: - mod = sco_moduleimpl.do_moduleimpl_withmodule_list( + mod = sco_moduleimpl.moduleimpl_withmodule_list( moduleimpl_id=ev["moduleimpl_id"] )[0] if format == "html": @@ -957,7 +957,7 @@ def _tables_abs_etud( def descr_abs(a): ex = [] for ev in a.get("absent", []): - mod = sco_moduleimpl.do_moduleimpl_withmodule_list( + mod = sco_moduleimpl.moduleimpl_withmodule_list( moduleimpl_id=ev["moduleimpl_id"] )[0] if format == "html": diff --git a/app/scodoc/sco_cache.py b/app/scodoc/sco_cache.py index 043104ed4..53a26d778 100644 --- a/app/scodoc/sco_cache.py +++ b/app/scodoc/sco_cache.py @@ -292,7 +292,7 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa class DefferedSemCacheManager: - """Experimental: pour effectuer des opérations indépendantes dans la + """Contexte pour effectuer des opérations indépendantes dans la même requete qui invalident le cache. Par exemple, quand on inscrit des étudiants un par un à un semestre, chaque inscription va invalider le cache, et la suivante va le reconstruire... pour l'invalider juste après. diff --git a/app/scodoc/sco_compute_moy.py b/app/scodoc/sco_compute_moy.py index 94d1f1f36..86ce3bd1b 100644 --- a/app/scodoc/sco_compute_moy.py +++ b/app/scodoc/sco_compute_moy.py @@ -79,7 +79,7 @@ def formsemestre_expressions_use_abscounts(formsemestre_id): if expr and expr[0] != "#" and ab in expr: return True # 2- moyennes de modules - for mod in sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id): + for mod in sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id): if moduleimpl_has_expression(mod) and ab in mod["computation_expr"]: return True return False @@ -199,7 +199,7 @@ def do_moduleimpl_moyennes(nt, mod): moduleimpl_id = mod["moduleimpl_id"] is_malus = mod["module"]["module_type"] == scu.MODULE_MALUS sem = sco_formsemestre.get_formsemestre(mod["formsemestre_id"]) - etudids = sco_moduleimpl.do_moduleimpl_listeetuds( + etudids = sco_moduleimpl.moduleimpl_listeetuds( moduleimpl_id ) # tous, y compris demissions # Inscrits au semestre (pour traiter les demissions): @@ -365,7 +365,7 @@ def do_moduleimpl_moyennes(nt, mod): return R, valid_evals, attente, diag_info -def do_formsemestre_moyennes(nt, formsemestre_id): +def compute_modimpls_moyennes(nt, formsemestre_id): """retourne dict { moduleimpl_id : { etudid, note_moyenne_dans_ce_module } }, la liste des moduleimpls, la liste des evaluations valides, liste des moduleimpls avec notes en attente. @@ -375,7 +375,7 @@ def do_formsemestre_moyennes(nt, formsemestre_id): # args={"formsemestre_id": formsemestre_id} # ) # etudids = [x["etudid"] for x in inscr] - modimpls = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) + modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) # recupere les moyennes des etudiants de tous les modules D = {} valid_evals = [] diff --git a/app/scodoc/sco_cost_formation.py b/app/scodoc/sco_cost_formation.py index 1c28ed4f1..d53d38619 100644 --- a/app/scodoc/sco_cost_formation.py +++ b/app/scodoc/sco_cost_formation.py @@ -59,9 +59,7 @@ def formsemestre_table_estim_cost( """ sem = sco_formsemestre.get_formsemestre(formsemestre_id) sco_formsemestre_status.fill_formsemestre(sem) - Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( - formsemestre_id=formsemestre_id - ) + Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) T = [] for M in Mlist: Mod = M["module"] diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 6ec8bce3e..1aeb20df6 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -257,7 +257,7 @@ def do_module_delete(oid): raise ScoLockedFormError() # S'il y a des moduleimpls, on ne peut pas detruire le module ! - mods = sco_moduleimpl.do_moduleimpl_list(module_id=oid) + mods = sco_moduleimpl.moduleimpl_list(module_id=oid) if mods: err_page = f"""

      Destruction du module impossible car il est utilisé dans des semestres existants !

      Il faut d'abord supprimer le semestre. Mais il est peut être préférable de @@ -580,7 +580,7 @@ def module_is_locked(module_id): def module_count_moduleimpls(module_id): "Number of moduleimpls using this module" - mods = sco_moduleimpl.do_moduleimpl_list(module_id=module_id) + mods = sco_moduleimpl.moduleimpl_list(module_id=module_id) return len(mods) diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py index e2faf71b9..cf6be3f13 100644 --- a/app/scodoc/sco_evaluations.py +++ b/app/scodoc/sco_evaluations.py @@ -179,7 +179,7 @@ def do_evaluation_list(args, sortkey=None): def do_evaluation_list_in_formsemestre(formsemestre_id): "list evaluations in this formsemestre" - mods = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) + mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) evals = [] for mod in mods: evals += do_evaluation_list(args={"moduleimpl_id": mod["moduleimpl_id"]}) @@ -213,7 +213,7 @@ def _check_evaluation_args(args): jour = args.get("jour", None) args["jour"] = jour if jour: - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) d, m, y = [int(x) for x in sem["date_debut"].split("/")] date_debut = datetime.date(y, m, d) @@ -301,7 +301,7 @@ def do_evaluation_create( r = _evaluationEditor.create(cnx, args) # news - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod["moduleimpl_id"] = M["moduleimpl_id"] mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod @@ -332,7 +332,7 @@ def do_evaluation_edit(args): cnx = ndb.GetDBConnexion() _evaluationEditor.edit(cnx, args) # inval cache pour ce semestre - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"]) @@ -357,7 +357,7 @@ def do_evaluation_delete(evaluation_id): _evaluationEditor.delete(cnx, evaluation_id) # inval cache pour ce semestre - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"]) # news mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] @@ -373,9 +373,6 @@ def do_evaluation_delete(evaluation_id): ) -_DEE_TOT = 0 - - def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=False): """donne infos sur l'etat du evaluation { nb_inscrits, nb_notes, nb_abs, nb_neutre, nb_att, @@ -412,7 +409,7 @@ def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition= last_modif = None # ---- Liste des groupes complets et incomplets E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] is_malus = Mod["module_type"] == scu.MODULE_MALUS # True si module de malus formsemestre_id = M["formsemestre_id"] @@ -735,7 +732,7 @@ def formsemestre_evaluations_cal(formsemestre_id): if not e["jour"]: continue day = e["jour"].strftime("%Y-%m-%d") - mod = sco_moduleimpl.do_moduleimpl_withmodule_list( + mod = sco_moduleimpl.moduleimpl_withmodule_list( moduleimpl_id=e["moduleimpl_id"] )[0] txt = mod["module"]["code"] or mod["module"]["abbrev"] or "eval" @@ -812,7 +809,7 @@ def evaluation_date_first_completion(evaluation_id): # (pour avoir l'etat et le groupe) et aussi les inscriptions # au module (pour gerer les modules optionnels correctement) # E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0] - # M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + # M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] # formsemestre_id = M["formsemestre_id"] # insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( formsemestre_id) # insmod = sco_moduleimpl.do_moduleimpl_inscription_list(moduleimpl_id=E["moduleimpl_id"]) @@ -854,7 +851,7 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"): evals = nt.get_sem_evaluation_etat_list() T = [] for e in evals: - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0] Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] if (e["evaluation_type"] != scu.EVALUATION_NORMALE) or ( Mod["module_type"] == scu.MODULE_MALUS @@ -1035,7 +1032,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True): E = do_evaluation_list({"evaluation_id": evaluation_id})[0] moduleimpl_id = E["moduleimpl_id"] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] formsemestre_id = M["formsemestre_id"] u = sco_users.user_info(M["responsable_id"]) @@ -1115,7 +1112,7 @@ def evaluation_create_form( the_eval = do_evaluation_list({"evaluation_id": evaluation_id})[0] moduleimpl_id = the_eval["moduleimpl_id"] # - M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] is_malus = M["module"]["module_type"] == scu.MODULE_MALUS # True si module de malus formsemestre_id = M["formsemestre_id"] min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index fb6c6a54b..bcfdddb70 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -171,7 +171,7 @@ def do_formsemestre_createwithmodules(edit=False): initvalues = sem semestre_id = initvalues["semestre_id"] # add associated modules to tf-checked: - ams = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) + ams = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) sem_module_ids = set([x["module_id"] for x in ams]) initvalues["tf-checked"] = ["MI" + str(x["module_id"]) for x in ams] for x in ams: @@ -751,7 +751,7 @@ def do_formsemestre_createwithmodules(edit=False): # (retire le "MI" du début du nom de champs) checkedmods = [int(x[2:]) for x in tf[2]["tf-checked"]] sco_formsemestre.do_formsemestre_edit(tf[2]) - ams = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) + ams = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) existingmods = [x["module_id"] for x in ams] mods_tocreate = [x for x in checkedmods if not x in existingmods] # modules a existants a modifier @@ -801,7 +801,7 @@ def do_formsemestre_createwithmodules(edit=False): ok, diag = formsemestre_delete_moduleimpls(formsemestre_id, mods_todelete) msg += diag for module_id in mods_toedit: - moduleimpl_id = sco_moduleimpl.do_moduleimpl_list( + moduleimpl_id = sco_moduleimpl.moduleimpl_list( formsemestre_id=formsemestre_id, module_id=module_id )[0]["moduleimpl_id"] modargs = { @@ -846,7 +846,7 @@ def formsemestre_delete_moduleimpls(formsemestre_id, module_ids_to_del): msg = [] for module_id in module_ids_to_del: # get id - moduleimpl_id = sco_moduleimpl.do_moduleimpl_list( + moduleimpl_id = sco_moduleimpl.moduleimpl_list( formsemestre_id=formsemestre_id, module_id=module_id )[0]["moduleimpl_id"] mod = sco_edit_module.do_module_list({"module_id": module_id})[0] @@ -1015,7 +1015,7 @@ def do_formsemestre_clone( formsemestre_id = sco_formsemestre.do_formsemestre_create(args) log("created formsemestre %s" % formsemestre_id) # 2- create moduleimpls - mods_orig = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=orig_formsemestre_id) + mods_orig = sco_moduleimpl.moduleimpl_list(formsemestre_id=orig_formsemestre_id) for mod_orig in mods_orig: args = mod_orig.copy() args["formsemestre_id"] = formsemestre_id @@ -1191,7 +1191,7 @@ def _reassociate_moduleimpls(cnx, formsemestre_id, ues_old2new, modules_old2new) et met à jour les décisions de jury (validations d'UE). """ # re-associate moduleimpls to new modules: - modimpls = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) + modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) for mod in modimpls: mod["module_id"] = modules_old2new[mod["module_id"]] sco_moduleimpl.do_moduleimpl_edit(mod, formsemestre_id=formsemestre_id) @@ -1308,7 +1308,7 @@ def do_formsemestre_delete(formsemestre_id): sco_cache.EvaluationCache.invalidate_sem(formsemestre_id) # --- Destruction des modules de ce semestre - mods = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) + mods = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) for mod in mods: # evaluations evals = sco_evaluations.do_evaluation_list( diff --git a/app/scodoc/sco_formsemestre_inscriptions.py b/app/scodoc/sco_formsemestre_inscriptions.py index c6bd3fab0..bbfb81b95 100644 --- a/app/scodoc/sco_formsemestre_inscriptions.py +++ b/app/scodoc/sco_formsemestre_inscriptions.py @@ -237,7 +237,7 @@ def do_formsemestre_inscription_with_modules( gdone[group_id] = 1 # inscription a tous les modules de ce semestre - modimpls = sco_moduleimpl.do_moduleimpl_withmodule_list( + modimpls = sco_moduleimpl.moduleimpl_withmodule_list( formsemestre_id=formsemestre_id ) for mod in modimpls: @@ -448,7 +448,7 @@ def formsemestre_inscription_option(etudid, formsemestre_id): ] # Cherche les moduleimpls et les inscriptions - mods = sco_moduleimpl.do_moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) + mods = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) inscr = sco_moduleimpl.do_moduleimpl_inscription_list(etudid=etudid) # Formulaire modimpls_by_ue_ids = scu.DictDefault(defaultvalue=[]) # ue_id : [ moduleimpl_id ] @@ -680,7 +680,7 @@ def do_moduleimpl_incription_options( # inscriptions for moduleimpl_id in a_inscrire: # verifie que ce module existe bien - mods = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id) + mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id) if len(mods) != 1: raise ScoValueError( "inscription: invalid moduleimpl_id: %s" % moduleimpl_id @@ -693,7 +693,7 @@ def do_moduleimpl_incription_options( # desinscriptions for moduleimpl_id in a_desinscrire: # verifie que ce module existe bien - mods = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id) + mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id) if len(mods) != 1: raise ScoValueError( "desinscription: invalid moduleimpl_id: %s" % moduleimpl_id diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 5aa948e43..d2b8609c4 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -453,7 +453,7 @@ def retreive_formsemestre_from_request() -> int: if "formsemestre_id" in args: formsemestre_id = args["formsemestre_id"] elif "moduleimpl_id" in args and args["moduleimpl_id"]: - modimpl = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=args["moduleimpl_id"]) + modimpl = sco_moduleimpl.moduleimpl_list(moduleimpl_id=args["moduleimpl_id"]) if not modimpl: return None # suppressed ? modimpl = modimpl[0] @@ -463,7 +463,7 @@ def retreive_formsemestre_from_request() -> int: if not E: return None # evaluation suppressed ? E = E[0] - modimpl = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + modimpl = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] formsemestre_id = modimpl["formsemestre_id"] elif "group_id" in args: group = sco_groups.get_group(args["group_id"]) @@ -593,9 +593,7 @@ def formsemestre_description_table(formsemestre_id, with_evals=False): use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id) F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"]) - Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( - formsemestre_id=formsemestre_id - ) + Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) R = [] sum_coef = 0 @@ -885,7 +883,7 @@ def html_expr_diagnostic(diagnostics): last_id, last_msg = None, None for diag in diagnostics: if "moduleimpl_id" in diag: - mod = sco_moduleimpl.do_moduleimpl_withmodule_list( + mod = sco_moduleimpl.moduleimpl_withmodule_list( moduleimpl_id=diag["moduleimpl_id"] )[0] H.append( @@ -982,9 +980,7 @@ def formsemestre_status(formsemestre_id=None): # porté du DTML cnx = ndb.GetDBConnexion() sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True) - Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( - formsemestre_id=formsemestre_id - ) + Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) # inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( # args={"formsemestre_id": formsemestre_id} # ) diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index dd1de1f33..668bbbebd 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -47,7 +47,7 @@ from flask import url_for, make_response import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb -from app import log +from app import log, cache from app.scodoc.scolog import logdb from app.scodoc import html_sco_header from app.scodoc import sco_codes_parcours @@ -438,6 +438,7 @@ def etud_add_group_infos(etud, sem, sep=" "): return etud +@cache.memoize(timeout=50) # seconds def get_etud_groups_in_partition(partition_id): """Returns { etudid : group }, with all students in this partition""" infos = ndb.SimpleDictFetch( @@ -1391,6 +1392,7 @@ def do_evaluation_listeetuds_groups( Si include_dems, compte aussi les etudiants démissionnaires (sinon, par défaut, seulement les 'I') """ + # nb: pour notes_table / do_evaluation_etat, getallstudents est vrai et include_dems faux fromtables = [ "notes_moduleimpl_inscription Im", "notes_formsemestre_inscription Isem", @@ -1402,7 +1404,7 @@ def do_evaluation_listeetuds_groups( if not groups: return [] # no groups, so no students rg = ["gm.group_id = '%(group_id)s'" % g for g in groups] - rq = """and Isem.etudid = gm.etudid + rq = """and Isem.etudid = gm.etudid and gd.partition_id = p.id and p.formsemestre_id = Isem.formsemestre_id """ @@ -1415,7 +1417,7 @@ def do_evaluation_listeetuds_groups( req = ( "SELECT distinct Im.etudid FROM " + ", ".join(fromtables) - + """ WHERE Isem.etudid = Im.etudid + + """ WHERE Isem.etudid = Im.etudid and Im.moduleimpl_id = M.id and Isem.formsemestre_id = M.formsemestre_id and E.moduleimpl_id = M.id @@ -1426,10 +1428,9 @@ def do_evaluation_listeetuds_groups( req += " and Isem.etat='I'" req += r cnx = ndb.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) + cursor = cnx.cursor() cursor.execute(req, {"evaluation_id": evaluation_id}) - res = cursor.fetchall() - return [x[0] for x in res] + return [x[0] for x in cursor] def do_evaluation_listegroupes(evaluation_id, include_default=False): @@ -1443,7 +1444,7 @@ def do_evaluation_listegroupes(evaluation_id, include_default=False): else: c = " AND p.partition_name is not NULL" cnx = ndb.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) + cursor = cnx.cursor() cursor.execute( """SELECT DISTINCT gd.id AS group_id FROM group_descr gd, group_membership gm, partition p, @@ -1457,8 +1458,7 @@ def do_evaluation_listegroupes(evaluation_id, include_default=False): + c, {"evaluation_id": evaluation_id}, ) - res = cursor.fetchall() - group_ids = [x[0] for x in res] + group_ids = [x[0] for x in cursor] return listgroups(group_ids) diff --git a/app/scodoc/sco_groups_view.py b/app/scodoc/sco_groups_view.py index 914447299..4fd4e7d62 100644 --- a/app/scodoc/sco_groups_view.py +++ b/app/scodoc/sco_groups_view.py @@ -304,7 +304,7 @@ class DisplayedGroupsInfos(object): else: group_ids = [int(g) for g in group_ids] if not formsemestre_id and moduleimpl_id: - mods = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id) + mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id) if len(mods) != 1: raise ValueError("invalid moduleimpl_id") formsemestre_id = mods[0]["formsemestre_id"] diff --git a/app/scodoc/sco_liste_notes.py b/app/scodoc/sco_liste_notes.py index a9ea6ce84..04f94fff8 100644 --- a/app/scodoc/sco_liste_notes.py +++ b/app/scodoc/sco_liste_notes.py @@ -228,7 +228,7 @@ def _make_table_notes( return "

      Aucune évaluation !

      " E = evals[0] moduleimpl_id = E["moduleimpl_id"] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) # (debug) check that all evals are in same module: @@ -872,9 +872,7 @@ def formsemestre_check_absences_html(formsemestre_id):

      """, ] # Modules, dans l'ordre - Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( - formsemestre_id=formsemestre_id - ) + Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) for M in Mlist: evals = sco_evaluations.do_evaluation_list( {"moduleimpl_id": M["moduleimpl_id"]} diff --git a/app/scodoc/sco_moduleimpl.py b/app/scodoc/sco_moduleimpl.py index b1d0ea474..87a11bf7c 100644 --- a/app/scodoc/sco_moduleimpl.py +++ b/app/scodoc/sco_moduleimpl.py @@ -100,7 +100,7 @@ def do_moduleimpl_delete(oid, formsemestre_id=None): ) # > moduleimpl_delete -def do_moduleimpl_list(moduleimpl_id=None, formsemestre_id=None, module_id=None): +def moduleimpl_list(moduleimpl_id=None, formsemestre_id=None, module_id=None): "list moduleimpls" args = locals() cnx = ndb.GetDBConnexion() @@ -122,10 +122,11 @@ def do_moduleimpl_edit(args, formsemestre_id=None, cnx=None): ) # > modif moduleimpl -def do_moduleimpl_withmodule_list( +def moduleimpl_withmodule_list( moduleimpl_id=None, formsemestre_id=None, module_id=None ): - """Liste les moduleimpls et ajoute dans chacun le module correspondant + """Liste les moduleimpls et ajoute dans chacun + l'UE, la matière et le module auxquels ils appartiennent. Tri la liste par semestre/UE/numero_matiere/numero_module. Attention: Cette fonction fait partie de l'API ScoDoc 7 et est publiée. @@ -134,22 +135,33 @@ def do_moduleimpl_withmodule_list( from app.scodoc import sco_edit_matiere from app.scodoc import sco_edit_module - args = locals() - modimpls = do_moduleimpl_list( + modimpls = moduleimpl_list( **{ "moduleimpl_id": moduleimpl_id, "formsemestre_id": formsemestre_id, "module_id": module_id, } ) - for mo in modimpls: - mo["module"] = sco_edit_module.do_module_list( - args={"module_id": mo["module_id"]} - )[0] - mo["ue"] = sco_edit_ue.do_ue_list(args={"ue_id": mo["module"]["ue_id"]})[0] - mo["matiere"] = sco_edit_matiere.do_matiere_list( - args={"matiere_id": mo["module"]["matiere_id"]} - )[0] + ues = {} + matieres = {} + modules = {} + for mi in modimpls: + module_id = mi["module_id"] + if not mi["module_id"] in modules: + modules[module_id] = sco_edit_module.do_module_list( + args={"module_id": module_id} + )[0] + mi["module"] = modules[module_id] + ue_id = mi["module"]["ue_id"] + if not ue_id in ues: + ues[ue_id] = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] + mi["ue"] = ues[ue_id] + matiere_id = mi["module"]["matiere_id"] + if not matiere_id in matieres: + matieres[matiere_id] = sco_edit_matiere.do_matiere_list( + args={"matiere_id": matiere_id} + )[0] + mi["matiere"] = matieres[matiere_id] # tri par semestre/UE/numero_matiere/numero_module modimpls.sort( @@ -173,7 +185,7 @@ def do_moduleimpl_inscription_list(moduleimpl_id=None, etudid=None): return _moduleimpl_inscriptionEditor.list(cnx, args) -def do_moduleimpl_listeetuds(moduleimpl_id): +def moduleimpl_listeetuds(moduleimpl_id): "retourne liste des etudids inscrits a ce module" req = """SELECT DISTINCT Im.etudid FROM notes_moduleimpl_inscription Im, @@ -306,7 +318,7 @@ def can_change_module_resp(moduleimpl_id): """Check if current user can modify module resp. (raise exception if not). = Admin, et dir des etud. (si option l'y autorise) """ - M = do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] + M = moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] # -- check lock sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) if not sem["etat"]: @@ -322,7 +334,7 @@ def can_change_module_resp(moduleimpl_id): def can_change_ens(moduleimpl_id, raise_exc=True): "check if current user can modify ens list (raise exception if not)" - M = do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] + M = moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] # -- check lock sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) if not sem["etat"]: diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index 93d420d0e..1517e6268 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -63,7 +63,7 @@ def moduleimpl_inscriptions_edit(moduleimpl_id, etuds=[], submitted=False): * Si pas les droits: idem en readonly """ - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] formsemestre_id = M["formsemestre_id"] mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] sem = sco_formsemestre.get_formsemestre(formsemestre_id) @@ -263,9 +263,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id): can_change = authuser.has_permission(Permission.ScoEtudInscrit) and sem["etat"] # Liste des modules - Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( - formsemestre_id=formsemestre_id - ) + Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) # Decrit les inscriptions aux modules: commons = [] # modules communs a tous les etuds du semestre options = [] # modules ou seuls quelques etudiants sont inscrits diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index c358b25c0..a54a0e431 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -58,7 +58,7 @@ from app.scodoc import sco_users def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0): "Menu avec actions sur une evaluation" E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] - modimpl = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + modimpl = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] group_id = sco_groups.get_default_group(modimpl["formsemestre_id"]) @@ -156,7 +156,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0): def moduleimpl_status(moduleimpl_id=None, partition_id=None): """Tableau de bord module (liste des evaluations etc)""" - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] formsemestre_id = M["formsemestre_id"] Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] sem = sco_formsemestre.get_formsemestre(formsemestre_id) diff --git a/app/scodoc/sco_news.py b/app/scodoc/sco_news.py index f2c19cf4c..e063a81dc 100644 --- a/app/scodoc/sco_news.py +++ b/app/scodoc/sco_news.py @@ -174,7 +174,7 @@ def _get_formsemestre_infos_from_news(n): elif n["type"] == NEWS_NOTE: moduleimpl_id = n["object"] if n["object"]: - mods = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id) + mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id) if not mods: return {} # module does not exists anymore return {} # pas d'indication du module diff --git a/app/scodoc/sco_permissions_check.py b/app/scodoc/sco_permissions_check.py index 0bf77b390..5c166dbe3 100644 --- a/app/scodoc/sco_permissions_check.py +++ b/app/scodoc/sco_permissions_check.py @@ -26,7 +26,7 @@ def can_edit_notes(authuser, moduleimpl_id, allow_ens=True): from app.scodoc import sco_formsemestre from app.scodoc import sco_parcours_dut - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) if not sem["etat"]: return False # semestre verrouillé @@ -64,7 +64,7 @@ def can_edit_evaluation(moduleimpl_id=None): # acces pour resp. moduleimpl et resp. form semestre (dir etud) if moduleimpl_id is None: raise ValueError("no moduleimpl specified") # bug - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) if ( @@ -200,4 +200,4 @@ def can_handle_passwd(user, allow_admindepts=False): if (current_user.dept == user.dept) or allow_admindepts: return True else: - return False \ No newline at end of file + return False diff --git a/app/scodoc/sco_placement.py b/app/scodoc/sco_placement.py index c2ef9d937..e09df0e88 100644 --- a/app/scodoc/sco_placement.py +++ b/app/scodoc/sco_placement.py @@ -244,7 +244,7 @@ class PlacementRunner: # gr_title = sco_groups.listgroups_abbrev(d['groups']) self.current_user = current_user self.moduleimpl_id = self.eval_data["moduleimpl_id"] - self.moduleimpl_data = sco_moduleimpl.do_moduleimpl_list( + self.moduleimpl_data = sco_moduleimpl.moduleimpl_list( moduleimpl_id=self.moduleimpl_id )[0] self.module_data = sco_edit_module.do_module_list( diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index 90dec7307..6c6638cd1 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -177,9 +177,7 @@ def do_evaluation_upload_xls(): evaluation_id = int(vals["evaluation_id"]) comment = vals["comment"] E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] - M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[ - 0 - ] + M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0] # Check access # (admin, respformation, and responsable_id) if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]): @@ -253,7 +251,7 @@ def do_evaluation_upload_xls(): ) # news E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod["moduleimpl_id"] = M["moduleimpl_id"] mod["url"] = url_for( @@ -292,9 +290,7 @@ def do_evaluation_upload_xls(): def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False): """Initialisation des notes manquantes""" E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] - M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[ - 0 - ] + M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0] # Check access # (admin, respformation, and responsable_id) if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]): @@ -340,7 +336,7 @@ def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False): comment = "Initialisation notes manquantes" nb_changed, _, _ = _notes_add(current_user, evaluation_id, L, comment) # news - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod["moduleimpl_id"] = M["moduleimpl_id"] mod["url"] = url_for( @@ -429,7 +425,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False): % E["moduleimpl_id"] ] # news - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] mod["moduleimpl_id"] = M["moduleimpl_id"] mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod @@ -476,7 +472,7 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True): nb_changed = 0 nb_suppress = 0 E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] existing_decisions = ( [] ) # etudids pour lesquels il y a une decision de jury et que la note change @@ -598,7 +594,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=()): if not evals: raise ScoValueError("invalid evaluation_id") E = evals[0] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] formsemestre_id = M["formsemestre_id"] if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]): return ( @@ -769,7 +765,7 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]): if not evals: raise ScoValueError("invalid evaluation_id") E = evals[0] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] formsemestre_id = M["formsemestre_id"] Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) @@ -868,9 +864,7 @@ def saisie_notes(evaluation_id, group_ids=[]): if not evals: raise ScoValueError("invalid evaluation_id") E = evals[0] - M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[ - 0 - ] + M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[0] formsemestre_id = M["formsemestre_id"] # Check access # (admin, respformation, and responsable_id) @@ -1230,7 +1224,7 @@ def save_note(etudid=None, evaluation_id=None, value=None, comment=""): % (evaluation_id, etudid, authuser, value) ) E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] Mod["url"] = url_for( "notes.moduleimpl_status", diff --git a/app/scodoc/sco_undo_notes.py b/app/scodoc/sco_undo_notes.py index add875724..1f410c704 100644 --- a/app/scodoc/sco_undo_notes.py +++ b/app/scodoc/sco_undo_notes.py @@ -149,7 +149,7 @@ def list_operations(evaluation_id): def evaluation_list_operations(evaluation_id): """Page listing operations on evaluation""" E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] Ops = list_operations(evaluation_id) diff --git a/app/views/notes.py b/app/views/notes.py index ad2d82f05..ece1029ee 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -1059,11 +1059,11 @@ def edit_moduleimpl_expr(moduleimpl_id): @scodoc7func def view_module_abs(moduleimpl_id, format="html"): """Visualisation des absences a un module""" - M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] + M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) debut_sem = ndb.DateDMYtoISO(sem["date_debut"]) fin_sem = ndb.DateDMYtoISO(sem["date_fin"]) - list_insc = sco_moduleimpl.do_moduleimpl_listeetuds(moduleimpl_id) + list_insc = sco_moduleimpl.moduleimpl_listeetuds(moduleimpl_id) T = [] for etudid in list_insc: @@ -1213,7 +1213,7 @@ def formsemestre_enseignants_list(formsemestre_id, format="html"): """ sem = sco_formsemestre.get_formsemestre(formsemestre_id) # resp. de modules: - mods = sco_moduleimpl.do_moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) + mods = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) sem_ens = {} for mod in mods: if not mod["responsable_id"] in sem_ens: @@ -1527,7 +1527,7 @@ def evaluation_delete(evaluation_id): if not El: raise ValueError("Evalution inexistante ! (%s)" % evaluation_id) E = El[0] - M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] + M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] tit = "Suppression de l'évaluation %(description)s (%(jour)s)" % E etat = sco_evaluations.do_evaluation_etat(evaluation_id) @@ -2424,7 +2424,7 @@ def check_sem_integrity(formsemestre_id, fix=False): """ sem = sco_formsemestre.get_formsemestre(formsemestre_id) - modimpls = sco_moduleimpl.do_moduleimpl_list(formsemestre_id=formsemestre_id) + modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) bad_ue = [] bad_sem = [] formations_set = set() # les formations mentionnées dans les UE et modules @@ -2534,9 +2534,7 @@ def check_formsemestre_integrity(formsemestre_id): # de formations diag = [] - Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( - formsemestre_id=formsemestre_id - ) + Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) for mod in Mlist: if mod["module"]["ue_id"] != mod["matiere"]["ue_id"]: diag.append( @@ -2593,14 +2591,14 @@ def check_integrity_all(): # -------------------------------------------------------------------- # Support for legacy ScoDoc 7 API # -------------------------------------------------------------------- -@bp.route("/do_moduleimpl_list") +@bp.route("/moduleimpl_list") @scodoc @permission_required(Permission.ScoView) @scodoc7func -def do_moduleimpl_list( +def moduleimpl_list( moduleimpl_id=None, formsemestre_id=None, module_id=None, format="json" ): - data = sco_moduleimpl.do_moduleimpl_list( + data = sco_moduleimpl.moduleimpl_list( moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id, module_id=module_id, @@ -2608,15 +2606,16 @@ def do_moduleimpl_list( return scu.sendResult(data, format=format) -@bp.route("/do_moduleimpl_withmodule_list") +@bp.route("/do_moduleimpl_withmodule_list") # ancien nom +@bp.route("/moduleimpl_withmodule_list") @scodoc @permission_required(Permission.ScoView) @scodoc7func -def do_moduleimpl_withmodule_list( +def moduleimpl_withmodule_list( moduleimpl_id=None, formsemestre_id=None, module_id=None ): """API ScoDoc 7""" - data = sco_moduleimpl.do_moduleimpl_withmodule_list( + data = sco_moduleimpl.moduleimpl_withmodule_list( moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id, module_id=module_id, diff --git a/bench.py b/bench.py new file mode 100644 index 000000000..79fe77d85 --- /dev/null +++ b/bench.py @@ -0,0 +1,13 @@ +"""Script pour faciliter le lancement de benchmarks +""" + +from tests.bench.notes_table import bench_notes_table + +BENCH_DEPT = "RT" +BENCH_FORMSEMESTRE_IDS = ( + 149, # RT S1 2020-21 + 145, # RT S2 2021 + 119, # RT S1 2029 +) + +bench_notes_table(BENCH_DEPT, BENCH_FORMSEMESTRE_IDS) diff --git a/misc/example-api-1.py b/misc/example-api-1.py index e79b329a3..37a06c56f 100644 --- a/misc/example-api-1.py +++ b/misc/example-api-1.py @@ -78,7 +78,7 @@ pp(sem) # semdescr = GET(s, f"Notes/formsemestre_description?formsemestre_id={sem['formsemestre_id']}&with_evals=0&format=json" ) # ---- Liste les modules et prend le premier -mods = GET(s, f"/Notes/do_moduleimpl_list?formsemestre_id={sem['formsemestre_id']}") +mods = GET(s, f"/Notes/moduleimpl_list?formsemestre_id={sem['formsemestre_id']}") print(f"{len(mods)} modules dans le semestre {sem['titre']}") mod = mods[0] diff --git a/tests/bench/notes_table.py b/tests/bench/notes_table.py new file mode 100644 index 000000000..0803be6eb --- /dev/null +++ b/tests/bench/notes_table.py @@ -0,0 +1,50 @@ +# Simple benchmark +# mesure temps execution NotesTable + +import time + +from flask import g +from flask_login import login_user + +from config import RunningConfig as BenchConfig +import app +from app import db, create_app +from app import clear_scodoc_cache +from app.auth.models import get_super_admin +from app.scodoc import notesdb as ndb +from app.scodoc import notes_table + + +def setup_generator(dept: str): + # Setup + apptest = create_app(BenchConfig) + # Run tests: + with apptest.test_client() as client: + with apptest.app_context(): + with apptest.test_request_context(): + # Clear application cache: + print("clearing cache...") + clear_scodoc_cache() + # initialize scodoc "g": + g.stored_get_formsemestre = {} + # Loge l'utilisateur super-admin + admin_user = get_super_admin() + login_user(admin_user) + app.set_sco_dept(dept) # set db connection + yield client + ndb.close_db_connection() + # Teardown: + db.session.commit() + db.session.remove() + + +def bench_notes_table(dept: str, formsemestre_ids: list[int]) -> float: + for client in setup_generator(dept): + tot_time = 0.0 + for formsemestre_id in formsemestre_ids: + print(f"building sem {formsemestre_id}...") + t0 = time.time() + nt = notes_table.NotesTable(formsemestre_id) + tot_time += time.time() - t0 + print(f"Total time: {tot_time}") + return tot_time diff --git a/tests/unit/sco_fake_gen.py b/tests/unit/sco_fake_gen.py index 0ad1453b8..1daf142b9 100644 --- a/tests/unit/sco_fake_gen.py +++ b/tests/unit/sco_fake_gen.py @@ -251,7 +251,7 @@ class ScoFake(object): if not responsable_id: responsable_id = self.default_user.id oid = sco_moduleimpl.do_moduleimpl_create(locals()) - oids = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=oid) # API inconsistency + oids = sco_moduleimpl.moduleimpl_list(moduleimpl_id=oid) # API inconsistency if not oids: raise ScoValueError("moduleimpl not created !") return oids[0] diff --git a/tests/unit/test_formations.py b/tests/unit/test_formations.py index 8859b3ca8..36cb67367 100644 --- a/tests/unit/test_formations.py +++ b/tests/unit/test_formations.py @@ -30,7 +30,7 @@ # - formation_list # - formation_export # - formsemestre_list -# - do_moduleimpl_list +# - moduleimpl_list # - do_module_impl_with_module_list # - do_formsemestre_delete # - do_module_list @@ -231,28 +231,24 @@ def test_formations(test_client): # --- Liste des modules - lim_sem1 = sco_moduleimpl.do_moduleimpl_list( - formsemestre_id=sem1["formsemestre_id"] - ) + lim_sem1 = sco_moduleimpl.moduleimpl_list(formsemestre_id=sem1["formsemestre_id"]) assert len(lim_sem1) == 2 assert mod["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"]) assert mod2["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"]) - lim_modid = sco_moduleimpl.do_moduleimpl_list(module_id=mod["module_id"]) + lim_modid = sco_moduleimpl.moduleimpl_list(module_id=mod["module_id"]) assert len(lim_modid) == 1 - lim_modimpl_id = sco_moduleimpl.do_moduleimpl_list( - moduleimpl_id=mi["moduleimpl_id"] - ) + lim_modimpl_id = sco_moduleimpl.moduleimpl_list(moduleimpl_id=mi["moduleimpl_id"]) # print(lim_modimpl_id) - # ---- Test de do_moduleimpl_withmodule_list + # ---- Test de moduleimpl_withmodule_list assert lim_modid == lim_modimpl_id # doit etre le meme resultat - liimp_sem1 = sco_moduleimpl.do_moduleimpl_withmodule_list( + liimp_sem1 = sco_moduleimpl.moduleimpl_withmodule_list( formsemestre_id=sem1["formsemestre_id"] ) @@ -262,16 +258,14 @@ def test_formations(test_client): liimp_sem1[0]["module_id"], liimp_sem1[1]["module_id"], ) - liimp_sem2 = sco_moduleimpl.do_moduleimpl_withmodule_list( + liimp_sem2 = sco_moduleimpl.moduleimpl_withmodule_list( formsemestre_id=sem2["formsemestre_id"] ) assert modt["module_id"] == liimp_sem2[0]["module_id"] - liimp_modid = sco_moduleimpl.do_moduleimpl_withmodule_list( - module_id=mod["module_id"] - ) + liimp_modid = sco_moduleimpl.moduleimpl_withmodule_list(module_id=mod["module_id"]) assert len(liimp_modid) == 1 - liimp_modimplid = sco_moduleimpl.do_moduleimpl_withmodule_list( + liimp_modimplid = sco_moduleimpl.moduleimpl_withmodule_list( moduleimpl_id=mi["moduleimpl_id"] ) @@ -305,9 +299,7 @@ def test_formations(test_client): assert len(li_module2) == 3 # verification de la suppression du module - lim_sem2 = sco_moduleimpl.do_moduleimpl_list( - formsemestre_id=sem2["formsemestre_id"] - ) + lim_sem2 = sco_moduleimpl.moduleimpl_list(formsemestre_id=sem2["formsemestre_id"]) assert len(lim_sem2) == 0 # deuxieme vérification si le module s'est bien sup From 93a23ff112f47a0678ca8cb728eb5201a26503be Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 15 Oct 2021 14:31:11 +0200 Subject: [PATCH 12/27] fix: partition_name when numeric --- app/scodoc/sco_groups.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index 668bbbebd..7a31b995b 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -791,7 +791,7 @@ def partition_create( if not sco_permissions_check.can_change_groups(formsemestre_id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") if partition_name: - partition_name = partition_name.strip() + partition_name = str(partition_name).strip() if default: partition_name = None if not partition_name and not default: @@ -1106,7 +1106,7 @@ def partition_rename(partition_id): def partition_set_name(partition_id, partition_name, redirect=1): """Set partition name""" - partition_name = partition_name.strip() + partition_name = str(partition_name).strip() if not partition_name: raise ValueError("partition name must be non empty") partition = get_partition(partition_id) @@ -1347,6 +1347,7 @@ def create_etapes_partition(formsemestre_id, partition_name="apo_etapes"): """ from app.scodoc import sco_formsemestre_inscriptions + partition_name = str(partition_name) log("create_etapes_partition(%s)" % formsemestre_id) ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( args={"formsemestre_id": formsemestre_id} From 45d20789dd921028430ab91c4c5d464a6ac2c197 Mon Sep 17 00:00:00 2001 From: Place Jean-Marie Date: Fri, 15 Oct 2021 15:12:46 +0200 Subject: [PATCH 13/27] wip: avant tests --- app/static/js/user_form.js | 30 +++++++++++ app/templates/email/welcome.html | 16 ++++++ app/templates/email/welcome.txt | 11 ++++ app/views/users.py | 87 +++++++++++++++++++++++++++----- 4 files changed, 130 insertions(+), 14 deletions(-) create mode 100644 app/static/js/user_form.js create mode 100644 app/templates/email/welcome.html create mode 100644 app/templates/email/welcome.txt diff --git a/app/static/js/user_form.js b/app/static/js/user_form.js new file mode 100644 index 000000000..5cbdf490e --- /dev/null +++ b/app/static/js/user_form.js @@ -0,0 +1,30 @@ + +function refresh() { + if ($("input[name='welcome:list']").is(":checked")) { + $("input[name='reset_password:list']").closest("tr").css("display", "table-row") + if ($("input[name='reset_password:list']").is(":checked")) { + $("#tf_password").closest('tr').css("display", "none"); + $("#tf_password2").closest('tr').css("display", "none"); + } else { + // Le mot de passe doit être saisi + $("#tf_password").closest('tr').css("display", "table-row"); + $("#tf_password2").closest('tr').css("display", "table-row"); + } + } else { + // Le mot de passe doit être saisi + $("input[name='reset_password:list']").closest("tr").css("display", "none") + $("#tf_password").closest('tr').css("display", "table-row"); + $("#tf_password2").closest('tr').css("display", "table-row"); + } +} + +$(function() { + $("input[name='welcome:list']").click(function() { + refresh(); + }) + $("input[name='reset_password:list']").click(function() { + refresh(); + }) + refresh(); +}) + diff --git a/app/templates/email/welcome.html b/app/templates/email/welcome.html new file mode 100644 index 000000000..7f2d6ad6e --- /dev/null +++ b/app/templates/email/welcome.html @@ -0,0 +1,16 @@ +

      Bienvenue {{ user.prenom }} {{ user.nom }},

      +

      + Votre accès à ScoDoc vient d'être validé. + votre identifiant de connexion est: {{ user.user_name }} + + {% if token %} + Pour initialiser votre mot de passe ScoDoc, + + cliquez sur ce lien + . +

      +

      Vous pouvez aussi copier ce lien dans votre navigateur Web:

      +

      {{ url_for('auth.reset_password', token=token, _external=True) }}

      + {% endif %} + +

      A bientôt !

      \ No newline at end of file diff --git a/app/templates/email/welcome.txt b/app/templates/email/welcome.txt new file mode 100644 index 000000000..b15bceae4 --- /dev/null +++ b/app/templates/email/welcome.txt @@ -0,0 +1,11 @@ +Bienvenue {{ user.prenom }} {{ user.nom }}, + +Votre accès à ScoDoc vient d'être validé. +Votre identifiant de connexion est: {{ user.user_name }} + +{% if token %} + Pour initialiser votre mot de passe ScoDoc, suivre le lien: + {{ url_for('auth.reset_password', token=token, _external=True) }} +{% endif %} + +

      A bientôt !

      \ No newline at end of file diff --git a/app/views/users.py b/app/views/users.py index cddcf1952..c4a7daa43 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -38,7 +38,7 @@ import re from xml.etree import ElementTree import flask -from flask import g, url_for, request +from flask import g, url_for, request, current_app from flask import redirect, render_template from flask_login import current_user @@ -49,6 +49,7 @@ from app.auth.models import Permission from app.auth.models import User from app.auth.models import Role from app.auth.models import UserRole +from app.email import send_email from app.models import Departement from app.decorators import ( @@ -63,6 +64,7 @@ from app.scodoc import sco_utils as scu from app.scodoc import sco_xml from app import log from app.scodoc.sco_exceptions import AccessDenied, ScoValueError +from app.scodoc.sco_import_users import generate_password from app.scodoc.sco_permissions_check import can_handle_passwd from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message from app.views import users_bp as bp @@ -100,7 +102,10 @@ def create_user_form(user_name=None, edit=0, all_roles=1): initvalues = {} edit = int(edit) all_roles = int(all_roles) - H = [html_sco_header.sco_header(bodyOnLoad="init_tf_form('')")] + H = [html_sco_header.sco_header( + bodyOnLoad="init_tf_form('')", + javascripts=["js/user_form.js"], + )] F = html_sco_header.sco_footer() if edit: if not user_name: @@ -214,7 +219,32 @@ def create_user_form(user_name=None, edit=0, all_roles=1): "title": "Pseudo (login)", "size": 20, "allow_null": False, - "explanation": "nom utilisé pour la connexion. Doit être unique parmi tous les utilisateurs.", + "explanation": "nom utilisé pour la connexion. Doit être unique parmi tous les utilisateurs. " + "Lettres ou chiffres uniquement.", + }, + ), + ("formsemestre_id", {"input_type": "hidden"}), + ( + "welcome", + { + "title": "Message d'accueil", + "input_type": "checkbox", + "explanation": "Envoie un mail d'accueil à l'utilsateur.", + "labels": ("",), + "allowed_values": ("1",), + "default": "1", + }, + ), + ( + "reset_password", + { + "title": "réinit. mot de passe.", + "input_type": "checkbox", + "explanation": "ajoute la procédure de changement de mot de passe au mail d'accueil", + "labels": ("",), + "allowed_values": ("1",), + "default": "1", + "attributes": ["style='margin-left:20pt'"], }, ), ( @@ -444,18 +474,33 @@ def create_user_form(user_name=None, edit=0, all_roles=1): "identifiant invalide (pas d'accents ni de caractères spéciaux)" ) return "\n".join(H) + msg + "\n" + tf[1] + F + # Traitement initial (mode) : 3 cas + # A: envoi de welcome + procedure de reset + # B: envoi de welcome seulement (mot de passe saisie dans le formulaire) + # C: Aucun envoi (mot de pase saisi dans le formulaire) + if vals['welcome:list'] == '1': + if vals['reset_password:list'] == '1': + mode = 'A' + else: + mode = 'B' + else: + mode = 'C' + # check passwords - if vals["password"]: - if vals["password"] != vals["password2"]: - msg = tf_error_message( - """Les deux mots de passes ne correspondent pas !""" - ) - return "\n".join(H) + msg + "\n" + tf[1] + F - if not sco_users.is_valid_password(vals["password"]): - msg = tf_error_message( - """Mot de passe trop simple, recommencez !""" - ) - return "\n".join(H) + msg + "\n" + tf[1] + F + if mode == 'A': + vals['password'] = generate_password() + else: + if vals["password"]: + if vals["password"] != vals["password2"]: + msg = tf_error_message( + """Les deux mots de passes ne correspondent pas !""" + ) + return "\n".join(H) + msg + "\n" + tf[1] + F + if not sco_users.is_valid_password(vals["password"]): + msg = tf_error_message( + """Mot de passe trop simple, recommencez !""" + ) + return "\n".join(H) + msg + "\n" + tf[1] + F if not can_choose_dept: vals["dept"] = auth_dept # ok, go @@ -467,6 +512,20 @@ def create_user_form(user_name=None, edit=0, all_roles=1): u.from_dict(vals, new_user=True) db.session.add(u) db.session.commit() + # envoi éventuel d'un message + if mode == 'A' or mode == 'B': + if mode == 'A': + token = u.get_reset_password_token() + else: + token = None + send_email( + "[ScoDoc] Réinitialisation de votre mot de passe", + sender=current_app.config["ADMINS"][0], + recipients=[u.email], + text_body=render_template("email/welcome.txt", user=u, token=token), + html_body=render_template("email/welcome.html", user=u, token=token), + ) + return flask.redirect( url_for( "users.user_info_page", From c68633bf5bd47b927bef21e2ee0392e299d04860 Mon Sep 17 00:00:00 2001 From: Place Jean-Marie Date: Fri, 15 Oct 2021 15:34:10 +0200 Subject: [PATCH 14/27] typo --- app/views/users.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/app/views/users.py b/app/views/users.py index c4a7daa43..430633d5a 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -102,10 +102,12 @@ def create_user_form(user_name=None, edit=0, all_roles=1): initvalues = {} edit = int(edit) all_roles = int(all_roles) - H = [html_sco_header.sco_header( - bodyOnLoad="init_tf_form('')", - javascripts=["js/user_form.js"], - )] + H = [ + html_sco_header.sco_header( + bodyOnLoad="init_tf_form('')", + javascripts=["js/user_form.js"], + ) + ] F = html_sco_header.sco_footer() if edit: if not user_name: @@ -220,7 +222,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1): "size": 20, "allow_null": False, "explanation": "nom utilisé pour la connexion. Doit être unique parmi tous les utilisateurs. " - "Lettres ou chiffres uniquement.", + "Lettres ou chiffres uniquement.", }, ), ("formsemestre_id", {"input_type": "hidden"}), @@ -229,7 +231,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1): { "title": "Message d'accueil", "input_type": "checkbox", - "explanation": "Envoie un mail d'accueil à l'utilsateur.", + "explanation": "Envoie un mail d'accueil à l'utilisateur.", "labels": ("",), "allowed_values": ("1",), "default": "1", @@ -417,7 +419,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1): nom=vals["nom"], prenom=vals["prenom"], email=vals["email"], - dept=vals["dept"], + dept=vals.get("dept", auth_dept), roles=vals["roles"], ) if not ok: @@ -478,17 +480,17 @@ def create_user_form(user_name=None, edit=0, all_roles=1): # A: envoi de welcome + procedure de reset # B: envoi de welcome seulement (mot de passe saisie dans le formulaire) # C: Aucun envoi (mot de pase saisi dans le formulaire) - if vals['welcome:list'] == '1': - if vals['reset_password:list'] == '1': - mode = 'A' + if vals["welcome:list"] == "1": + if vals["reset_password:list"] == "1": + mode = "A" else: - mode = 'B' + mode = "B" else: - mode = 'C' + mode = "C" # check passwords - if mode == 'A': - vals['password'] = generate_password() + if mode == "A": + vals["password"] = generate_password() else: if vals["password"]: if vals["password"] != vals["password2"]: @@ -513,8 +515,8 @@ def create_user_form(user_name=None, edit=0, all_roles=1): db.session.add(u) db.session.commit() # envoi éventuel d'un message - if mode == 'A' or mode == 'B': - if mode == 'A': + if mode == "A" or mode == "B": + if mode == "A": token = u.get_reset_password_token() else: token = None @@ -523,7 +525,9 @@ def create_user_form(user_name=None, edit=0, all_roles=1): sender=current_app.config["ADMINS"][0], recipients=[u.email], text_body=render_template("email/welcome.txt", user=u, token=token), - html_body=render_template("email/welcome.html", user=u, token=token), + html_body=render_template( + "email/welcome.html", user=u, token=token + ), ) return flask.redirect( From 9c50b58d5fa2622b056a95d93c2f05a35077ce4f Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 15 Oct 2021 19:17:40 +0200 Subject: [PATCH 15/27] =?UTF-8?q?am=C3=A9lioration=20formulaires=20creatio?= =?UTF-8?q?n/edition=20utilisateurs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/auth/forms.py | 10 +++-- app/auth/models.py | 18 ++++++++ app/auth/routes.py | 4 +- app/scodoc/sco_users.py | 27 ++++-------- app/static/js/user_form.js | 8 ++-- app/templates/auth/reset_password.html | 9 +++- app/templates/email/welcome.html | 12 +++--- app/views/users.py | 58 ++++++++++++++------------ 8 files changed, 85 insertions(+), 61 deletions(-) diff --git a/app/auth/forms.py b/app/auth/forms.py index 143f65546..dd1f68ef1 100644 --- a/app/auth/forms.py +++ b/app/auth/forms.py @@ -8,7 +8,7 @@ TODO: à revoir complètement pour reprendre ZScoUsers et les pages d'authentifi from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms.validators import ValidationError, DataRequired, Email, EqualTo -from app.auth.models import User +from app.auth.models import User, is_valid_password _ = lambda x: x # sans babel @@ -44,7 +44,7 @@ class UserCreationForm(FlaskForm): class ResetPasswordRequestForm(FlaskForm): email = StringField(_l("Email"), validators=[DataRequired(), Email()]) - submit = SubmitField(_l("Request Password Reset")) + submit = SubmitField(_l("Valider ce mot de passe")) class ResetPasswordForm(FlaskForm): @@ -52,7 +52,11 @@ class ResetPasswordForm(FlaskForm): password2 = PasswordField( _l("Répéter"), validators=[DataRequired(), EqualTo("password")] ) - submit = SubmitField(_l("Request Password Reset")) + submit = SubmitField(_l("Valider ce mot de passe")) + + def validate_password(self, password): + if not is_valid_password(password.data): + raise ValidationError(f"Mot de passe trop simple, recommencez") class DeactivateUserForm(FlaskForm): diff --git a/app/auth/models.py b/app/auth/models.py index ed20d5eeb..f243f0e79 100644 --- a/app/auth/models.py +++ b/app/auth/models.py @@ -10,6 +10,7 @@ import re from time import time from typing import Optional +import cracklib # pylint: disable=import-error from flask import current_app, url_for, g from flask_login import UserMixin, AnonymousUserMixin @@ -28,6 +29,23 @@ from app.scodoc import sco_etud # a deplacer dans scu VALID_LOGIN_EXP = re.compile(r"^[a-zA-Z0-9@\\\-_\.]+$") +def is_valid_password(cleartxt): + """Check password. + returns True if OK. + """ + if ( + hasattr(scu.CONFIG, "MIN_PASSWORD_LENGTH") + and scu.CONFIG.MIN_PASSWORD_LENGTH > 0 + and len(cleartxt) < scu.CONFIG.MIN_PASSWORD_LENGTH + ): + return False # invalid: too short + try: + _ = cracklib.FascistCheck(cleartxt) + return True + except ValueError: + return False + + class User(UserMixin, db.Model): """ScoDoc users, handled by Flask / SQLAlchemy""" diff --git a/app/auth/routes.py b/app/auth/routes.py index bf7272a75..61f2bceac 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -116,6 +116,6 @@ def reset_password(token): if form.validate_on_submit(): user.set_password(form.password.data) db.session.commit() - flash(_("Your password has been reset.")) + flash(_("Votre mot de passe a été changé.")) return redirect(url_for("auth.login")) - return render_template("auth/reset_password.html", form=form) + return render_template("auth/reset_password.html", form=form, user=user) diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index be43125d9..3f1b646d2 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -34,7 +34,6 @@ import re from flask import url_for, g, request from flask_login import current_user -import cracklib # pylint: disable=import-error from app import db, Departement @@ -56,23 +55,6 @@ from app.scodoc.sco_exceptions import ( ) -def is_valid_password(cleartxt): - """Check password. - returns True if OK. - """ - if ( - hasattr(scu.CONFIG, "MIN_PASSWORD_LENGTH") - and scu.CONFIG.MIN_PASSWORD_LENGTH > 0 - and len(cleartxt) < scu.CONFIG.MIN_PASSWORD_LENGTH - ): - return False # invalid: too short - try: - _ = cracklib.FascistCheck(cleartxt) - return True - except ValueError: - return False - - # --------------- # --------------- @@ -439,6 +421,15 @@ def check_modif_user( return False, "département '%s' inexistant" % dept + MSG_OPT if enforce_optionals and not roles: return False, "aucun rôle sélectionné, êtes vous sûr ?" + MSG_OPT + # Unicité du mail + users_with_this_mail = User.query.filter_by(email=email).all() + if edit: # modification + if email != user["email"] and len(users_with_this_mail) > 0: + return False, "un autre utilisateur existe déjà avec cette adresse mail" + else: # création utilisateur + if len(users_with_this_mail) > 0: + return False, "un autre utilisateur existe déjà avec cette adresse mail" + # ok # Des noms/prénoms semblables existent ? nom = nom.lower().strip() diff --git a/app/static/js/user_form.js b/app/static/js/user_form.js index 5cbdf490e..4ad06672a 100644 --- a/app/static/js/user_form.js +++ b/app/static/js/user_form.js @@ -6,7 +6,7 @@ function refresh() { $("#tf_password").closest('tr').css("display", "none"); $("#tf_password2").closest('tr').css("display", "none"); } else { - // Le mot de passe doit être saisi + // Le mot de passe doit être saisi $("#tf_password").closest('tr').css("display", "table-row"); $("#tf_password2").closest('tr').css("display", "table-row"); } @@ -18,11 +18,11 @@ function refresh() { } } -$(function() { - $("input[name='welcome:list']").click(function() { +$(function () { + $("input[name='welcome:list']").click(function () { refresh(); }) - $("input[name='reset_password:list']").click(function() { + $("input[name='reset_password:list']").click(function () { refresh(); }) refresh(); diff --git a/app/templates/auth/reset_password.html b/app/templates/auth/reset_password.html index d054674f6..261e37db0 100644 --- a/app/templates/auth/reset_password.html +++ b/app/templates/auth/reset_password.html @@ -2,8 +2,13 @@ {% import 'bootstrap/wtf.html' as wtf %} {% block app_content %} -

      Reset Your Password

      -
      +

      Changez votre mot de passe ScoDoc

      + +
      +
      Votre identifiant: {{user.user_name}}
      +
      + +
      {{ wtf.quick_form(form) }}
      diff --git a/app/templates/email/welcome.html b/app/templates/email/welcome.html index 7f2d6ad6e..41ee5f65c 100644 --- a/app/templates/email/welcome.html +++ b/app/templates/email/welcome.html @@ -1,16 +1,18 @@

      Bienvenue {{ user.prenom }} {{ user.nom }},

      Votre accès à ScoDoc vient d'être validé. - votre identifiant de connexion est: {{ user.user_name }} - - {% if token %} - Pour initialiser votre mot de passe ScoDoc, +

      +

      + Votre identifiant de connexion est: {{ user.user_name }} +

      +{% if token %} +

      Pour initialiser votre mot de passe ScoDoc, cliquez sur ce lien .

      Vous pouvez aussi copier ce lien dans votre navigateur Web:

      {{ url_for('auth.reset_password', token=token, _external=True) }}

      - {% endif %} +{% endif %}

      A bientôt !

      \ No newline at end of file diff --git a/app/views/users.py b/app/views/users.py index 430633d5a..d293f3bc0 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -49,6 +49,7 @@ from app.auth.models import Permission from app.auth.models import User from app.auth.models import Role from app.auth.models import UserRole +from app.auth.models import is_valid_password from app.email import send_email from app.models import Departement @@ -226,29 +227,6 @@ def create_user_form(user_name=None, edit=0, all_roles=1): }, ), ("formsemestre_id", {"input_type": "hidden"}), - ( - "welcome", - { - "title": "Message d'accueil", - "input_type": "checkbox", - "explanation": "Envoie un mail d'accueil à l'utilisateur.", - "labels": ("",), - "allowed_values": ("1",), - "default": "1", - }, - ), - ( - "reset_password", - { - "title": "réinit. mot de passe.", - "input_type": "checkbox", - "explanation": "ajoute la procédure de changement de mot de passe au mail d'accueil", - "labels": ("",), - "allowed_values": ("1",), - "default": "1", - "attributes": ["style='margin-left:20pt'"], - }, - ), ( "password", { @@ -289,6 +267,32 @@ def create_user_form(user_name=None, edit=0, all_roles=1): }, ) ] + if not edit: # options création utilisateur + descr += [ + ( + "welcome", + { + "title": "Message d'accueil", + "input_type": "checkbox", + "explanation": "Envoie un mail d'accueil à l'utilisateur.", + "labels": ("",), + "allowed_values": ("1",), + "default": "1", + }, + ), + ( + "reset_password", + { + "title": "", + "input_type": "checkbox", + "explanation": "indiquer par mail de changer le mot de passe initial", + "labels": ("",), + "allowed_values": ("1",), + "default": "1", + # "attributes": ["style='margin-left:20pt'"], + }, + ), + ] if not auth_dept: # si auth n'a pas de departement (admin global) @@ -479,7 +483,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1): # Traitement initial (mode) : 3 cas # A: envoi de welcome + procedure de reset # B: envoi de welcome seulement (mot de passe saisie dans le formulaire) - # C: Aucun envoi (mot de pase saisi dans le formulaire) + # C: Aucun envoi (mot de passe saisi dans le formulaire) if vals["welcome:list"] == "1": if vals["reset_password:list"] == "1": mode = "A" @@ -498,7 +502,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1): """Les deux mots de passes ne correspondent pas !""" ) return "\n".join(H) + msg + "\n" + tf[1] + F - if not sco_users.is_valid_password(vals["password"]): + if not is_valid_password(vals["password"]): msg = tf_error_message( """Mot de passe trop simple, recommencez !""" ) @@ -521,7 +525,7 @@ def create_user_form(user_name=None, edit=0, all_roles=1): else: token = None send_email( - "[ScoDoc] Réinitialisation de votre mot de passe", + "[ScoDoc] Création de votre compte", sender=current_app.config["ADMINS"][0], recipients=[u.email], text_body=render_template("email/welcome.txt", user=u, token=token), @@ -735,7 +739,7 @@ def change_password(user_name, password, password2): % user_name ) else: - if not sco_users.is_valid_password(password): + if not is_valid_password(password): H.append( """

      ce mot de passe n\'est pas assez compliqué !
      (oui, il faut un mot de passe vraiment compliqué !)

      Recommencer

      From 177a8912365e787efd76adf7702581036529d637 Mon Sep 17 00:00:00 2001 From: Place Jean-Marie Date: Sat, 16 Oct 2021 07:10:55 +0200 Subject: [PATCH 16/27] =?UTF-8?q?ajout=20v=C3=A9rification=20date=20d'expi?= =?UTF-8?q?ration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/users.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/users.py b/app/views/users.py index d293f3bc0..98d14f303 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -436,6 +436,9 @@ def create_user_form(user_name=None, edit=0, all_roles=1): vals["date_expiration"] = datetime.datetime.strptime( vals["date_expiration"], "%d/%m/%Y" ) + if vals["date_expiration"] < datetime.datetime.now(): + H.append(tf_error_message("date expiration passée")) + return "\n".join(H) + "\n" + tf[1] + F else: vals["date_expiration"] = None except ValueError: From 4ce50927b077d356384e30e49650e55cc32ed26c Mon Sep 17 00:00:00 2001 From: Place Jean-Marie Date: Sat, 16 Oct 2021 10:10:35 +0200 Subject: [PATCH 17/27] clear timezone for datetime values --- app/scodoc/sco_excel.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index f3fc0ba2a..4258a16ae 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -285,9 +285,15 @@ class ScoExcelSheet: def make_cell(self, value: any = None, style=None, comment=None): """Construit une cellule. - value -- contenu de la cellule (texte ou numérique) + value -- contenu de la cellule (texte, numérique, booléen ou date) style -- style par défaut (dictionnaire cf. excel_make_style) de la feuille si non spécifié """ + if value is False: + value = 0 + elif value is True: + value = 1 + if isinstance(value, datetime.datetime): + value = value.replace(tzinfo=None) # make date naive (cf https://openpyxl.readthedocs.io/en/latest/datetime.html#timezones) cell = WriteOnlyCell(self.ws, value or "") if style is None: style = self.default_style From 7cc9f6d1f4ec5b5289ce28004b3e4651113e5aa0 Mon Sep 17 00:00:00 2001 From: Place Jean-Marie Date: Sat, 16 Oct 2021 10:25:40 +0200 Subject: [PATCH 18/27] clear timezone for datetime object --- app/scodoc/sco_excel.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index 4258a16ae..2ae5b59c2 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -288,10 +288,6 @@ class ScoExcelSheet: value -- contenu de la cellule (texte, numérique, booléen ou date) style -- style par défaut (dictionnaire cf. excel_make_style) de la feuille si non spécifié """ - if value is False: - value = 0 - elif value is True: - value = 1 if isinstance(value, datetime.datetime): value = value.replace(tzinfo=None) # make date naive (cf https://openpyxl.readthedocs.io/en/latest/datetime.html#timezones) cell = WriteOnlyCell(self.ws, value or "") From 3c38ef4cc08178d3df7dc8975f86b4cb3ce3c2b0 Mon Sep 17 00:00:00 2001 From: Place Jean-Marie Date: Sat, 16 Oct 2021 14:30:35 +0200 Subject: [PATCH 19/27] fix true_false --- app/scodoc/sco_excel.py | 37 ++++++++++++++++++++++++----------- app/scodoc/sco_groups_view.py | 8 +------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index 2ae5b59c2..586b48b73 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -288,9 +288,31 @@ class ScoExcelSheet: value -- contenu de la cellule (texte, numérique, booléen ou date) style -- style par défaut (dictionnaire cf. excel_make_style) de la feuille si non spécifié """ - if isinstance(value, datetime.datetime): - value = value.replace(tzinfo=None) # make date naive (cf https://openpyxl.readthedocs.io/en/latest/datetime.html#timezones) - cell = WriteOnlyCell(self.ws, value or "") + # adapatation des valeurs si nécessaire + if value is None: + value = "" + elif value is True: + value = 1 + elif value is False: + value = 0 + elif isinstance(value, datetime.datetime): + value = value.replace( + tzinfo=None + ) # make date naive (cf https://openpyxl.readthedocs.io/en/latest/datetime.html#timezones) + + # création de la cellule + cell = WriteOnlyCell(self.ws, value) + + # test datatype to overwrite datetime format + if isinstance(value, datetime.date): + cell.data_type = "d" + cell.number_format = FORMAT_DATE_DDMMYY + elif isinstance(value, int) or isinstance(value, float): + cell.data_type = "n" + else: + cell.data_type = "s" + + # recopie des styles if style is None: style = self.default_style if "font" in style: @@ -312,14 +334,7 @@ class ScoExcelSheet: lines = comment.splitlines() cell.comment.width = 7 * max([len(line) for line in lines]) cell.comment.height = 20 * len(lines) - # test datatype at the end so that datetime format may be overwritten - if isinstance(value, datetime.date): - cell.data_type = "d" - cell.number_format = FORMAT_DATE_DDMMYY - elif isinstance(value, int) or isinstance(value, float): - cell.data_type = "n" - else: - cell.data_type = "s" + return cell def make_row(self, values: list, style=None, comments=None): diff --git a/app/scodoc/sco_groups_view.py b/app/scodoc/sco_groups_view.py index 4fd4e7d62..9a28d8d79 100644 --- a/app/scodoc/sco_groups_view.py +++ b/app/scodoc/sco_groups_view.py @@ -777,13 +777,7 @@ def groups_table( m["parcours"] = Se.get_parcours_descr() m["codeparcours"], _ = sco_report.get_codeparcoursetud(etud) - def dicttakestr(d, keys): - r = [] - for k in keys: - r.append(str(d.get(k, ""))) - return r - - L = [dicttakestr(m, keys) for m in groups_infos.members] + L = [[m.get(k, "") for k in keys] for m in groups_infos.members] title = "etudiants_%s" % groups_infos.groups_filename xls = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title) filename = title From 2ca91fc4e99dce2276aeae86d16d95efc149aa66 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 16 Oct 2021 19:19:07 +0200 Subject: [PATCH 20/27] added some relations --- app/models/formations.py | 9 +++++++++ app/models/formsemestre.py | 7 ++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/models/formations.py b/app/models/formations.py index 5002ab08b..bb8229115 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -32,6 +32,7 @@ class NotesFormation(db.Model): ues = db.relationship("NotesUE", backref="formation", lazy="dynamic") formsemestres = db.relationship("FormSemestre", lazy="dynamic", backref="formation") + ues = db.relationship("NotesUE", lazy="dynamic", backref="formation") def __repr__(self): return f"<{self.__class__.__name__}(id={self.id}, dept_id={self.dept_id}, acronyme='{self.acronyme}')>" @@ -65,6 +66,10 @@ class NotesUE(db.Model): # coef UE, utilise seulement si l'option use_ue_coefs est activée: coefficient = db.Column(db.Float) + # relations + matieres = db.relationship("NotesMatiere", lazy="dynamic", backref="ue") + modules = db.relationship("NotesModule", lazy="dynamic", backref="ue") + def __repr__(self): return f"<{self.__class__.__name__}(id={self.id}, formation_id={self.formation_id}, acronyme='{self.acronyme}')>" @@ -84,6 +89,8 @@ class NotesMatiere(db.Model): titre = db.Column(db.Text()) numero = db.Column(db.Integer) # ordre de présentation + modules = db.relationship("NotesModule", lazy="dynamic", backref="matiere") + class NotesModule(db.Model): """Module""" @@ -110,6 +117,8 @@ class NotesModule(db.Model): # id de l'element pedagogique Apogee correspondant: code_apogee = db.Column(db.String(APO_CODE_STR_LEN)) module_type = db.Column(db.Integer) # NULL ou 0:defaut, 1: malus (NOTES_MALUS) + # Relations: + modimpls = db.relationship("NotesModuleImpl", backref="module", lazy="dynamic") class NotesTag(db.Model): diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 93b781b7d..73830b065 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -70,9 +70,14 @@ class FormSemestre(db.Model): # code element annee Apogee, eg 'VRT1A' ou 'V2INLA,V2INCA,...' elt_annee_apo = db.Column(db.Text()) + # Relations: etapes = db.relationship( - "NotesFormsemestreEtape", cascade="all,delete", backref="notes_formsemestre" + "NotesFormsemestreEtape", cascade="all,delete", backref="formsemestre" ) + formsemestres = db.relationship( + "NotesModuleImpl", backref="formsemestre", lazy="dynamic" + ) + # Ancien id ScoDoc7 pour les migrations de bases anciennes # ne pas utiliser après migrate_scodoc7_dept_archive scodoc7_id = db.Column(db.Text(), nullable=True) From 256e89605bf90e7c7ee505f5a41ec269111653e3 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 16 Oct 2021 19:20:36 +0200 Subject: [PATCH 21/27] rename some old methods --- app/__init__.py | 2 + app/scodoc/notes_table.py | 13 ++--- app/scodoc/sco_compute_moy.py | 53 ++++++++++++--------- app/scodoc/sco_edit_formation.py | 4 +- app/scodoc/sco_edit_matiere.py | 2 +- app/scodoc/sco_edit_module.py | 22 ++++----- app/scodoc/sco_edit_ue.py | 8 ++-- app/scodoc/sco_evaluations.py | 12 ++--- app/scodoc/sco_formations.py | 6 ++- app/scodoc/sco_formsemestre_edit.py | 8 ++-- app/scodoc/sco_liste_notes.py | 2 +- app/scodoc/sco_moduleimpl.py | 2 +- app/scodoc/sco_moduleimpl_inscriptions.py | 2 +- app/scodoc/sco_moduleimpl_status.py | 2 +- app/scodoc/sco_placement.py | 2 +- app/scodoc/sco_saisie_notes.py | 12 ++--- app/scodoc/sco_tag_module.py | 2 +- app/views/__init__.py | 2 +- app/views/notes.py | 8 ++-- tests/scenarios/test_scenario1_formation.py | 2 +- tests/unit/sco_fake_gen.py | 2 +- tests/unit/test_formations.py | 8 ++-- 22 files changed, 94 insertions(+), 82 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 65707c26c..41875f33b 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -317,6 +317,8 @@ def set_sco_dept(scodoc_dept: str): g.scodoc_dept_id = dept.id # l'id if not hasattr(g, "db_conn"): ndb.open_db_connection() + if not hasattr(g, "stored_get_formsemestre"): + g.stored_get_formsemestre = {} def user_db_init(): diff --git a/app/scodoc/notes_table.py b/app/scodoc/notes_table.py index 5e8f2f190..136a2d89e 100644 --- a/app/scodoc/notes_table.py +++ b/app/scodoc/notes_table.py @@ -105,9 +105,7 @@ def get_sem_ues_modimpls(formsemestre_id, modimpls=None): modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id) uedict = {} for modimpl in modimpls: - mod = sco_edit_module.do_module_list(args={"module_id": modimpl["module_id"]})[ - 0 - ] + mod = sco_edit_module.module_list(args={"module_id": modimpl["module_id"]})[0] modimpl["module"] = mod if not mod["ue_id"] in uedict: ue = sco_edit_ue.do_ue_list(args={"ue_id": mod["ue_id"]})[0] @@ -212,16 +210,19 @@ class NotesTable(object): valid_evals, mods_att, self.expr_diagnostics, - ) = sco_compute_moy.compute_modimpls_moyennes(self, formsemestre_id) + ) = sco_compute_moy.formsemestre_compute_modimpls_moyennes( + self, formsemestre_id + ) self._mods_att = mods_att # liste des modules avec des notes en attente self._matmoys = {} # moyennes par matieres self._valid_evals = {} # { evaluation_id : eval } for e in valid_evals: self._valid_evals[e["evaluation_id"]] = e # Liste des modules et UE uedict = {} # public member: { ue_id : ue } - self.uedict = uedict + self.uedict = uedict # les ues qui ont un modimpl dans ce semestre for modimpl in self._modimpls: - mod = modimpl["module"] # has been added here by compute_modimpls_moyennes + # module has been added by formsemestre_compute_modimpls_moyennes + mod = modimpl["module"] if not mod["ue_id"] in uedict: ue = sco_edit_ue.do_ue_list(args={"ue_id": mod["ue_id"]})[0] uedict[ue["ue_id"]] = ue diff --git a/app/scodoc/sco_compute_moy.py b/app/scodoc/sco_compute_moy.py index 86ce3bd1b..4d46f065c 100644 --- a/app/scodoc/sco_compute_moy.py +++ b/app/scodoc/sco_compute_moy.py @@ -27,10 +27,10 @@ """Calcul des moyennes de module """ - -import traceback import pprint +import traceback +from flask import url_for, g import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.sco_utils import ( @@ -40,7 +40,7 @@ from app.scodoc.sco_utils import ( EVALUATION_RATTRAPAGE, EVALUATION_SESSION2, ) -from app.scodoc.sco_exceptions import ScoException +from app.scodoc.sco_exceptions import ScoValueError from app import log from app.scodoc import sco_abs from app.scodoc import sco_edit_module @@ -65,7 +65,8 @@ def moduleimpl_has_expression(mod): def formsemestre_expressions_use_abscounts(formsemestre_id): """True si les notes de ce semestre dépendent des compteurs d'absences. - Cela n'est normalement pas le cas, sauf si des formules utilisateur utilisent ces compteurs. + Cela n'est normalement pas le cas, sauf si des formules utilisateur + utilisent ces compteurs. """ # check presence of 'nbabs' in expressions ab = "nb_abs" # chaine recherchée @@ -128,7 +129,7 @@ def compute_user_formula( coefs, coefs_mask, formula, - diag_info={}, # infos supplementaires a placer ds messages d'erreur + diag_info=None, # infos supplementaires a placer ds messages d'erreur use_abs=True, ): """Calcul moyenne a partir des notes et coefs, en utilisant la formule utilisateur (une chaine). @@ -164,9 +165,14 @@ def compute_user_formula( if (user_moy > 20) or (user_moy < 0): etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] - raise ScoException( - """valeur moyenne %s hors limite pour %s""" - % (user_moy, sem["formsemestre_id"], etudid, etud["nomprenom"]) + raise ScoValueError( + f""" + Valeur moyenne {user_moy} hors limite pour + {etud["nomprenom"]}""" ) except: log( @@ -183,7 +189,7 @@ def compute_user_formula( return user_moy -def do_moduleimpl_moyennes(nt, mod): +def compute_moduleimpl_moyennes(nt, modimpl): """Retourne dict { etudid : note_moyenne } pour tous les etuds inscrits au moduleimpl mod, la liste des evaluations "valides" (toutes notes entrées ou en attente), et att (vrai s'il y a des notes en attente dans ce module). @@ -193,12 +199,12 @@ def do_moduleimpl_moyennes(nt, mod): S'il manque des notes et que le coef n'est pas nul, la moyenne n'est pas calculée: NA Ne prend en compte que les evaluations où toutes les notes sont entrées. - Le résultat est une note sur 20. + Le résultat note_moyenne est une note sur 20. """ diag_info = {} # message d'erreur formule - moduleimpl_id = mod["moduleimpl_id"] - is_malus = mod["module"]["module_type"] == scu.MODULE_MALUS - sem = sco_formsemestre.get_formsemestre(mod["formsemestre_id"]) + moduleimpl_id = modimpl["moduleimpl_id"] + is_malus = modimpl["module"]["module_type"] == scu.MODULE_MALUS + sem = sco_formsemestre.get_formsemestre(modimpl["formsemestre_id"]) etudids = sco_moduleimpl.moduleimpl_listeetuds( moduleimpl_id ) # tous, y compris demissions @@ -207,7 +213,7 @@ def do_moduleimpl_moyennes(nt, mod): [ x["etudid"] for x in sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits( - mod["formsemestre_id"] + modimpl["formsemestre_id"] ) ] ) @@ -218,7 +224,7 @@ def do_moduleimpl_moyennes(nt, mod): key=lambda x: (x["numero"], x["jour"], x["heure_debut"]) ) # la plus ancienne en tête - user_expr = moduleimpl_has_expression(mod) + user_expr = moduleimpl_has_expression(modimpl) attente = False # recupere les notes de toutes les evaluations eval_rattr = None @@ -268,7 +274,7 @@ def do_moduleimpl_moyennes(nt, mod): ] # R = {} - formula = scu.unescape_html(mod["computation_expr"]) + formula = scu.unescape_html(modimpl["computation_expr"]) formula_use_abs = "abs" in formula for etudid in insmod_set: # inscrits au semestre et au module @@ -365,7 +371,7 @@ def do_moduleimpl_moyennes(nt, mod): return R, valid_evals, attente, diag_info -def compute_modimpls_moyennes(nt, formsemestre_id): +def formsemestre_compute_modimpls_moyennes(nt, formsemestre_id): """retourne dict { moduleimpl_id : { etudid, note_moyenne_dans_ce_module } }, la liste des moduleimpls, la liste des evaluations valides, liste des moduleimpls avec notes en attente. @@ -383,15 +389,16 @@ def compute_modimpls_moyennes(nt, formsemestre_id): mods_att = [] expr_diags = [] for modimpl in modimpls: - mod = sco_edit_module.do_module_list(args={"module_id": modimpl["module_id"]})[ - 0 - ] + mod = sco_edit_module.module_list(args={"module_id": modimpl["module_id"]})[0] modimpl["module"] = mod # add module dict to moduleimpl (used by nt) moduleimpl_id = modimpl["moduleimpl_id"] assert moduleimpl_id not in D - D[moduleimpl_id], valid_evals_mod, attente, expr_diag = do_moduleimpl_moyennes( - nt, modimpl - ) + ( + D[moduleimpl_id], + valid_evals_mod, + attente, + expr_diag, + ) = compute_moduleimpl_moyennes(nt, modimpl) valid_evals_per_mod[moduleimpl_id] = valid_evals_mod valid_evals += valid_evals_mod if attente: diff --git a/app/scodoc/sco_edit_formation.py b/app/scodoc/sco_edit_formation.py index ec97d9081..362f2312a 100644 --- a/app/scodoc/sco_edit_formation.py +++ b/app/scodoc/sco_edit_formation.py @@ -313,13 +313,13 @@ def invalidate_sems_in_formation(formation_id): def module_move(module_id, after=0, redirect=1): """Move before/after previous one (decrement/increment numero)""" - module = sco_edit_module.do_module_list({"module_id": module_id})[0] + module = sco_edit_module.module_list({"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({"matiere_id": module["matiere_id"]}) + others = sco_edit_module.module_list({"matiere_id": module["matiere_id"]}) # log('others=%s' % others) if len(others) > 1: idx = [p["module_id"] for p in others].index(module_id) diff --git a/app/scodoc/sco_edit_matiere.py b/app/scodoc/sco_edit_matiere.py index d4bdc6d87..13311e9b6 100644 --- a/app/scodoc/sco_edit_matiere.py +++ b/app/scodoc/sco_edit_matiere.py @@ -174,7 +174,7 @@ def do_matiere_delete(oid): raise ScoLockedFormError() log("do_matiere_delete: matiere_id=%s" % oid) # delete all modules in this matiere - mods = sco_edit_module.do_module_list({"matiere_id": oid}) + mods = sco_edit_module.module_list({"matiere_id": oid}) for mod in mods: sco_edit_module.do_module_delete(mod["module_id"]) _matiereEditor.delete(cnx, oid) diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 1aeb20df6..51c5cef19 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -94,7 +94,7 @@ _moduleEditor = ndb.EditableTable( ) -def do_module_list(*args, **kw): +def module_list(*args, **kw): "list modules" cnx = ndb.GetDBConnexion() return _moduleEditor.list(cnx, *args, **kw) @@ -138,7 +138,7 @@ def module_create(matiere_id=None): _MODULE_HELP, ] # cherche le numero adequat (pour placer le module en fin de liste) - Mods = do_module_list(args={"matiere_id": matiere_id}) + Mods = module_list(args={"matiere_id": matiere_id}) if Mods: default_num = max([m["numero"] for m in Mods]) + 10 else: @@ -252,7 +252,7 @@ def do_module_delete(oid): "delete module" from app.scodoc import sco_formations - mod = do_module_list({"module_id": oid})[0] + mod = module_list({"module_id": oid})[0] if module_is_locked(mod["module_id"]): raise ScoLockedFormError() @@ -285,7 +285,7 @@ def module_delete(module_id=None): """Delete a module""" if not module_id: raise ScoValueError("invalid module !") - Mods = do_module_list(args={"module_id": module_id}) + Mods = module_list(args={"module_id": module_id}) if not Mods: raise ScoValueError("Module inexistant !") Mod = Mods[0] @@ -317,7 +317,7 @@ def do_module_edit(val): from app.scodoc import sco_edit_formation # check - mod = do_module_list({"module_id": val["module_id"]})[0] + mod = module_list({"module_id": val["module_id"]})[0] if module_is_locked(mod["module_id"]): # formation verrouillée: empeche de modifier certains champs: protected_fields = ("coefficient", "ue_id", "matiere_id", "semestre_id") @@ -332,7 +332,7 @@ def do_module_edit(val): def check_module_code_unicity(code, field, formation_id, module_id=None): "true si code module unique dans la formation" - Mods = do_module_list(args={"code": code, "formation_id": formation_id}) + Mods = module_list(args={"code": code, "formation_id": formation_id}) if module_id: # edition: supprime le module en cours Mods = [m for m in Mods if m["module_id"] != module_id] @@ -346,7 +346,7 @@ def module_edit(module_id=None): if not module_id: raise ScoValueError("invalid module !") - Mod = do_module_list(args={"module_id": module_id}) + Mod = module_list(args={"module_id": module_id}) if not Mod: raise ScoValueError("invalid module !") Mod = Mod[0] @@ -521,7 +521,7 @@ def edit_module_set_code_apogee(id=None, value=None): value = value.strip("-_ \t") log("edit_module_set_code_apogee: module_id=%s code_apogee=%s" % (module_id, value)) - modules = do_module_list(args={"module_id": module_id}) + modules = module_list(args={"module_id": module_id}) if not modules: return "module invalide" # should not occur @@ -531,7 +531,7 @@ def edit_module_set_code_apogee(id=None, value=None): return value -def module_list(formation_id): +def module_table(formation_id): """Liste des modules de la formation (XXX inutile ou a revoir) """ @@ -548,7 +548,7 @@ def module_list(formation_id): ] editable = current_user.has_permission(Permission.ScoChangeFormation) - for Mod in do_module_list(args={"formation_id": formation_id}): + for Mod in module_list(args={"formation_id": formation_id}): H.append('
    • %s' % Mod) if editable: H.append('modifier' % Mod) @@ -595,7 +595,7 @@ def formation_add_malus_modules(formation_id, titre=None, redirect=True): nb_mod_malus = len( [ mod - for mod in do_module_list(args={"ue_id": ue["ue_id"]}) + for mod in module_list(args={"ue_id": ue["ue_id"]}) if mod["module_type"] == scu.MODULE_MALUS ] ) diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 8cf7c3c05..37e5786b2 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -382,7 +382,7 @@ def _add_ue_semestre_id(ue_list): qui les place à la fin de la liste. """ for ue in ue_list: - Modlist = sco_edit_module.do_module_list(args={"ue_id": ue["ue_id"]}) + Modlist = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]}) if Modlist: ue["semestre_id"] = Modlist[0]["semestre_id"] else: @@ -648,7 +648,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours); H.append("") H.append('
        ') - Modlist = sco_edit_module.do_module_list( + Modlist = sco_edit_module.module_list( args={"matiere_id": Mat["matiere_id"]} ) im = 0 @@ -970,7 +970,7 @@ def formation_table_recap(formation_id, format="html"): for UE in ue_list: Matlist = sco_edit_matiere.do_matiere_list(args={"ue_id": UE["ue_id"]}) for Mat in Matlist: - Modlist = sco_edit_module.do_module_list( + Modlist = sco_edit_module.module_list( args={"matiere_id": Mat["matiere_id"]} ) for Mod in Modlist: @@ -1049,5 +1049,5 @@ def ue_list_semestre_ids(ue): Mais cela n'a pas toujours été le cas dans les programmes pédagogiques officiels, aussi ScoDoc laisse le choix. """ - Modlist = sco_edit_module.do_module_list(args={"ue_id": ue["ue_id"]}) + Modlist = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]}) return sorted(list(set([mod["semestre_id"] for mod in Modlist]))) diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py index cf6be3f13..110fe0bed 100644 --- a/app/scodoc/sco_evaluations.py +++ b/app/scodoc/sco_evaluations.py @@ -302,7 +302,7 @@ def do_evaluation_create( # news M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] - mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] mod["moduleimpl_id"] = M["moduleimpl_id"] mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod sco_news.add( @@ -360,7 +360,7 @@ def do_evaluation_delete(evaluation_id): M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"]) # news - mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] mod["moduleimpl_id"] = M["moduleimpl_id"] mod["url"] = ( scu.NotesURL() + "/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod @@ -410,7 +410,7 @@ def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition= # ---- Liste des groupes complets et incomplets E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] - Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] is_malus = Mod["module_type"] == scu.MODULE_MALUS # True si module de malus formsemestre_id = M["formsemestre_id"] # Si partition_id is None, prend 'all' ou bien la premiere: @@ -852,7 +852,7 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"): T = [] for e in evals: M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=e["moduleimpl_id"])[0] - Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] if (e["evaluation_type"] != scu.EVALUATION_NORMALE) or ( Mod["module_type"] == scu.MODULE_MALUS ): @@ -1033,7 +1033,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True): E = do_evaluation_list({"evaluation_id": evaluation_id})[0] moduleimpl_id = E["moduleimpl_id"] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] - Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] formsemestre_id = M["formsemestre_id"] u = sco_users.user_info(M["responsable_id"]) resp = u["prenomnom"] @@ -1171,7 +1171,7 @@ def evaluation_create_form( else: min_note_max_str = "0" # - Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] # help = """

        Le coefficient d'une évaluation n'est utilisé que pour pondérer les évaluations au sein d'un module. diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py index 019d7833d..6781b1dcd 100644 --- a/app/scodoc/sco_formations.py +++ b/app/scodoc/sco_formations.py @@ -114,7 +114,7 @@ def formation_export(formation_id, export_ids=False, export_tags=True, format=No if not export_ids: del mat["matiere_id"] del mat["ue_id"] - mods = sco_edit_module.do_module_list({"matiere_id": matiere_id}) + mods = sco_edit_module.module_list({"matiere_id": matiere_id}) mat["module"] = mods for mod in mods: if export_tags: @@ -130,7 +130,9 @@ def formation_export(formation_id, export_ids=False, export_tags=True, format=No if mod["ects"] is None: del mod["ects"] - return scu.sendResult(F, name="formation", format=format, force_outer_xml_tag=False, attached=True) + return scu.sendResult( + F, name="formation", format=format, force_outer_xml_tag=False, attached=True + ) def formation_import_xml(doc: str, import_tags=True): diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index bcfdddb70..8d13cf349 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -209,7 +209,7 @@ def do_formsemestre_createwithmodules(edit=False): for ue in uelist: matlist = sco_edit_matiere.do_matiere_list({"ue_id": ue["ue_id"]}) for mat in matlist: - modsmat = sco_edit_module.do_module_list({"matiere_id": mat["matiere_id"]}) + modsmat = sco_edit_module.module_list({"matiere_id": mat["matiere_id"]}) # XXX debug checks for m in modsmat: if m["ue_id"] != ue["ue_id"]: @@ -767,7 +767,7 @@ def do_formsemestre_createwithmodules(edit=False): "responsable_id": tf[2]["MI" + str(module_id)], } moduleimpl_id = sco_moduleimpl.do_moduleimpl_create(modargs) - mod = sco_edit_module.do_module_list({"module_id": module_id})[0] + mod = sco_edit_module.module_list({"module_id": module_id})[0] msg += ["création de %s (%s)" % (mod["code"], mod["titre"])] # INSCRIPTIONS DES ETUDIANTS log( @@ -813,7 +813,7 @@ def do_formsemestre_createwithmodules(edit=False): sco_moduleimpl.do_moduleimpl_edit( modargs, formsemestre_id=formsemestre_id ) - mod = sco_edit_module.do_module_list({"module_id": module_id})[0] + mod = sco_edit_module.module_list({"module_id": module_id})[0] if msg: msg_html = ( @@ -849,7 +849,7 @@ def formsemestre_delete_moduleimpls(formsemestre_id, module_ids_to_del): moduleimpl_id = sco_moduleimpl.moduleimpl_list( formsemestre_id=formsemestre_id, module_id=module_id )[0]["moduleimpl_id"] - mod = sco_edit_module.do_module_list({"module_id": module_id})[0] + mod = sco_edit_module.module_list({"module_id": module_id})[0] # Evaluations dans ce module ? evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id}) if evals: diff --git a/app/scodoc/sco_liste_notes.py b/app/scodoc/sco_liste_notes.py index 04f94fff8..cee1a4c39 100644 --- a/app/scodoc/sco_liste_notes.py +++ b/app/scodoc/sco_liste_notes.py @@ -229,7 +229,7 @@ def _make_table_notes( E = evals[0] moduleimpl_id = E["moduleimpl_id"] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] - Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) # (debug) check that all evals are in same module: for e in evals: diff --git a/app/scodoc/sco_moduleimpl.py b/app/scodoc/sco_moduleimpl.py index 87a11bf7c..b18e9b5c9 100644 --- a/app/scodoc/sco_moduleimpl.py +++ b/app/scodoc/sco_moduleimpl.py @@ -148,7 +148,7 @@ def moduleimpl_withmodule_list( for mi in modimpls: module_id = mi["module_id"] if not mi["module_id"] in modules: - modules[module_id] = sco_edit_module.do_module_list( + modules[module_id] = sco_edit_module.module_list( args={"module_id": module_id} )[0] mi["module"] = modules[module_id] diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index 1517e6268..41ad53fad 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -65,7 +65,7 @@ def moduleimpl_inscriptions_edit(moduleimpl_id, etuds=[], submitted=False): """ M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] formsemestre_id = M["formsemestre_id"] - mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] sem = sco_formsemestre.get_formsemestre(formsemestre_id) # -- check lock if not sem["etat"]: diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index a54a0e431..899b8778a 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -158,7 +158,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None): """Tableau de bord module (liste des evaluations etc)""" M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] formsemestre_id = M["formsemestre_id"] - Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] sem = sco_formsemestre.get_formsemestre(formsemestre_id) F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] ModInscrits = sco_moduleimpl.do_moduleimpl_inscription_list( diff --git a/app/scodoc/sco_placement.py b/app/scodoc/sco_placement.py index e09df0e88..6fa6f412f 100644 --- a/app/scodoc/sco_placement.py +++ b/app/scodoc/sco_placement.py @@ -247,7 +247,7 @@ class PlacementRunner: self.moduleimpl_data = sco_moduleimpl.moduleimpl_list( moduleimpl_id=self.moduleimpl_id )[0] - self.module_data = sco_edit_module.do_module_list( + self.module_data = sco_edit_module.module_list( args={"module_id": self.moduleimpl_data["module_id"]} )[0] self.sem = sco_formsemestre.get_formsemestre( diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index 6c6638cd1..356ff339d 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -252,7 +252,7 @@ def do_evaluation_upload_xls(): # news E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] - mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] mod["moduleimpl_id"] = M["moduleimpl_id"] mod["url"] = url_for( "notes.moduleimpl_status", @@ -337,7 +337,7 @@ def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False): nb_changed, _, _ = _notes_add(current_user, evaluation_id, L, comment) # news M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] - mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] mod["moduleimpl_id"] = M["moduleimpl_id"] mod["url"] = url_for( "notes.moduleimpl_status", @@ -426,7 +426,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False): ] # news M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] - mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] mod["moduleimpl_id"] = M["moduleimpl_id"] mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod sco_news.add( @@ -767,7 +767,7 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]): E = evals[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] formsemestre_id = M["formsemestre_id"] - Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) mod_responsable = sco_users.user_info(M["responsable_id"]) if E["jour"]: @@ -845,7 +845,7 @@ def has_existing_decision(M, E, etudid): return True dec_ues = nt.get_etud_decision_ues(etudid) if dec_ues: - mod = sco_edit_module.do_module_list({"module_id": M["module_id"]})[0] + mod = sco_edit_module.module_list({"module_id": M["module_id"]})[0] ue_id = mod["ue_id"] if ue_id in dec_ues: return True # decision pour l'UE a laquelle appartient cette evaluation @@ -1225,7 +1225,7 @@ def save_note(etudid=None, evaluation_id=None, value=None, comment=""): ) E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] - Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] Mod["url"] = url_for( "notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, diff --git a/app/scodoc/sco_tag_module.py b/app/scodoc/sco_tag_module.py index 944f95053..b106a754e 100644 --- a/app/scodoc/sco_tag_module.py +++ b/app/scodoc/sco_tag_module.py @@ -242,7 +242,7 @@ def module_tag_set(module_id="", taglist=None): taglist = [t.strip() for t in taglist] # log("module_tag_set: module_id=%s taglist=%s" % (module_id, taglist)) # Sanity check: - Mod = sco_edit_module.do_module_list(args={"module_id": module_id}) + Mod = sco_edit_module.module_list(args={"module_id": module_id}) if not Mod: raise ScoValueError("invalid module !") diff --git a/app/views/__init__.py b/app/views/__init__.py index 9dbefc445..9ceb5c41e 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -27,7 +27,7 @@ def start_scodoc_request(): """Affecte toutes les requêtes, de tous les blueprints""" # current_app.logger.info(f"start_scodoc_request") ndb.open_db_connection() - if current_user.is_authenticated: + if current_user and current_user.is_authenticated: current_user.last_seen = datetime.datetime.utcnow() db.session.commit() # caches locaux (durée de vie=la requête en cours) diff --git a/app/views/notes.py b/app/views/notes.py index ece1029ee..0b089e3c5 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -396,7 +396,7 @@ sco_publish( sco_edit_module.edit_module_set_code_apogee, Permission.ScoChangeFormation, ) -sco_publish("/module_list", sco_edit_module.module_list, Permission.ScoView) +sco_publish("/module_list", sco_edit_module.module_table, Permission.ScoView) sco_publish("/module_tag_search", sco_tag_module.module_tag_search, Permission.ScoView) sco_publish( "/module_tag_set", @@ -1528,7 +1528,7 @@ def evaluation_delete(evaluation_id): raise ValueError("Evalution inexistante ! (%s)" % evaluation_id) E = El[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] - Mod = sco_edit_module.do_module_list(args={"module_id": M["module_id"]})[0] + Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] tit = "Suppression de l'évaluation %(description)s (%(jour)s)" % E etat = sco_evaluations.do_evaluation_etat(evaluation_id) H = [ @@ -2429,7 +2429,7 @@ def check_sem_integrity(formsemestre_id, fix=False): bad_sem = [] formations_set = set() # les formations mentionnées dans les UE et modules for modimpl in modimpls: - mod = sco_edit_module.do_module_list({"module_id": modimpl["module_id"]})[0] + mod = sco_edit_module.module_list({"module_id": modimpl["module_id"]})[0] formations_set.add(mod["formation_id"]) ue = sco_edit_ue.do_ue_list({"ue_id": mod["ue_id"]})[0] formations_set.add(ue["formation_id"]) @@ -2495,7 +2495,7 @@ def check_form_integrity(formation_id, fix=False): for ue in ues: mats = sco_edit_matiere.do_matiere_list(args={"ue_id": ue["ue_id"]}) for mat in mats: - mods = sco_edit_module.do_module_list({"matiere_id": mat["matiere_id"]}) + mods = sco_edit_module.module_list({"matiere_id": mat["matiere_id"]}) for mod in mods: if mod["ue_id"] != ue["ue_id"]: if fix: diff --git a/tests/scenarios/test_scenario1_formation.py b/tests/scenarios/test_scenario1_formation.py index 197b103cc..9447674b9 100644 --- a/tests/scenarios/test_scenario1_formation.py +++ b/tests/scenarios/test_scenario1_formation.py @@ -52,7 +52,7 @@ def run_scenario1(): ] # --- Implémentation des modules - modules = sco_edit_module.do_module_list({"formation_id": formation_id}) + modules = sco_edit_module.module_list({"formation_id": formation_id}) mods_imp = [] for mod in modules: mi = G.create_moduleimpl( diff --git a/tests/unit/sco_fake_gen.py b/tests/unit/sco_fake_gen.py index 1daf142b9..f761403eb 100644 --- a/tests/unit/sco_fake_gen.py +++ b/tests/unit/sco_fake_gen.py @@ -203,7 +203,7 @@ class ScoFake(object): module_type=None, ): oid = sco_edit_module.do_module_create(locals()) - oids = sco_edit_module.do_module_list(args={"module_id": oid}) + oids = sco_edit_module.module_list(args={"module_id": oid}) if not oids: raise ScoValueError("module not created ! (oid=%s)" % oid) return oids[0] diff --git a/tests/unit/test_formations.py b/tests/unit/test_formations.py index 36cb67367..534638471 100644 --- a/tests/unit/test_formations.py +++ b/tests/unit/test_formations.py @@ -33,7 +33,7 @@ # - moduleimpl_list # - do_module_impl_with_module_list # - do_formsemestre_delete -# - do_module_list +# - module_list # - do_module_delete # - do_matiere_list # - do_matiere_delete @@ -290,12 +290,12 @@ def test_formations(test_client): # RIEN NE SE PASSE AVEC CES FONCTIONS - li_module = sco_edit_module.do_module_list() + li_module = sco_edit_module.module_list() assert len(li_module) == 4 sco_edit_module.do_module_delete(oid=modt["module_id"]) # on supprime le semestre # sco_formsemestre_edit.formsemestre_delete_moduleimpls( formsemestre_id=sem2["formsemestre_id"], module_ids_to_del=[modt["module_id"]]) # deuxieme methode de supression d'un module - li_module2 = sco_edit_module.do_module_list() + li_module2 = sco_edit_module.module_list() assert len(li_module2) == 3 # verification de la suppression du module @@ -355,7 +355,7 @@ def test_import_formation(test_client): ) ] # et les modules - modules = sco_edit_module.do_module_list({"formation_id": formation_id}) + modules = sco_edit_module.module_list({"formation_id": formation_id}) for mod in modules: mi = G.create_moduleimpl( module_id=mod["module_id"], From bb7ed682c0e03080fc2c2be7178bd16ea09b51d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 16 Oct 2021 19:31:14 +0200 Subject: [PATCH 22/27] Fixes #158 --- app/scodoc/htmlutils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/scodoc/htmlutils.py b/app/scodoc/htmlutils.py index cb3568752..68e835c3a 100644 --- a/app/scodoc/htmlutils.py +++ b/app/scodoc/htmlutils.py @@ -104,6 +104,8 @@ def make_menu(title, items, css_class="", alone=False): item["urlq"] = url_for( item["endpoint"], scodoc_dept=g.scodoc_dept, **args ) + elif "url" in item: + item["urlq"] = item["url"] else: item["urlq"] = "#" item["attr"] = item.get("attr", "") From 390118226dca712ea27488fe7a6f8b10dfe6b12b Mon Sep 17 00:00:00 2001 From: Place Jean-Marie Date: Sat, 16 Oct 2021 23:22:03 +0200 Subject: [PATCH 23/27] modification du formulaire de changement de mot de passe personnel --- app/api/auth.py | 2 +- app/templates/auth/change_password.html | 46 +++++++++++++ app/views/users.py | 90 +++++++++++++++++-------- 3 files changed, 108 insertions(+), 30 deletions(-) create mode 100644 app/templates/auth/change_password.html diff --git a/app/api/auth.py b/app/api/auth.py index 0226976cd..24348aab8 100644 --- a/app/api/auth.py +++ b/app/api/auth.py @@ -33,7 +33,7 @@ token_auth = HTTPTokenAuth() @basic_auth.verify_password def verify_password(username, password): - user = User.query.filter_by(username=username).first() + user = User.query.filter_by(user_name=username).first() if user and user.check_password(password): return user diff --git a/app/templates/auth/change_password.html b/app/templates/auth/change_password.html new file mode 100644 index 000000000..228982ab6 --- /dev/null +++ b/app/templates/auth/change_password.html @@ -0,0 +1,46 @@ +{% extends "base.html" %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% macro render_field(field) %} + + {{ field.label }} + {{ field(**kwargs)|safe }} + {% if field.errors %} +

          + {% for error in field.errors %} +
        • {{ error }}
        • + {% endfor %} +
        + {% endif %} + + +{% endmacro %} + +{% block app_content %} +

        Changez vos données personnelles

        +

        Identifiez vous avez votre mot de passe actuel

        +

        Vous pouvez changer votre mot de passe (laisez les champs vides sinon)

        +

        et/ou votre adresse email.

        + +
        + {{ form.user_name }} + {{ form.csrf_token }} + + {{ render_field(form.old_password, size=14, style="padding:1px;") }} + {{ render_field(form.new_password, size=14, style="padding:1px;") }} + {{ render_field(form.bis_password, size=14, style="padding:1px;") }} + {{ render_field(form.email, size=40, style="padding:1px;") }} + {{ render_field(form.submit) }} +
        +
        + +{#
        #} +{#
        Votre identifiant: {{user.user_name}}
        #} +{#
        #} +{##} +{#
        #} +{#
        #} +{# {{ wtf.quick_form(form) }}#} +{#
        #} +{#
        #} +{% endblock %} \ No newline at end of file diff --git a/app/views/users.py b/app/views/users.py index 98d14f303..84f176606 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -38,12 +38,15 @@ import re from xml.etree import ElementTree import flask -from flask import g, url_for, request, current_app +from flask import g, url_for, request, current_app, flash from flask import redirect, render_template from flask_login import current_user +from wtforms import HiddenField, PasswordField, StringField, SubmitField +from wtforms.validators import DataRequired, Email, ValidationError, EqualTo from app import db +from app.api.auth import verify_password from app.auth.forms import DeactivateUserForm from app.auth.models import Permission from app.auth.models import User @@ -69,6 +72,40 @@ from app.scodoc.sco_import_users import generate_password from app.scodoc.sco_permissions_check import can_handle_passwd from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message from app.views import users_bp as bp +from flask_wtf import FlaskForm + +_ = lambda x: x # sans babel +_l = _ + + +class ChangePasswordForm(FlaskForm): + user_name = HiddenField() + old_password = PasswordField(_l("Ancien mot de passe")) + new_password = PasswordField(_l("Nouveau mot de passe")) + bis_password = PasswordField( + _l("Répéter"), + validators=[ + EqualTo( + "new_password", + message="Les deux saisies sont " "différentes, recommencez", + ), + ], + ) + email = StringField(_l("Email"), validators=[DataRequired(), Email()]) + submit = SubmitField(_l("Modifier")) + + def validate_email(self, email): + user = User.query.filter_by(email=email.data).first() + if user is not None and self.user_name.data != user.user_name: + raise ValidationError(_("Please choose a different email address.")) + + def validate_new_password(self, new_password): + if new_password.data != "" and not is_valid_password(new_password.data): + raise ValidationError(f"Mot de passe trop simple, recommencez") + + def validate_old_password(self, old_password): + if not verify_password(self.user_name.data, old_password.data): + raise ValidationError("Ancien mot de passe incorrect, recommenccez") @bp.route("/") @@ -676,7 +713,7 @@ def get_user_list_xml(dept=None, start="", limit=25): return scu.send_file(data, mime=scu.XML_MIMETYPE) -@bp.route("/form_change_password") +@bp.route("/form_change_password", methods=["GET", "POST"]) @scodoc @permission_required(Permission.ScoView) @scodoc7func @@ -685,36 +722,31 @@ def form_change_password(user_name=None): Un utilisateur peut toujours changer son propre mot de passe. """ if not user_name: - u = current_user + user = current_user else: - u = User.query.filter_by(user_name=user_name).first() - H = [html_sco_header.sco_header(user_check=False)] - F = html_sco_header.sco_footer() + user = User.query.filter_by(user_name=user_name).first() + # check access - if not can_handle_passwd(u): - return ( - "\n".join(H) - + "

        Vous n'avez pas la permission de changer ce mot de passe

        " - + F + if not can_handle_passwd(user): + return "\n".join( + [ + html_sco_header.sco_header(user_check=False), + "

        Vous n'avez pas la permission de changer ce mot de passe

        ", + html_sco_header.sco_footer(), + ] ) - # - H.append( - """

        Changement du mot de passe de %(nomplogin)s

        -

        -

        - - -
        Nouveau mot de passe:
        Confirmation:
        - - -

        -

        Note: en ScoDoc 9, les utilisateurs peuvent changer eux-même leur mot de passe - en indiquant l'adresse mail associée à leur compte. -

        -""" - % {"nomplogin": u.get_nomplogin(), "user_name": user_name} - ) - return "\n".join(H) + F + form = ChangePasswordForm(user_name=user.user_name, email=user.email) + if form.validate_on_submit(): + messages = [] + if form.new_password.data != "": # change password + user.set_password(form.new_password.data) + messages.append("Mot de passe modifié") + if form.email.data != user.email: # change email + user.email = form.email.data + messages.append("Adresse email modifiée") + db.session.commit() + flash("\n".join(messages)) + return render_template("auth/change_password.html", form=form) @bp.route("/change_password", methods=["POST"]) From a909a307c022f323bd19b0b329bac2653d80de03 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sun, 17 Oct 2021 11:19:01 +0200 Subject: [PATCH 24/27] =?UTF-8?q?Am=C3=A9liorer=20page=20infos=20utilisate?= =?UTF-8?q?urs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/auth/forms.py | 7 +- app/auth/routes.py | 4 +- app/scodoc/sco_users.py | 97 +------------------ app/templates/auth/change_password.html | 26 +++-- .../auth/reset_password_request.html | 2 +- app/templates/auth/user_info_page.html | 67 +++++++++++++ app/templates/base.html | 13 ++- app/templates/email/reset_password.html | 2 +- app/views/users.py | 50 ++++++++-- sco_version.py | 2 +- 10 files changed, 147 insertions(+), 123 deletions(-) create mode 100644 app/templates/auth/user_info_page.html diff --git a/app/auth/forms.py b/app/auth/forms.py index dd1f68ef1..3d70054d8 100644 --- a/app/auth/forms.py +++ b/app/auth/forms.py @@ -43,8 +43,11 @@ class UserCreationForm(FlaskForm): class ResetPasswordRequestForm(FlaskForm): - email = StringField(_l("Email"), validators=[DataRequired(), Email()]) - submit = SubmitField(_l("Valider ce mot de passe")) + email = StringField( + _l("Adresse email associée à votre compte ScoDoc:"), + validators=[DataRequired(), Email()], + ) + submit = SubmitField(_l("Envoyer")) class ResetPasswordForm(FlaskForm): diff --git a/app/auth/routes.py b/app/auth/routes.py index 61f2bceac..df3401515 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -98,7 +98,9 @@ def reset_password_request(): current_app.logger.info( "reset_password_request: for unkown user '{}'".format(form.email.data) ) - flash(_("Voir les instructions envoyées par mail")) + flash( + _("Voir les instructions envoyées par mail (pensez à regarder vos spams)") + ) return redirect(url_for("auth.login")) return render_template( "auth/reset_password_request.html", title=_("Reset Password"), form=form diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index 3f1b646d2..eaf434943 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -32,6 +32,7 @@ import re from flask import url_for, g, request +from flask.templating import render_template from flask_login import current_user @@ -271,102 +272,6 @@ def user_info(user_name_or_id=None, user=None): return info -def user_info_page(user_name=None): - """Display page of info about given user. - If user_name not specified, user current_user - """ - from app.scodoc.sco_permissions_check import can_handle_passwd - - # peut on divulguer ces infos ? - if not can_handle_passwd(current_user, allow_admindepts=True): - raise AccessDenied("Vous n'avez pas la permission de voir cette page") - - dept = g.scodoc_dept - if not user_name: - user = current_user - else: - user = User.query.filter_by(user_name=user_name).first() - if not user: - raise ScoValueError("invalid user_name") - H = [ - html_sco_header.sco_header( - page_title="Utilisateur %s" % user.user_name, - ) - ] - F = html_sco_header.sco_footer() - H.append("

        Utilisateur: %s" % user.user_name) - info = user.to_dict() - if info: - H.append(" (%(status_txt)s)" % info) - H.append("

        ") - if not info: - H.append( - "

        L' utilisateur '%s' n'est pas défini dans ce module.

        " % user_name - ) - if user.has_permission(Permission.ScoEditAllNotes, dept): - H.append("

        (il peut modifier toutes les notes de %s)

        " % dept) - if user.has_permission(Permission.ScoEditAllEvals, dept): - H.append("

        (il peut modifier toutes les évaluations de %s)

        " % dept) - if user.has_permission(Permission.ScoImplement, dept): - H.append("

        (il peut creer des formations en %s)

        " % dept) - else: - H.append( - """

        - Login : %(user_name)s
        - Nom : %(nom)s
        - Prénom : %(prenom)s
        - Mail : %(email)s
        - Roles : %(roles_string)s
        - Dept : %(dept)s
        - Dernière modif mot de passe: %(date_modif_passwd)s
        - Date d'expiration: %(date_expiration)s -

        ") - - if current_user.user_name == user_name: - H.append( - '

        Se déconnecter: logout

        ' - % url_for("auth.logout") - ) - # Liste des permissions - H.append( - '

        Permissions de cet utilisateur dans le département %s:

          ' - % dept - ) - for p in Permission.description: - perm = getattr(Permission, p) - if user.has_permission(perm, dept): - b = "oui" - else: - b = "non" - H.append("
        • %s : %s
        • " % (Permission.description[p], b)) - H.append("
        ") - - if current_user.has_permission(Permission.ScoUsersAdmin, dept): - H.append( - '

        Liste de tous les utilisateurs

        ' - % url_for("users.index_html", scodoc_dept=g.scodoc_dept) - ) - return "\n".join(H) + F - - def check_modif_user( edit, enforce_optionals=False, diff --git a/app/templates/auth/change_password.html b/app/templates/auth/change_password.html index 228982ab6..702059c8c 100644 --- a/app/templates/auth/change_password.html +++ b/app/templates/auth/change_password.html @@ -17,21 +17,27 @@ {% endmacro %} {% block app_content %} -

        Changez vos données personnelles

        -

        Identifiez vous avez votre mot de passe actuel

        -

        Vous pouvez changer votre mot de passe (laisez les champs vides sinon)

        -

        et/ou votre adresse email.

        - +

        Modification du compte ScoDoc {{form.user_name.data}}

        +
        +

        Identifiez-vous avez votre mot de passe actuel

        +

        Vous pouvez changer le mot de passe et/ou l'adresse email.

        +

        Les champs vides ne seront pas changés.

        +
        {{ form.user_name }} {{ form.csrf_token }} - {{ render_field(form.old_password, size=14, style="padding:1px;") }} - {{ render_field(form.new_password, size=14, style="padding:1px;") }} - {{ render_field(form.bis_password, size=14, style="padding:1px;") }} - {{ render_field(form.email, size=40, style="padding:1px;") }} - {{ render_field(form.submit) }} + {{ render_field(form.old_password, size=14, + style="padding:1px; margin-left: 1em; margin-top: 4px;") }} + {{ render_field(form.new_password, size=14, + style="padding:1px; margin-left: 1em; margin-top: 12px;") }} + {{ render_field(form.bis_password, size=14, + style="padding:1px; margin-left: 1em; margin-top: 4px;") }} + {{ render_field(form.email, size=40, + style="padding:1px; margin-top: 12px;margin-bottom: 16px; margin-left: 1em;") }}
        + + #} diff --git a/app/templates/auth/reset_password_request.html b/app/templates/auth/reset_password_request.html index 6fc7329f3..99a37fb2f 100644 --- a/app/templates/auth/reset_password_request.html +++ b/app/templates/auth/reset_password_request.html @@ -2,7 +2,7 @@ {% import 'bootstrap/wtf.html' as wtf %} {% block app_content %} -

        Reset Password

        +

        Demande d'un nouveau mot de passe

        {{ wtf.quick_form(form) }} diff --git a/app/templates/auth/user_info_page.html b/app/templates/auth/user_info_page.html new file mode 100644 index 000000000..695929991 --- /dev/null +++ b/app/templates/auth/user_info_page.html @@ -0,0 +1,67 @@ +{% extends "base.html" %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block app_content %} + +

        Utilisateur: {{user.user_name}} ({{'actif' if user.active else 'fermé'}})

        +

        +Login : {{user.user_name}}
        +Nom : {{user.nom or ""}}
        +Prénom : {{user.prenom or ""}}
        +Mail : {{user.email}}
        +Roles : {{user.get_roles_string()}}
        +Dept : {{user.dept or ""}}
        +Dernière modif mot de passe: +{{user.date_modif_passwd.isoformat() if user.date_modif_passwd else ""}}
        +Date d'expiration: +{{user.date_expiration.isoformat() if user.date_expiration else "(sans limite)"}} +

        +

        + +{% if current_user.id == user.id %} +

        Se déconnecter: +logout +

        +{% endif %} + +{# Liste des permissions #} +
        +

        Permissions de cet utilisateur dans le département {dept}:

        +
          +{% for p in Permission.description %} +
        • {{Permission.description[p]}} : + {{ + "oui" if user.has_permission(Permission.get_by_name(p), dept) else "non" + }} +
        • +{% endfor %} +
        +
        + +{% if current_user.has_permission(Permission.ScoUsersAdmin, dept) %} +

        Liste de tous les utilisateurs

        + +{% endif %} + + +{% endblock %} \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html index 0be7c6fce..815ba7b76 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -22,12 +22,19 @@ ScoDoc
        +
    • #} -{##} -{#
      #} -{#
      #} -{# {{ wtf.quick_form(form) }}#} -{#
      #} -{#
      #} {% endblock %} \ No newline at end of file diff --git a/app/views/users.py b/app/views/users.py index e1709c92a..7a03a899c 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -79,7 +79,7 @@ _l = _ class ChangePasswordForm(FlaskForm): user_name = HiddenField() - old_password = PasswordField(_l("Ancien mot de passe")) + old_password = PasswordField(_l("Identifiez-vous")) new_password = PasswordField(_l("Nouveau mot de passe")) bis_password = PasswordField( _l("Répéter"), @@ -90,14 +90,22 @@ class ChangePasswordForm(FlaskForm): ), ], ) - email = StringField(_l("Email"), validators=[DataRequired(), Email()]) + email = StringField( + _l("Email"), + validators=[ + DataRequired(), + Email(message="adresse email invalide, recommencez"), + ], + ) submit = SubmitField() cancel = SubmitField("Annuler") def validate_email(self, email): - user = User.query.filter_by(email=email.data).first() + user = User.query.filter_by(email=email.data.strip()).first() if user is not None and self.user_name.data != user.user_name: - raise ValidationError(_("Adresse e-mail invalide")) + raise ValidationError( + _("Cette adresse e-mail est déjà attribuée à un autre compte") + ) def validate_new_password(self, new_password): if new_password.data != "" and not is_valid_password(new_password.data): @@ -771,8 +779,8 @@ def form_change_password(user_name=None): if form.new_password.data != "": # change password user.set_password(form.new_password.data) messages.append("Mot de passe modifié") - if form.email.data != user.email: # change email - user.email = form.email.data + if form.email.data.strip() != user.email: # change email + user.email = form.email.data.strip() messages.append("Adresse email modifiée") db.session.commit() flash("\n".join(messages)) From d69a6c283f7cf4fbbe3b05e555845abeddfa82e1 Mon Sep 17 00:00:00 2001 From: Place Jean-Marie Date: Sun, 17 Oct 2021 20:20:31 +0200 Subject: [PATCH 26/27] restore date formatting at the right place --- app/scodoc/sco_excel.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index 586b48b73..77fe375db 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -303,15 +303,6 @@ class ScoExcelSheet: # création de la cellule cell = WriteOnlyCell(self.ws, value) - # test datatype to overwrite datetime format - if isinstance(value, datetime.date): - cell.data_type = "d" - cell.number_format = FORMAT_DATE_DDMMYY - elif isinstance(value, int) or isinstance(value, float): - cell.data_type = "n" - else: - cell.data_type = "s" - # recopie des styles if style is None: style = self.default_style @@ -335,6 +326,15 @@ class ScoExcelSheet: cell.comment.width = 7 * max([len(line) for line in lines]) cell.comment.height = 20 * len(lines) + # test datatype to overwrite datetime format + if isinstance(value, datetime.date): + cell.data_type = "d" + cell.number_format = FORMAT_DATE_DDMMYY + elif isinstance(value, int) or isinstance(value, float): + cell.data_type = "n" + else: + cell.data_type = "s" + return cell def make_row(self, values: list, style=None, comments=None): From 54ed09ed083791076c23c31c33345d67fcb06d36 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sun, 17 Oct 2021 23:19:26 +0200 Subject: [PATCH 27/27] renomme: ue_list, matiere_list --- app/scodoc/bonus_sport.py | 3 +- app/scodoc/notes_table.py | 12 +++---- app/scodoc/sco_bulletins_json.py | 2 +- app/scodoc/sco_bulletins_xml.py | 2 +- app/scodoc/sco_edit_formation.py | 12 +++---- app/scodoc/sco_edit_matiere.py | 30 ++++++++-------- app/scodoc/sco_edit_module.py | 14 ++++---- app/scodoc/sco_edit_ue.py | 42 +++++++++++------------ app/scodoc/sco_evaluations.py | 2 +- app/scodoc/sco_formations.py | 6 ++-- app/scodoc/sco_formsemestre_edit.py | 4 +-- app/scodoc/sco_formsemestre_exterieurs.py | 2 +- app/scodoc/sco_formsemestre_status.py | 6 ++-- app/scodoc/sco_formsemestre_validation.py | 4 +-- app/scodoc/sco_moduleimpl.py | 4 +-- app/scodoc/sco_moduleimpl_inscriptions.py | 2 +- app/scodoc/sco_pvjury.py | 2 +- app/scodoc/sco_ue_external.py | 4 +-- app/views/notes.py | 16 ++++----- tests/unit/sco_fake_gen.py | 4 +-- tests/unit/test_formations.py | 12 +++---- 21 files changed, 91 insertions(+), 94 deletions(-) diff --git a/app/scodoc/bonus_sport.py b/app/scodoc/bonus_sport.py index 51762b4ce..4a3f8abae 100644 --- a/app/scodoc/bonus_sport.py +++ b/app/scodoc/bonus_sport.py @@ -194,7 +194,8 @@ def bonus_tours(notes_sport, coefs, infos=None): def bonus_iutr(notes_sport, coefs, infos=None): - """Calcul du bonus , regle de l'IUT de Roanne (contribuée par Raphael C., nov 2012) + """Calcul du bonus , règle de l'IUT de Roanne + (contribuée par Raphael C., nov 2012) Le bonus est compris entre 0 et 0.35 point. cette procédure modifie la moyenne de chaque UE capitalisable. diff --git a/app/scodoc/notes_table.py b/app/scodoc/notes_table.py index 136a2d89e..fb18d050d 100644 --- a/app/scodoc/notes_table.py +++ b/app/scodoc/notes_table.py @@ -108,7 +108,7 @@ def get_sem_ues_modimpls(formsemestre_id, modimpls=None): mod = sco_edit_module.module_list(args={"module_id": modimpl["module_id"]})[0] modimpl["module"] = mod if not mod["ue_id"] in uedict: - ue = sco_edit_ue.do_ue_list(args={"ue_id": mod["ue_id"]})[0] + ue = sco_edit_ue.ue_list(args={"ue_id": mod["ue_id"]})[0] uedict[ue["ue_id"]] = ue ues = list(uedict.values()) ues.sort(key=lambda u: u["numero"]) @@ -224,15 +224,15 @@ class NotesTable(object): # module has been added by formsemestre_compute_modimpls_moyennes mod = modimpl["module"] if not mod["ue_id"] in uedict: - ue = sco_edit_ue.do_ue_list(args={"ue_id": mod["ue_id"]})[0] + ue = sco_edit_ue.ue_list(args={"ue_id": mod["ue_id"]})[0] uedict[ue["ue_id"]] = ue else: 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( - args={"matiere_id": mod["matiere_id"]} - )[0] + mat = sco_edit_matiere.matiere_list(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= @@ -1053,7 +1053,7 @@ class NotesTable(object): "Warning: %s capitalized an UE %s which is not part of current sem %s" % (etudid, ue_id, self.formsemestre_id) ) - ue = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] + ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0] self.uedict[ue_id] = ue # record this UE if ue_id not in self._uecoef: cl = formsemestre_uecoef_list( diff --git a/app/scodoc/sco_bulletins_json.py b/app/scodoc/sco_bulletins_json.py index 160fc30d5..4bcf839e1 100644 --- a/app/scodoc/sco_bulletins_json.py +++ b/app/scodoc/sco_bulletins_json.py @@ -360,7 +360,7 @@ def formsemestre_bulletinetud_published_dict( "decisions_ue" ]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee) for ue_id in decision["decisions_ue"].keys(): - ue = sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] + ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0] d["decision_ue"].append( dict( ue_id=ue["ue_id"], diff --git a/app/scodoc/sco_bulletins_xml.py b/app/scodoc/sco_bulletins_xml.py index bd20c7a1e..efdbe8c08 100644 --- a/app/scodoc/sco_bulletins_xml.py +++ b/app/scodoc/sco_bulletins_xml.py @@ -385,7 +385,7 @@ def make_xml_formsemestre_bulletinetud( "decisions_ue" ]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee) for ue_id in decision["decisions_ue"].keys(): - ue = sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] + ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0] doc.append( Element( "decision_ue", diff --git a/app/scodoc/sco_edit_formation.py b/app/scodoc/sco_edit_formation.py index 362f2312a..7d2e7e726 100644 --- a/app/scodoc/sco_edit_formation.py +++ b/app/scodoc/sco_edit_formation.py @@ -104,7 +104,7 @@ def do_formation_delete(oid): raise ScoLockedFormError() cnx = ndb.GetDBConnexion() # delete all UE in this formation - ues = sco_edit_ue.do_ue_list({"formation_id": oid}) + ues = sco_edit_ue.ue_list({"formation_id": oid}) for ue in ues: sco_edit_ue.do_ue_delete(ue["ue_id"], force=True) @@ -252,7 +252,7 @@ def formation_edit(formation_id=None, create=False): do_formation_edit(tf[2]) return flask.redirect( url_for( - "notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=formation_id + "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id ) ) @@ -343,21 +343,21 @@ def module_move(module_id, after=0, redirect=1): if redirect: return flask.redirect( url_for( - "notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=formation_id + "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id ) ) def ue_move(ue_id, after=0, redirect=1): """Move UE before/after previous one (decrement/increment numero)""" - o = sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] + o = sco_edit_ue.ue_list({"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({"formation_id": formation_id}) + others = sco_edit_ue.ue_list({"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 @@ -378,7 +378,7 @@ def ue_move(ue_id, after=0, redirect=1): if redirect: return flask.redirect( url_for( - "notes.ue_list", + "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=o["formation_id"], ) diff --git a/app/scodoc/sco_edit_matiere.py b/app/scodoc/sco_edit_matiere.py index 13311e9b6..1cbd199d3 100644 --- a/app/scodoc/sco_edit_matiere.py +++ b/app/scodoc/sco_edit_matiere.py @@ -47,7 +47,7 @@ _matiereEditor = ndb.EditableTable( ) -def do_matiere_list(*args, **kw): +def matiere_list(*args, **kw): "list matieres" cnx = ndb.GetDBConnexion() return _matiereEditor.list(cnx, *args, **kw) @@ -60,12 +60,12 @@ def do_matiere_edit(*args, **kw): cnx = ndb.GetDBConnexion() # check - mat = do_matiere_list({"matiere_id": args[0]["matiere_id"]})[0] + mat = matiere_list({"matiere_id": args[0]["matiere_id"]})[0] if matiere_is_locked(mat["matiere_id"]): raise ScoLockedFormError() # edit _matiereEditor.edit(cnx, *args, **kw) - formation_id = sco_edit_ue.do_ue_list({"ue_id": mat["ue_id"]})[0]["formation_id"] + formation_id = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]["formation_id"] sco_edit_formation.invalidate_sems_in_formation(formation_id) @@ -77,7 +77,7 @@ def do_matiere_create(args): cnx = ndb.GetDBConnexion() # check - ue = sco_edit_ue.do_ue_list({"ue_id": args["ue_id"]})[0] + ue = sco_edit_ue.ue_list({"ue_id": args["ue_id"]})[0] # create matiere r = _matiereEditor.create(cnx, args) @@ -96,7 +96,7 @@ def matiere_create(ue_id=None): """Creation d'une matiere""" from app.scodoc import sco_edit_ue - UE = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] + UE = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0] H = [ html_sco_header.sco_header(page_title="Création d'une matière"), """

      Création d'une matière dans l'UE %(titre)s (%(acronyme)s)

      """ % UE, @@ -134,7 +134,7 @@ associé. ) dest_url = url_for( - "notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=UE["formation_id"] + "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=UE["formation_id"] ) if tf[0] == 0: @@ -143,7 +143,7 @@ associé. return flask.redirect(dest_url) else: # check unicity - mats = do_matiere_list(args={"ue_id": ue_id, "titre": tf[2]["titre"]}) + mats = matiere_list(args={"ue_id": ue_id, "titre": tf[2]["titre"]}) if mats: return ( "\n".join(H) @@ -164,8 +164,8 @@ def do_matiere_delete(oid): cnx = ndb.GetDBConnexion() # check - mat = do_matiere_list({"matiere_id": oid})[0] - ue = sco_edit_ue.do_ue_list({"ue_id": mat["ue_id"]})[0] + mat = matiere_list({"matiere_id": oid})[0] + ue = sco_edit_ue.ue_list({"ue_id": mat["ue_id"]})[0] locked = matiere_is_locked(mat["matiere_id"]) if locked: log("do_matiere_delete: mat=%s" % mat) @@ -193,8 +193,8 @@ def matiere_delete(matiere_id=None): """Delete an UE""" from app.scodoc import sco_edit_ue - M = do_matiere_list(args={"matiere_id": matiere_id})[0] - UE = sco_edit_ue.do_ue_list(args={"ue_id": M["ue_id"]})[0] + M = matiere_list(args={"matiere_id": matiere_id})[0] + UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0] H = [ html_sco_header.sco_header(page_title="Suppression d'une matière"), "

      Suppression de la matière %(titre)s" % M, @@ -223,17 +223,17 @@ def matiere_edit(matiere_id=None): from app.scodoc import sco_formations from app.scodoc import sco_edit_ue - F = do_matiere_list(args={"matiere_id": matiere_id}) + F = matiere_list(args={"matiere_id": matiere_id}) if not F: raise ScoValueError("Matière inexistante !") F = F[0] - U = sco_edit_ue.do_ue_list(args={"ue_id": F["ue_id"]}) + U = sco_edit_ue.ue_list(args={"ue_id": F["ue_id"]}) if not F: raise ScoValueError("UE inexistante !") U = U[0] Fo = sco_formations.formation_list(args={"formation_id": U["formation_id"]})[0] - ues = sco_edit_ue.do_ue_list(args={"formation_id": U["formation_id"]}) + ues = sco_edit_ue.ue_list(args={"formation_id": U["formation_id"]}) ue_names = ["%(acronyme)s (%(titre)s)" % u for u in ues] ue_ids = [u["ue_id"] for u in ues] H = [ @@ -286,7 +286,7 @@ associé. return flask.redirect(dest_url) else: # check unicity - mats = do_matiere_list(args={"ue_id": tf[2]["ue_id"], "titre": tf[2]["titre"]}) + mats = matiere_list(args={"ue_id": tf[2]["ue_id"], "titre": tf[2]["titre"]}) if len(mats) > 1 or (len(mats) == 1 and mats[0]["matiere_id"] != matiere_id): return ( "\n".join(H) diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 51c5cef19..a23ddf8c8 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -126,8 +126,8 @@ def module_create(matiere_id=None): if matiere_id is None: raise ScoValueError("invalid matiere !") - M = sco_edit_matiere.do_matiere_list(args={"matiere_id": matiere_id})[0] - UE = sco_edit_ue.do_ue_list(args={"ue_id": M["ue_id"]})[0] + M = sco_edit_matiere.matiere_list(args={"matiere_id": matiere_id})[0] + UE = sco_edit_ue.ue_list(args={"ue_id": M["ue_id"]})[0] Fo = sco_formations.formation_list(args={"formation_id": UE["formation_id"]})[0] parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"]) semestres_indices = list(range(1, parcours.NB_SEM + 1)) @@ -241,7 +241,7 @@ def module_create(matiere_id=None): do_module_create(tf[2]) return flask.redirect( url_for( - "notes.ue_list", + "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=UE["formation_id"], ) @@ -263,7 +263,7 @@ def do_module_delete(oid):

      Il faut d'abord supprimer le semestre. Mais il est peut être préférable de laisser ce programme intact et d'en créer une nouvelle version pour la modifier.

      - reprendre """ raise ScoGenError(err_page) @@ -588,7 +588,7 @@ def formation_add_malus_modules(formation_id, titre=None, redirect=True): """Création d'un module de "malus" dans chaque UE d'une formation""" from app.scodoc import sco_edit_ue - ue_list = sco_edit_ue.do_ue_list(args={"formation_id": formation_id}) + ue_list = sco_edit_ue.ue_list(args={"formation_id": formation_id}) for ue in ue_list: # Un seul module de malus par UE: @@ -610,7 +610,7 @@ def ue_add_malus_module(ue_id, titre=None, code=None): """Add a malus module in this ue""" from app.scodoc import sco_edit_ue - ue = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] + ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0] if titre is None: titre = "" @@ -629,7 +629,7 @@ def ue_add_malus_module(ue_id, titre=None, code=None): ) # Matiere pour placer le module malus - Matlist = sco_edit_matiere.do_matiere_list(args={"ue_id": ue_id}) + Matlist = sco_edit_matiere.matiere_list(args={"ue_id": ue_id}) numero = max([mat["numero"] for mat in Matlist]) + 10 matiere_id = sco_edit_matiere.do_matiere_create( {"ue_id": ue_id, "titre": "Malus", "numero": numero} diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 37e5786b2..cc7d7557d 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -85,7 +85,7 @@ _ueEditor = ndb.EditableTable( ) -def do_ue_list(*args, **kw): +def ue_list(*args, **kw): "list UEs" cnx = ndb.GetDBConnexion() return _ueEditor.list(cnx, *args, **kw) @@ -97,9 +97,7 @@ def do_ue_create(args): cnx = ndb.GetDBConnexion() # check duplicates - ues = do_ue_list( - {"formation_id": args["formation_id"], "acronyme": args["acronyme"]} - ) + ues = ue_list({"formation_id": args["formation_id"], "acronyme": args["acronyme"]}) if ues: raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"]) # create @@ -124,7 +122,7 @@ def do_ue_delete(ue_id, delete_validations=False, force=False): cnx = ndb.GetDBConnexion() log("do_ue_delete: ue_id=%s, delete_validations=%s" % (ue_id, delete_validations)) # check - ue = do_ue_list({"ue_id": ue_id}) + ue = ue_list({"ue_id": ue_id}) if not ue: raise ScoValueError("UE inexistante !") ue = ue[0] @@ -152,7 +150,7 @@ def do_ue_delete(ue_id, delete_validations=False, force=False): ) # delete all matiere in this UE - mats = sco_edit_matiere.do_matiere_list({"ue_id": ue_id}) + mats = sco_edit_matiere.matiere_list({"ue_id": ue_id}) for mat in mats: sco_edit_matiere.do_matiere_delete(mat["matiere_id"]) # delete uecoef and events @@ -177,7 +175,7 @@ def do_ue_delete(ue_id, delete_validations=False, force=False): if not force: return flask.redirect( url_for( - "notes.ue_list", + "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=ue["formation_id"], ) @@ -197,7 +195,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None): create = int(create) if not create: - U = do_ue_list(args={"ue_id": ue_id}) + U = ue_list(args={"ue_id": ue_id}) if not U: raise ScoValueError("UE inexistante !") U = U[0] @@ -371,7 +369,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None): do_ue_edit(tf[2]) return flask.redirect( url_for( - "notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=formation_id + "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id ) ) @@ -393,7 +391,7 @@ def next_ue_numero(formation_id, semestre_id=None): """Numero d'une nouvelle UE dans cette formation. Si le semestre est specifie, cherche les UE ayant des modules de ce semestre """ - ue_list = do_ue_list(args={"formation_id": formation_id}) + ue_list = ue_list(args={"formation_id": formation_id}) if not ue_list: return 0 if semestre_id is None: @@ -410,7 +408,7 @@ def next_ue_numero(formation_id, semestre_id=None): def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False): """Delete an UE""" - ue = do_ue_list(args={"ue_id": ue_id}) + ue = ue_list(args={"ue_id": ue_id}) if not ue: raise ScoValueError("UE inexistante !") ue = ue[0] @@ -426,9 +424,9 @@ def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False): return do_ue_delete(ue_id, delete_validations=delete_validations) -def ue_list(formation_id=None, msg=""): +def ue_table(formation_id=None, msg=""): # was ue_list """Liste des matières et modules d'une formation, avec liens pour - editer (si non verrouillée). + éditer (si non verrouillée). """ from app.scodoc import sco_formations from app.scodoc import sco_formsemestre_validation @@ -440,7 +438,7 @@ def ue_list(formation_id=None, msg=""): parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"]) locked = sco_formations.formation_has_locked_sems(formation_id) - ue_list = do_ue_list(args={"formation_id": formation_id}) + ue_list = ue_list(args={"formation_id": formation_id}) # tri par semestre et numero: _add_ue_semestre_id(ue_list) ue_list.sort(key=lambda u: (u["semestre_id"], u["numero"])) @@ -627,7 +625,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours); H.append('[verrouillé]') if not parcours.UE_IS_MODULE: H.append('
        ') - Matlist = sco_edit_matiere.do_matiere_list(args={"ue_id": UE["ue_id"]}) + Matlist = sco_edit_matiere.matiere_list(args={"ue_id": UE["ue_id"]}) for Mat in Matlist: if not parcours.UE_IS_MODULE: H.append('
      • ') @@ -847,7 +845,7 @@ def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None): ue_code = str(ue_code) if ue_id: - ue = do_ue_list(args={"ue_id": ue_id})[0] + ue = ue_list(args={"ue_id": ue_id})[0] if not ue_code: ue_code = ue["ue_code"] F = sco_formations.formation_list(args={"formation_id": ue["formation_id"]})[0] @@ -884,7 +882,7 @@ def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None): for ue in ues: H.append( f"""
      • {ue.acronyme} ({ue.titre}) dans {ue.formation.acronyme} ({ue.formation.titre}), version {ue.formation.version}
      • """ @@ -897,13 +895,13 @@ def do_ue_edit(args, bypass_lock=False, dont_invalidate_cache=False): "edit an UE" # check ue_id = args["ue_id"] - ue = do_ue_list({"ue_id": ue_id})[0] + ue = ue_list({"ue_id": ue_id})[0] if (not bypass_lock) and ue_is_locked(ue["ue_id"]): raise ScoLockedFormError() # check: acronyme unique dans cette formation if "acronyme" in args: new_acro = args["acronyme"] - ues = do_ue_list({"formation_id": ue["formation_id"], "acronyme": new_acro}) + ues = ue_list({"formation_id": ue["formation_id"], "acronyme": new_acro}) if ues and ues[0]["ue_id"] != ue_id: raise ScoValueError('Acronyme d\'UE "%s" déjà utilisé !' % args["acronyme"]) @@ -926,7 +924,7 @@ def edit_ue_set_code_apogee(id=None, value=None): value = value.strip("-_ \t") log("edit_ue_set_code_apogee: ue_id=%s code_apogee=%s" % (ue_id, value)) - ues = do_ue_list(args={"ue_id": ue_id}) + ues = ue_list(args={"ue_id": ue_id}) if not ues: return "ue invalide" @@ -966,9 +964,9 @@ def formation_table_recap(formation_id, format="html"): raise ScoValueError("invalid formation_id") F = F[0] T = [] - ue_list = do_ue_list(args={"formation_id": formation_id}) + ue_list = ue_list(args={"formation_id": formation_id}) for UE in ue_list: - Matlist = sco_edit_matiere.do_matiere_list(args={"ue_id": UE["ue_id"]}) + Matlist = sco_edit_matiere.matiere_list(args={"ue_id": UE["ue_id"]}) for Mat in Matlist: Modlist = sco_edit_module.module_list( args={"matiere_id": Mat["matiere_id"]} diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py index 110fe0bed..17ab9b938 100644 --- a/app/scodoc/sco_evaluations.py +++ b/app/scodoc/sco_evaluations.py @@ -1062,7 +1062,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True): ] if Mod["module_type"] == scu.MODULE_MALUS: # Indique l'UE - ue = sco_edit_ue.do_ue_list(args={"ue_id": Mod["ue_id"]})[0] + ue = sco_edit_ue.ue_list(args={"ue_id": Mod["ue_id"]})[0] H.append("

        UE : %(acronyme)s

        " % ue) # store min/max values used by JS client-side checks: H.append( diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py index 6781b1dcd..a320afe57 100644 --- a/app/scodoc/sco_formations.py +++ b/app/scodoc/sco_formations.py @@ -98,7 +98,7 @@ def formation_export(formation_id, export_ids=False, export_tags=True, format=No in desired format """ F = formation_list(args={"formation_id": formation_id})[0] - ues = sco_edit_ue.do_ue_list({"formation_id": formation_id}) + ues = sco_edit_ue.ue_list({"formation_id": formation_id}) F["ue"] = ues for ue in ues: ue_id = ue["ue_id"] @@ -107,7 +107,7 @@ def formation_export(formation_id, export_ids=False, export_tags=True, format=No del ue["formation_id"] if ue["ects"] is None: del ue["ects"] - mats = sco_edit_matiere.do_matiere_list({"ue_id": ue_id}) + mats = sco_edit_matiere.matiere_list({"ue_id": ue_id}) ue["matiere"] = mats for mat in mats: matiere_id = mat["matiere_id"] @@ -366,7 +366,7 @@ def formation_create_new_version(formation_id, redirect=True): if redirect: return flask.redirect( url_for( - "notes.ue_list", + "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=new_id, msg="Nouvelle version !", diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 8d13cf349..416e531e0 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -205,9 +205,9 @@ def do_formsemestre_createwithmodules(edit=False): # on pourrait faire un simple module_list( ) # mais si on veut l'ordre du PPN (groupe par UE et matieres) il faut: mods = [] # liste de dicts - uelist = sco_edit_ue.do_ue_list({"formation_id": formation_id}) + uelist = sco_edit_ue.ue_list({"formation_id": formation_id}) for ue in uelist: - matlist = sco_edit_matiere.do_matiere_list({"ue_id": ue["ue_id"]}) + matlist = sco_edit_matiere.matiere_list({"ue_id": ue["ue_id"]}) for mat in matlist: modsmat = sco_edit_module.module_list({"matiere_id": mat["matiere_id"]}) # XXX debug checks diff --git a/app/scodoc/sco_formsemestre_exterieurs.py b/app/scodoc/sco_formsemestre_exterieurs.py index 23cea7a44..412e06d4e 100644 --- a/app/scodoc/sco_formsemestre_exterieurs.py +++ b/app/scodoc/sco_formsemestre_exterieurs.py @@ -439,7 +439,7 @@ def _list_ue_with_coef_and_validations(sem, etudid): """ cnx = ndb.GetDBConnexion() formsemestre_id = sem["formsemestre_id"] - ue_list = sco_edit_ue.do_ue_list({"formation_id": sem["formation_id"]}) + ue_list = sco_edit_ue.ue_list({"formation_id": sem["formation_id"]}) for ue in ue_list: # add coefficient uecoef = sco_formsemestre.formsemestre_uecoef_list( diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index d2b8609c4..26aab1605 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -141,7 +141,7 @@ def formsemestre_status_menubar(sem): }, { "title": "Voir la formation %(acronyme)s (v%(version)s)" % F, - "endpoint": "notes.ue_list", + "endpoint": "notes.ue_table", "args": {"formation_id": sem["formation_id"]}, "enabled": True, "helpmsg": "Tableau de bord du semestre", @@ -896,7 +896,7 @@ def html_expr_diagnostic(diagnostics): ) else: if diag["ue_id"] != last_id or diag["msg"] != last_msg: - ue = sco_edit_ue.do_ue_list({"ue_id": diag["ue_id"]})[0] + ue = sco_edit_ue.ue_list({"ue_id": diag["ue_id"]})[0] H.append( '
      • UE "%s": %s
      • ' % (ue["acronyme"] or ue["titre"] or "?", diag["msg"]) @@ -926,7 +926,7 @@ def formsemestre_status_head(formsemestre_id=None, page_title=None): ), f"""
        Formation: - {F['titre']}""", ] if sem["semestre_id"] >= 0: diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py index 5319ee2a4..310173fd3 100644 --- a/app/scodoc/sco_formsemestre_validation.py +++ b/app/scodoc/sco_formsemestre_validation.py @@ -1022,7 +1022,7 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid): ] # Toutes les UE de cette formation sont présentées (même celles des autres semestres) - ues = sco_edit_ue.do_ue_list({"formation_id": Fo["formation_id"]}) + ues = sco_edit_ue.ue_list({"formation_id": Fo["formation_id"]}) ue_names = ["Choisir..."] + ["%(acronyme)s %(titre)s" % ue for ue in ues] ue_ids = [""] + [ue["ue_id"] for ue in ues] tf = TrivialFormulator( @@ -1234,7 +1234,7 @@ def check_formation_ues(formation_id): définition du programme: cette fonction retourne un bout de HTML à afficher pour prévenir l'utilisateur, ou '' si tout est ok. """ - ues = sco_edit_ue.do_ue_list({"formation_id": formation_id}) + ues = sco_edit_ue.ue_list({"formation_id": formation_id}) ue_multiples = {} # { ue_id : [ liste des formsemestre ] } for ue in ues: # formsemestres utilisant cette ue ? diff --git a/app/scodoc/sco_moduleimpl.py b/app/scodoc/sco_moduleimpl.py index b18e9b5c9..739061b12 100644 --- a/app/scodoc/sco_moduleimpl.py +++ b/app/scodoc/sco_moduleimpl.py @@ -154,11 +154,11 @@ def moduleimpl_withmodule_list( mi["module"] = modules[module_id] ue_id = mi["module"]["ue_id"] if not ue_id in ues: - ues[ue_id] = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] + ues[ue_id] = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0] mi["ue"] = ues[ue_id] matiere_id = mi["module"]["matiere_id"] if not matiere_id in matieres: - matieres[matiere_id] = sco_edit_matiere.do_matiere_list( + matieres[matiere_id] = sco_edit_matiere.matiere_list( args={"matiere_id": matiere_id} )[0] mi["matiere"] = matieres[matiere_id] diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index 41ad53fad..5efe51f03 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -339,7 +339,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id): UECaps = get_etuds_with_capitalized_ue(formsemestre_id) if UECaps: H.append('

        Etudiants avec UEs capitalisées:

          ') - ues = [sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] for ue_id in UECaps.keys()] + ues = [sco_edit_ue.ue_list({"ue_id": ue_id})[0] for ue_id in UECaps.keys()] ues.sort(key=lambda u: u["numero"]) for ue in ues: H.append( diff --git a/app/scodoc/sco_pvjury.py b/app/scodoc/sco_pvjury.py index 2f06dbce6..322486d3f 100644 --- a/app/scodoc/sco_pvjury.py +++ b/app/scodoc/sco_pvjury.py @@ -92,7 +92,7 @@ def _descr_decisions_ues(nt, etudid, decisions_ue, decision_sem): and sco_codes_parcours.code_semestre_validant(decision_sem["code"]) ) ): - ue = sco_edit_ue.do_ue_list(args={"ue_id": ue_id})[0] + ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0] uelist.append(ue) except: log("descr_decisions_ues: ue_id=%s decisions_ue=%s" % (ue_id, decisions_ue)) diff --git a/app/scodoc/sco_ue_external.py b/app/scodoc/sco_ue_external.py index 25ab8aebb..b06e187e1 100644 --- a/app/scodoc/sco_ue_external.py +++ b/app/scodoc/sco_ue_external.py @@ -174,9 +174,7 @@ def external_ue_inscrit_et_note(moduleimpl_id, formsemestre_id, notes_etuds): def get_existing_external_ue(formation_id): "la liste de toutes les UE externes définies dans cette formation" - return sco_edit_ue.do_ue_list( - args={"formation_id": formation_id, "is_external": True} - ) + return sco_edit_ue.ue_list(args={"formation_id": formation_id, "is_external": True}) def get_external_moduleimpl_id(formsemestre_id, ue_id): diff --git a/app/views/notes.py b/app/views/notes.py index 0b089e3c5..b3ba8c599 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -160,7 +160,7 @@ def sco_publish(route, function, permission, methods=["GET"]): #

          Il faut d'abord supprimer le semestre. Mais il est peut être préférable de # laisser ce programme intact et d'en créer une nouvelle version pour la modifier. #

          -# reprendre +# reprendre # """ # raise ScoGenError(err_page) # # raise ScoGenError("une erreur banale") @@ -334,7 +334,7 @@ sco_publish( Permission.ScoChangeFormation, methods=["GET", "POST"], ) -sco_publish("/ue_list", sco_edit_ue.ue_list, Permission.ScoView) +sco_publish("/ue_list", sco_edit_ue.ue_table, Permission.ScoView) sco_publish("/ue_sharing_code", sco_edit_ue.ue_sharing_code, Permission.ScoView) sco_publish( "/edit_ue_set_code_apogee", @@ -548,8 +548,8 @@ sco_publish( ) sco_publish( - "/do_ue_list", - sco_edit_ue.do_ue_list, + "/ue_list", + sco_edit_ue.ue_list, Permission.ScoView, ) @@ -1142,7 +1142,7 @@ def edit_ue_expr(formsemestre_id, ue_id): raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération") cnx = ndb.GetDBConnexion() # - ue = sco_edit_ue.do_ue_list({"ue_id": ue_id})[0] + ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0] H = [ html_sco_header.html_sem_header( "Modification règle de calcul de l'UE %s (%s)" @@ -2431,7 +2431,7 @@ def check_sem_integrity(formsemestre_id, fix=False): for modimpl in modimpls: mod = sco_edit_module.module_list({"module_id": modimpl["module_id"]})[0] formations_set.add(mod["formation_id"]) - ue = sco_edit_ue.do_ue_list({"ue_id": mod["ue_id"]})[0] + ue = sco_edit_ue.ue_list({"ue_id": mod["ue_id"]})[0] formations_set.add(ue["formation_id"]) if ue["formation_id"] != mod["formation_id"]: modimpl["mod"] = mod @@ -2490,10 +2490,10 @@ def check_sem_integrity(formsemestre_id, fix=False): def check_form_integrity(formation_id, fix=False): "debug" log("check_form_integrity: formation_id=%s fix=%s" % (formation_id, fix)) - ues = sco_edit_ue.do_ue_list(args={"formation_id": formation_id}) + ues = sco_edit_ue.ue_list(args={"formation_id": formation_id}) bad = [] for ue in ues: - mats = sco_edit_matiere.do_matiere_list(args={"ue_id": ue["ue_id"]}) + mats = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]}) for mat in mats: mods = sco_edit_module.module_list({"matiere_id": mat["matiere_id"]}) for mod in mods: diff --git a/tests/unit/sco_fake_gen.py b/tests/unit/sco_fake_gen.py index f761403eb..8b98e35df 100644 --- a/tests/unit/sco_fake_gen.py +++ b/tests/unit/sco_fake_gen.py @@ -170,7 +170,7 @@ class ScoFake(object): if numero is None: numero = sco_edit_ue.next_ue_numero(formation_id, 0) oid = sco_edit_ue.do_ue_create(locals()) - oids = sco_edit_ue.do_ue_list(args={"ue_id": oid}) + oids = sco_edit_ue.ue_list(args={"ue_id": oid}) if not oids: raise ScoValueError("ue not created !") return oids[0] @@ -178,7 +178,7 @@ class ScoFake(object): @logging_meth def create_matiere(self, ue_id=None, titre=None, numero=None): oid = sco_edit_matiere.do_matiere_create(locals()) - oids = sco_edit_matiere.do_matiere_list(args={"matiere_id": oid}) + oids = sco_edit_matiere.matiere_list(args={"matiere_id": oid}) if not oids: raise ScoValueError("matiere not created !") return oids[0] diff --git a/tests/unit/test_formations.py b/tests/unit/test_formations.py index 534638471..3d3cdbc50 100644 --- a/tests/unit/test_formations.py +++ b/tests/unit/test_formations.py @@ -35,9 +35,9 @@ # - do_formsemestre_delete # - module_list # - do_module_delete -# - do_matiere_list +# - matiere_list # - do_matiere_delete -# - do_ue_list +# - ue_list # - do_ue_delete # - do_formation_delete @@ -303,16 +303,16 @@ def test_formations(test_client): assert len(lim_sem2) == 0 # deuxieme vérification si le module s'est bien sup - li_mat = sco_edit_matiere.do_matiere_list() + li_mat = sco_edit_matiere.matiere_list() assert len(li_mat) == 4 sco_edit_matiere.do_matiere_delete(oid=matt["matiere_id"]) # on supprime la matiere - li_mat2 = sco_edit_matiere.do_matiere_list() + li_mat2 = sco_edit_matiere.matiere_list() assert len(li_mat2) == 3 # verification de la suppression de la matiere - li_ue = sco_edit_ue.do_ue_list() + li_ue = sco_edit_ue.ue_list() assert len(li_ue) == 4 sco_edit_ue.ue_delete(ue_id=uet["ue_id"], dialog_confirmed=True) - li_ue2 = sco_edit_ue.do_ue_list() + li_ue2 = sco_edit_ue.ue_list() assert len(li_ue2) == 3 # verification de la suppression de l'UE # --- Suppression d'une formation