From 068d2a659e3590f77a405b878173ded0e53f1d18 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 10 Aug 2021 12:57:38 +0200 Subject: [PATCH] pass unit test_sco_basic --- app/__init__.py | 14 ++++++++++ app/models/events.py | 2 ++ app/scodoc/sco_abs.py | 4 +-- app/scodoc/sco_dept.py | 2 +- app/scodoc/sco_edit_ue.py | 2 +- app/scodoc/sco_etud.py | 1 - app/scodoc/sco_evaluations.py | 15 +++++++---- app/scodoc/sco_formsemestre.py | 14 +++++----- app/scodoc/sco_formsemestre_edit.py | 8 +++--- app/scodoc/sco_formsemestre_inscriptions.py | 6 ++--- app/scodoc/sco_formsemestre_status.py | 16 +++++------ app/scodoc/sco_formsemestre_validation.py | 4 +-- app/scodoc/sco_groups.py | 15 ++++++----- app/scodoc/sco_inscr_passage.py | 2 +- app/scodoc/sco_moduleimpl.py | 16 ++++++++--- app/scodoc/sco_moduleimpl_inscriptions.py | 6 ++--- app/scodoc/sco_moduleimpl_status.py | 8 +++--- app/scodoc/sco_page_etud.py | 2 +- app/scodoc/sco_parcours_dut.py | 30 +++++++++++---------- app/scodoc/sco_permissions_check.py | 4 +-- app/scodoc/sco_saisie_notes.py | 13 +++++---- app/scodoc/sco_synchro_etuds.py | 2 +- app/scodoc/sco_users.py | 13 ++++++--- app/scodoc/sco_utils.py | 9 ++++--- app/scodoc/scolog.py | 5 ++-- app/views/notes.py | 2 +- app/views/scolar.py | 10 +++---- config.py | 1 + misc/example-api-1.py | 6 +++-- scodoc.py | 10 +++---- tests/conftest.py | 8 +++--- tests/scenarios/__init__.py | 1 + tests/scenarios/test_scenario1_formation.py | 5 +++- tests/unit/sco_fake_gen.py | 8 +++--- tests/unit/test_caches.py | 11 +++++--- tests/unit/test_sco_basic.py | 20 +++++++++----- 36 files changed, 177 insertions(+), 118 deletions(-) create mode 100644 tests/scenarios/__init__.py diff --git a/app/__init__.py b/app/__init__.py index 07160ecd23..41d2b38f7d 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -187,6 +187,8 @@ def initialize_scodoc_database(erase=False): user_db_init() # - Insert some constant values (modalites, ...) sco_db_init() + # - Flush cache + clear_scodoc_cache() def truncate_database(): @@ -202,6 +204,18 @@ def truncate_database(): raise +def clear_scodoc_cache(): + """Clear ScoDoc cache + This cache (currently Redis) is persistent between invocation + and it may be necessary to clear it during developement or tests. + """ + # attaque directement redis, court-circuite ScoDoc: + import redis + + r = redis.Redis() + r.flushall() + + # admin_role = Role.query.filter_by(name="SuperAdmin").first() # if admin_role: # admin = ( diff --git a/app/models/events.py b/app/models/events.py index cd95e492e4..b2c9ca7943 100644 --- a/app/models/events.py +++ b/app/models/events.py @@ -16,6 +16,8 @@ class Scolog(db.Model): id = db.Column(db.Integer, primary_key=True) date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) + method = db.Column(db.Text) + msg = db.Column(db.Text) etudid = db.Column(db.Integer) # sans contrainte pour garder logs après suppression authenticated_user = db.Column(db.Text) # login, sans contrainte # zope_remote_addr suppressed diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py index 8e866285ea..0e137710e7 100644 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -1028,7 +1028,7 @@ def get_abs_count(etudid, sem): """ date_debut = sem["date_debut_iso"] date_fin = sem["date_fin_iso"] - key = etudid + "_" + date_debut + "_" + date_fin + key = str(etudid) + "_" + date_debut + "_" + date_fin r = sco_cache.AbsSemEtudCache.get(key) if not r: nb_abs = count_abs( # was CountAbs XXX @@ -1052,7 +1052,7 @@ def invalidate_abs_count(etudid, sem): """Invalidate (clear) cached counts""" date_debut = sem["date_debut_iso"] date_fin = sem["date_fin_iso"] - key = etudid + "_" + date_debut + "_" + date_fin + key = str(etudid) + "_" + date_debut + "_" + date_fin sco_cache.AbsSemEtudCache.delete(key) diff --git a/app/scodoc/sco_dept.py b/app/scodoc/sco_dept.py index e723139bca..5c377f9f04 100644 --- a/app/scodoc/sco_dept.py +++ b/app/scodoc/sco_dept.py @@ -67,7 +67,7 @@ def index_html(context, REQUEST=None, showcodes=0, showsemtable=0): lockicon = scu.icontag("lock32_img", title="verrouillé", border="0") # Sélection sur l'etat du semestre for sem in sems: - if sem["etat"] == "1" and sem["modalite"] != "EXT": + if sem["etat"] and sem["modalite"] != "EXT": sem["lockimg"] = "" cursems.append(sem) else: diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 13896270eb..5a0b4f0de1 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -805,7 +805,7 @@ Si vous souhaitez modifier cette formation (par exemple pour y ajouter un module '
  • %(titremois)s' % sem ) - if sem["etat"] != "1": + if not sem["etat"]: H.append(" [verrouillé]") else: H.append( diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index 88159d9ae4..6f48277dac 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -991,7 +991,6 @@ def fill_etuds_info(etuds): ) else: etud["telephonemobilestr"] = "" - etud["debouche"] = etud["debouche"] or "" def descr_situation_etud(context, etudid, ne=""): diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py index afa9281754..60cf0e648c 100644 --- a/app/scodoc/sco_evaluations.py +++ b/app/scodoc/sco_evaluations.py @@ -115,16 +115,16 @@ _evaluationEditor = ndb.EditableTable( sortkey="numero desc, jour desc, heure_debut desc", # plus recente d'abord output_formators={ "jour": ndb.DateISOtoDMY, - "visibulletin": str, - "publish_incomplete": str, + "visibulletin": bool, + "publish_incomplete": bool, "numero": ndb.int_null_is_zero, }, input_formators={ "jour": ndb.DateDMYtoISO, "heure_debut": ndb.TimetoISO8601, # converti par do_evaluation_list "heure_fin": ndb.TimetoISO8601, # converti par do_evaluation_list - "visibulletin": int, - "publish_incomplete": int, + "visibulletin": bool, + "publish_incomplete": bool, }, ) @@ -602,7 +602,12 @@ def do_evaluation_list_in_sem(formsemestre_id, with_etat=True): 'visibulletin': 1} ] """ - req = "select E.* from notes_evaluation E, notes_moduleimpl MI where MI.formsemestre_id = %(formsemestre_id)s and MI.moduleimpl_id = E.moduleimpl_id order by moduleimpl_id, numero desc, jour desc, heure_debut desc" + req = """SELECT E.id AS evaluation_id, E.* + FROM notes_evaluation E, notes_moduleimpl MI + WHERE MI.formsemestre_id = %(formsemestre_id)s + and MI.id = E.moduleimpl_id + ORDER BY MI.id, numero desc, jour desc, heure_debut DESC + """ cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute(req, {"formsemestre_id": formsemestre_id}) diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index 3e321b551e..96cce806eb 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -69,18 +69,18 @@ _formsemestreEditor = ndb.EditableTable( output_formators={ "date_debut": ndb.DateISOtoDMY, "date_fin": ndb.DateISOtoDMY, - "gestion_compensation": str, - "gestion_semestrielle": str, + "gestion_compensation": bool, + "gestion_semestrielle": bool, "etat": bool, - "bul_hide_xml": str, + "bul_hide_xml": bool, }, input_formators={ "date_debut": ndb.DateDMYtoISO, "date_fin": ndb.DateDMYtoISO, - "gestion_compensation": int, - "gestion_semestrielle": int, + "gestion_compensation": bool, + "gestion_semestrielle": bool, "etat": bool, - "bul_hide_xml": int, + "bul_hide_xml": bool, }, ) @@ -480,7 +480,7 @@ def scodoc_get_all_unlocked_sems(context): semdepts += [ (sem, dept.Scolarite.Notes) for sem in do_formsemestre_list(dept.Scolarite.Notes) - if sem["etat"] == "1" + if sem["etat"] ] return semdepts diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 39ca211a1c..b82f3ef185 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -99,7 +99,7 @@ def formsemestre_editwithmodules(context, REQUEST, formsemestre_id): bodyOnLoad="init_tf_form('')", ) ] - if sem["etat"] != "1": + if not sem["etat"]: H.append( """

    %sCe semestre est verrouillé.

    """ % scu.icontag("lock_img", border="0", title="Semestre verrouillé") @@ -1483,7 +1483,7 @@ def formsemestre_change_lock( if not ok: return err sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) - etat = 1 - int(sem["etat"]) + etat = not sem["etat"] if REQUEST and not dialog_confirmed: if etat: @@ -1500,11 +1500,9 @@ def formsemestre_change_lock( """, dest_url="", cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id, - parameters={"etat": etat, "formsemestre_id": formsemestre_id}, + parameters={"formsemestre_id": formsemestre_id}, ) - if etat not in (0, 1): - raise ScoValueError("formsemestre_lock: invalid value for etat (%s)" % etat) args = {"formsemestre_id": formsemestre_id, "etat": etat} sco_formsemestre.do_formsemestre_edit(context, args) if REQUEST: diff --git a/app/scodoc/sco_formsemestre_inscriptions.py b/app/scodoc/sco_formsemestre_inscriptions.py index 44a2e3e666..4ff2068e8d 100644 --- a/app/scodoc/sco_formsemestre_inscriptions.py +++ b/app/scodoc/sco_formsemestre_inscriptions.py @@ -87,7 +87,7 @@ def do_formsemestre_inscription_create(context, args, REQUEST, method=None): raise ScoValueError("code de semestre invalide: %s" % args["formsemestre_id"]) sem = sems[0] # check lock - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("inscription: semestre verrouille") # r = _formsemestre_inscriptionEditor.create(cnx, args) @@ -143,7 +143,7 @@ def do_formsemestre_desinscription(context, etudid, formsemestre_id, REQUEST=Non sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) # -- check lock - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("desinscription impossible: semestre verrouille") # -- Si decisions de jury, desinscription interdite @@ -444,7 +444,7 @@ def formsemestre_inscription_with_modules( def formsemestre_inscription_option(context, etudid, formsemestre_id, REQUEST=None): """Dialogue pour (dés)inscription à des modules optionnels.""" sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("Modification impossible: semestre verrouille") etud = sco_etud.get_etud_info(etudid=etudid, filled=1)[0] diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 9e2c0eb1c6..7d688087af 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -163,7 +163,7 @@ def formsemestre_status_menubar(context, sem): and sem["resp_can_edit"] ) ) - and (sem["etat"] == "1"), + and (sem["etat"]), "helpmsg": "Modifie le contenu du semestre (modules)", }, { @@ -177,7 +177,7 @@ def formsemestre_status_menubar(context, sem): and sem["resp_can_edit"] ) ) - and (sem["etat"] == "1"), + and (sem["etat"]), "helpmsg": "Préférences du semestre", }, { @@ -229,7 +229,7 @@ def formsemestre_status_menubar(context, sem): "endpoint": "notes.formsemestre_associate_new_version", "args": {"formsemestre_id": formsemestre_id}, "enabled": current_user.has_permission(Permission.ScoChangeFormation) - and (sem["etat"] == "1"), + and (sem["etat"]), "helpmsg": "", }, { @@ -264,7 +264,7 @@ def formsemestre_status_menubar(context, sem): "endpoint": "notes.formsemestre_inscr_passage", "args": {"formsemestre_id": formsemestre_id}, "enabled": current_user.has_permission(Permission.ScoEtudInscrit) - and (sem["etat"] == "1"), + and (sem["etat"]), }, { "title": "Synchroniser avec étape Apogée", @@ -272,21 +272,21 @@ def formsemestre_status_menubar(context, sem): "args": {"formsemestre_id": formsemestre_id}, "enabled": current_user.has_permission(Permission.ScoView) and sco_preferences.get_preference("portal_url") - and (sem["etat"] == "1"), + and (sem["etat"]), }, { "title": "Inscrire un étudiant", "endpoint": "notes.formsemestre_inscription_with_modules_etud", "args": {"formsemestre_id": formsemestre_id}, "enabled": current_user.has_permission(Permission.ScoEtudInscrit) - and (sem["etat"] == "1"), + and (sem["etat"]), }, { "title": "Importer des étudiants dans ce semestre (table Excel)", "endpoint": "scolar.form_students_import_excel", "args": {"formsemestre_id": formsemestre_id}, "enabled": current_user.has_permission(Permission.ScoEtudInscrit) - and (sem["etat"] == "1"), + and (sem["etat"]), }, { "title": "Import/export des données admission", @@ -539,7 +539,7 @@ def fill_formsemestre(sem): notes_url = scu.NotesURL() sem["notes_url"] = notes_url formsemestre_id = sem["formsemestre_id"] - if sem["etat"] != "1": + if not sem["etat"]: sem[ "locklink" ] = """%s""" % ( diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py index 8395b6bab7..842389079a 100644 --- a/app/scodoc/sco_formsemestre_validation.py +++ b/app/scodoc/sco_formsemestre_validation.py @@ -102,7 +102,7 @@ def formsemestre_validation_etud_form( etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] Se = sco_parcours_dut.SituationEtudParcours(context, etud, formsemestre_id) - if Se.sem["etat"] != "1": + if not Se.sem["etat"]: raise ScoValueError("validation: semestre verrouille") H = [ @@ -599,7 +599,7 @@ def formsemestre_recap_parcours_table( default_sem_info = '[sem. précédent]' else: default_sem_info = "" - if sem["etat"] != "1": # locked + if not sem["etat"]: # locked lockicon = scu.icontag("lock32_img", title="verrouillé", border="0") default_sem_info += lockicon if sem["formation_code"] != Se.formation["formation_code"]: diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index 24a12e0483..fbbb3c7d1d 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -453,10 +453,10 @@ def etud_add_group_infos(context, etud, sem, sep=" "): def get_etud_groups_in_partition(context, partition_id): """Returns { etudid : group }, with all students in this partition""" infos = ndb.SimpleDictFetch( - """SELECT gd.id as group_id, gd.*, etudid + """SELECT gd.id AS group_id, gd.*, etudid FROM group_descr gd, group_membership gm WHERE gd.partition_id = %(partition_id)s - AND gm.group_id = gd.group_id + AND gm.group_id = gd.id """, {"partition_id": partition_id}, ) @@ -670,7 +670,7 @@ def setGroups( log("groupsToCreate=%s" % groupsToCreate) log("groupsToDelete=%s" % groupsToDelete) sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) - if sem["etat"] != "1": + if not sem["etat"]: raise AccessDenied("Modification impossible: semestre verrouillé") groupsToDelete = [g for g in groupsToDelete.split(";") if g] @@ -1410,7 +1410,12 @@ def do_evaluation_listeetuds_groups( req = ( "SELECT distinct Im.etudid FROM " + ", ".join(fromtables) - + " WHERE Isem.etudid=Im.etudid and Im.moduleimpl_id=M.moduleimpl_id and Isem.formsemestre_id=M.formsemestre_id and E.moduleimpl_id=M.moduleimpl_id and E.evaluation_id = %(evaluation_id)s" + + """ WHERE Isem.etudid = Im.etudid + and Im.moduleimpl_id = M.id + and Isem.formsemestre_id = M.formsemestre_id + and E.moduleimpl_id = M.id + and E.id = %(evaluation_id)s + """ ) if not include_dems: req += " and Isem.etat='I'" @@ -1418,8 +1423,6 @@ def do_evaluation_listeetuds_groups( cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute(req, {"evaluation_id": evaluation_id}) - # log('listeetuds_groups: getallstudents=%s groups=%s' % (getallstudents,groups)) - # log('req=%s' % (req % { 'evaluation_id' : "'"+evaluation_id+"'" })) res = cursor.fetchall() return [x[0] for x in res] diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py index e5f6f87fca..c4b9ef0d86 100644 --- a/app/scodoc/sco_inscr_passage.py +++ b/app/scodoc/sco_inscr_passage.py @@ -285,7 +285,7 @@ def formsemestre_inscr_passage( inscrit_groupes = int(inscrit_groupes) sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) # -- check lock - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("opération impossible: semestre verrouille") header = html_sco_header.sco_header(page_title="Passage des étudiants") footer = html_sco_header.sco_footer() diff --git a/app/scodoc/sco_moduleimpl.py b/app/scodoc/sco_moduleimpl.py index 5e1ed9bb69..6c65cf0970 100644 --- a/app/scodoc/sco_moduleimpl.py +++ b/app/scodoc/sco_moduleimpl.py @@ -182,7 +182,14 @@ def do_moduleimpl_inscription_list( def do_moduleimpl_listeetuds(context, moduleimpl_id): "retourne liste des etudids inscrits a ce module" - req = "select distinct Im.etudid from notes_moduleimpl_inscription Im, notes_formsemestre_inscription Isem, notes_moduleimpl M where Isem.etudid=Im.etudid and Im.moduleimpl_id=M.moduleimpl_id and M.moduleimpl_id = %(moduleimpl_id)s" + req = """SELECT DISTINCT Im.etudid + FROM notes_moduleimpl_inscription Im, + notes_formsemestre_inscription Isem, + notes_moduleimpl M + WHERE Isem.etudid = Im.etudid + and Im.moduleimpl_id = M.id + and M.id = %(moduleimpl_id)s + """ cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute(req, {"moduleimpl_id": moduleimpl_id}) @@ -199,7 +206,8 @@ def do_moduleimpl_inscrit_tout_semestre(context, moduleimpl_id, formsemestre_id) (moduleimpl_id, etudid) SELECT %(moduleimpl_id)s, I.etudid FROM notes_formsemestre_inscription I - WHERE I.formsemestre_id=%(formsemestre_id)s""" + WHERE I.formsemestre_id=%(formsemestre_id)s + """ args = {"moduleimpl_id": moduleimpl_id, "formsemestre_id": formsemestre_id} cursor.execute(req, args) @@ -322,7 +330,7 @@ def can_change_module_resp(context, REQUEST, moduleimpl_id): M = do_moduleimpl_withmodule_list(context, moduleimpl_id=moduleimpl_id)[0] # -- check lock sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"]) - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("Modification impossible: semestre verrouille") # -- check access authuser = REQUEST.AUTHENTICATED_USER @@ -340,7 +348,7 @@ def can_change_ens(context, REQUEST, moduleimpl_id, raise_exc=True): M = do_moduleimpl_withmodule_list(context, moduleimpl_id=moduleimpl_id)[0] # -- check lock sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"]) - if sem["etat"] != "1": + if not sem["etat"]: if raise_exc: raise ScoValueError("Modification impossible: semestre verrouille") else: diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index f0a196a71e..8e873ee693 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -69,7 +69,7 @@ def moduleimpl_inscriptions_edit( mod = sco_edit_module.do_module_list(context, args={"module_id": M["module_id"]})[0] sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) # -- check lock - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("opération impossible: semestre verrouille") header = html_sco_header.sco_header( page_title="Inscription au module", @@ -261,9 +261,7 @@ def moduleimpl_inscriptions_stats(context, formsemestre_id, REQUEST=None): context, formsemestre_id ) - can_change = ( - authuser.has_permission(Permission.ScoEtudInscrit) and sem["etat"] == "1" - ) + can_change = authuser.has_permission(Permission.ScoEtudInscrit) and sem["etat"] # Liste des modules Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list( diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index bd4a8b1d51..6037a59faf 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -223,7 +223,7 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No H.append("""Semestre: %s""" % sem["semestre_id"]) else: H.append("""""") - if sem["etat"] != "1": + if not sem["etat"]: H.append(scu.icontag("lock32_img", title="verrouillé")) H.append( """Coef dans le semestre: %(coefficient)s""" @@ -345,7 +345,7 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No # -------- Tableau des evaluations top_table_links = "" - if sem["etat"] == "1": # non verrouillé + if sem["etat"]: # non verrouillé top_table_links = ( """Créer nouvelle évaluation Trier par date @@ -610,9 +610,9 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No H.append("""""") # - if caneditevals or sem["etat"] != "1": + if caneditevals or not sem["etat"]: H.append("""""") - if sem["etat"] != "1": + if not sem["etat"]: H.append("""%s semestre verrouillé""" % scu.icontag("lock32_img")) else: H.append(top_table_links) diff --git a/app/scodoc/sco_page_etud.py b/app/scodoc/sco_page_etud.py index af4ae14921..bc08c9f5fe 100644 --- a/app/scodoc/sco_page_etud.py +++ b/app/scodoc/sco_page_etud.py @@ -58,7 +58,7 @@ def _menuScolarite(context, authuser, sem, etudid): """HTML pour menu "scolarite" pour un etudiant dans un semestre. Le contenu du menu depend des droits de l'utilisateur et de l'état de l'étudiant. """ - locked = sem["etat"] != "1" + locked = not sem["etat"] if locked: lockicon = scu.icontag("lock32_img", title="verrouillé", border="0") return lockicon # no menu diff --git a/app/scodoc/sco_parcours_dut.py b/app/scodoc/sco_parcours_dut.py index 5d5e640b0d..63a6c8cfc1 100644 --- a/app/scodoc/sco_parcours_dut.py +++ b/app/scodoc/sco_parcours_dut.py @@ -779,8 +779,8 @@ _scolar_formsemestre_validation_editor = ndb.EditableTable( "semestre_id", "is_external", ), - output_formators={"event_date": ndb.DateISOtoDMY, "assidu": str}, - input_formators={"event_date": ndb.DateDMYtoISO, "assidu": int_or_null}, + output_formators={"event_date": ndb.DateISOtoDMY, "assidu": bool}, + input_formators={"event_date": ndb.DateDMYtoISO, "assidu": bool}, ) scolar_formsemestre_validation_create = _scolar_formsemestre_validation_editor.create @@ -949,7 +949,7 @@ def do_formsemestre_validate_ue( moy_ue=None, date=None, semestre_id=None, - is_external=0, + is_external=False, ): """Ajoute ou change validation UE""" args = { @@ -1003,10 +1003,12 @@ def etud_est_inscrit_ue(cnx, etudid, formsemestre_id, ue_id): """Vrai si l'étudiant est inscrit a au moins un module de cette UE dans ce semestre""" cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( - """select mi.* from notes_moduleimpl mi, notes_modules mo, notes_ue ue, notes_moduleimpl_inscription i - where i.etudid = %(etudid)s and i.moduleimpl_id=mi.moduleimpl_id + """SELECT mi.* + FROM notes_moduleimpl mi, notes_modules mo, notes_ue ue, notes_moduleimpl_inscription i + WHERE i.etudid = %(etudid)s + and i.moduleimpl_id=mi.id and mi.formsemestre_id = %(formsemestre_id)s - and mi.module_id = mo.module_id + and mi.module_id = mo.id and mo.ue_id = %(ue_id)s """, {"etudid": etudid, "formsemestre_id": formsemestre_id, "ue_id": ue_id}, @@ -1053,22 +1055,22 @@ def formsemestre_get_etud_capitalisation(context, sem, etudid): cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( - """select distinct SFV.*, ue.ue_code from notes_ue ue, notes_formations nf, notes_formations nf2, - scolar_formsemestre_validation SFV, notes_formsemestre sem + """select distinct SFV.*, ue.ue_code from notes_ue ue, notes_formations nf, + notes_formations nf2, scolar_formsemestre_validation SFV, notes_formsemestre sem - where ue.formation_id = nf.formation_id - and nf.formation_code = nf2.formation_code - and nf2.formation_id=%(formation_id)s + WHERE ue.formation_id = nf.id + and nf.formation_code = nf2.formation_code + and nf2.id=%(formation_id)s - and SFV.ue_id = ue.ue_id + and SFV.ue_id = ue.id and SFV.code = 'ADM' and SFV.etudid = %(etudid)s - and ( (sem.formsemestre_id = SFV.formsemestre_id + and ( (sem.id = SFV.formsemestre_id and sem.date_debut < %(date_debut)s and sem.semestre_id = %(semestre_id)s ) or ( - ((SFV.formsemestre_id is NULL) OR (SFV.is_external = 1)) -- les UE externes ou "anterieures" + ((SFV.formsemestre_id is NULL) OR (SFV.is_external)) -- les UE externes ou "anterieures" AND (SFV.semestre_id is NULL OR SFV.semestre_id=%(semestre_id)s) ) ) """, diff --git a/app/scodoc/sco_permissions_check.py b/app/scodoc/sco_permissions_check.py index 2647d34c72..96dc747cf8 100644 --- a/app/scodoc/sco_permissions_check.py +++ b/app/scodoc/sco_permissions_check.py @@ -28,7 +28,7 @@ def can_edit_notes(authuser, moduleimpl_id, allow_ens=True): uid = str(authuser) M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0] sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"]) - if sem["etat"] != "1": + if not sem["etat"]: return False # semestre verrouillé if sco_parcours_dut.formsemestre_has_decisions(context, sem["formsemestre_id"]): @@ -111,7 +111,7 @@ def can_validate_sem(formsemestre_id): context = None # XXX #context sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) - if sem["etat"] != "1": + if not sem["etat"]: return False # semestre verrouillé return is_chef_or_diretud(sem) diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index b6cb13208a..021fc9a566 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -429,7 +429,9 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False): return html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer() -def _notes_add(context, uid, evaluation_id, notes, comment=None, do_it=True): +def _notes_add( + context, user, evaluation_id: int, notes: list, comment=None, do_it=True +): """ Insert or update notes notes is a list of tuples (etudid,value) @@ -439,7 +441,6 @@ def _notes_add(context, uid, evaluation_id, notes, comment=None, do_it=True): - si la note existe deja avec valeur distincte, ajoute une entree au log (notes_notes_log) Return number of changed notes """ - uid = str(uid) now = psycopg2.Timestamp( *time.localtime()[:6] ) # datetime.datetime.now().isoformat() @@ -478,12 +479,14 @@ def _notes_add(context, uid, evaluation_id, notes, comment=None, do_it=True): "evaluation_id": evaluation_id, "value": value, "comment": comment, - "uid": uid, + "uid": user.id, "date": now, } ndb.quote_dict(aa) cursor.execute( - "insert into notes_notes (etudid,evaluation_id,value,comment,date,uid) values (%(etudid)s,%(evaluation_id)s,%(value)s,%(comment)s,%(date)s,%(uid)s)", + """INSERT INTO notes_notes + (etudid,evaluation_id,value,comment,date,uid) + VALUES (%(etudid)s,%(evaluation_id)s,%(value)s,%(comment)s,%(date)s,%(uid)s)""", aa, ) changed = True @@ -511,7 +514,7 @@ def _notes_add(context, uid, evaluation_id, notes, comment=None, do_it=True): "value": value, "date": now, "comment": comment, - "uid": uid, + "uid": user.id, } ndb.quote_dict(aa) if value != scu.NOTES_SUPPRESS: diff --git a/app/scodoc/sco_synchro_etuds.py b/app/scodoc/sco_synchro_etuds.py index a6c230a25a..f40e00f226 100644 --- a/app/scodoc/sco_synchro_etuds.py +++ b/app/scodoc/sco_synchro_etuds.py @@ -98,7 +98,7 @@ def formsemestre_synchro_etuds( submitted = False dialog_confirmed = False # -- check lock - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("opération impossible: semestre verrouille") if not sem["etapes"]: raise ScoValueError( diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index 939b73c6b5..f79c81cb47 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -244,11 +244,18 @@ def _user_list(user_name): def user_info(user_name=None, user=None): """Dict avec infos sur l'utilisateur (qui peut ne pas etre dans notre base). - Si user_name est specifie, interroge la BD. Sinon, user doit etre une instance + Si user_name est specifie (string ou id), interroge la BD. Sinon, user doit etre une instance de User. """ - if user_name: - info = _user_list(user_name) + if user_name is not None: + if isinstance(user_name, int): + u = User.query.filter_by(id=user_name).first() + else: + u = User.query.filter_by(user_name=user_name).first() + if u: + info = u.to_dict() + else: + info = None else: info = user.to_dict() user_name = user.user_name diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 855644371e..25c0b49674 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -318,9 +318,12 @@ BULLETINS_VERSIONS = ("short", "selectedevals", "long") # Support for ScoDoc7 compatibility def get_dept_id(): - if g.scodoc_dept in sco_mgr.get_dept_ids(): - return g.scodoc_dept - raise sco_exceptions.ScoInvalidDept("département invalide: %s" % g.scodoc_dept) + return g.scodoc_dept # en scodoc 8.1 #sco8 + + +# if g.scodoc_dept in sco_mgr.get_dept_ids(): +# return g.scodoc_dept +# raise sco_exceptions.ScoInvalidDept("département invalide: %s" % g.scodoc_dept) def get_db_cnx_string(scodoc_dept=None): diff --git a/app/scodoc/scolog.py b/app/scodoc/scolog.py index 84ec306360..3b659b66f3 100644 --- a/app/scodoc/scolog.py +++ b/app/scodoc/scolog.py @@ -40,7 +40,6 @@ def logdb(cnx=None, method=None, etudid=None, msg=None, commit=True): args = { "authenticated_user": current_user.user_name, - "remote_addr": request.remote_addr, } args.update({"method": method, "etudid": etudid, "msg": msg}) @@ -48,9 +47,9 @@ def logdb(cnx=None, method=None, etudid=None, msg=None, commit=True): cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( """INSERT INTO scolog - (authenticated_user,remote_addr,method,etudid,msg) + (authenticated_user,method,etudid,msg) VALUES - (%(authenticated_user)s,%(remote_addr)s,%(method)s,%(etudid)s,%(msg)s)""", + (%(authenticated_user)s,%(method)s,%(etudid)s,%(msg)s)""", args, ) if commit: diff --git a/app/views/notes.py b/app/views/notes.py index d29e9e9719..a3921fd824 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -1245,7 +1245,7 @@ def formsemestre_desinscription( """ sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) # -- check lock - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("desinscription impossible: semestre verrouille") # -- Si décisions de jury, désinscription interdite diff --git a/app/views/scolar.py b/app/views/scolar.py index c59b99c51f..db78538691 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -185,7 +185,7 @@ def formsemestre_edit_preferences(context, formsemestre_id, REQUEST): ok = ( authuser.has_permission(Permission.ScoImplement) or ((str(authuser) in sem["responsables"]) and sem["resp_can_edit"]) - ) and (sem["etat"] == "1") + ) and (sem["etat"]) if ok: return sco_preferences.SemPreferences(formsemestre_id=formsemestre_id).edit( REQUEST=REQUEST @@ -842,7 +842,7 @@ def _formDem_of_Def( "Formulaire démission ou défaillance Etudiant" etud = sco_etud.get_etud_info(etudid=etudid, filled=1, REQUEST=REQUEST)[0] sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("Modification impossible: semestre verrouille") etud["formsemestre_id"] = formsemestre_id @@ -927,7 +927,7 @@ def _do_dem_or_def_etud( cnx = ndb.GetDBConnexion() # check lock sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("Modification impossible: semestre verrouille") # ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( @@ -1015,7 +1015,7 @@ def _do_cancel_dem_or_def( "Annule une demission ou une défaillance" # check lock sem = sco_formsemestre.get_formsemestre(context, formsemestre_id) - if sem["etat"] != "1": + if not sem["etat"]: raise ScoValueError("Modification impossible: semestre verrouille") # verif info = sco_etud.get_etud_info(etudid, filled=True)[0] @@ -1712,7 +1712,7 @@ def form_students_import_excel(context, REQUEST, formsemestre_id=None): else: sem = None dest_url = scu.ScoURL() - if sem and sem["etat"] != "1": + if sem and not sem["etat"]: raise ScoValueError("Modification impossible: semestre verrouille") H = [ html_sco_header.sco_header(page_title="Import etudiants"), diff --git a/config.py b/config.py index 923b9e52b3..cf15053b57 100755 --- a/config.py +++ b/config.py @@ -62,3 +62,4 @@ class TestConfig(DevConfig): os.environ.get("SCODOC_TEST_DATABASE_URI") or "postgresql:///SCODOC_TEST" ) SERVER_NAME = "test.gr" + DEPT_TEST = "TEST_" # nom du département, ne pas l'utiliser pour un "vrai" diff --git a/misc/example-api-1.py b/misc/example-api-1.py index 06b070cff3..18b2b523e1 100644 --- a/misc/example-api-1.py +++ b/misc/example-api-1.py @@ -24,13 +24,15 @@ BASEURL = "https://scodoc.xxx.net/ScoDoc/RT/Scolarite" USER = "XXX" PASSWORD = "XXX" -# --- +# --- if not CHECK_CERTIFICATE: urllib3.disable_warnings() + class ScoError(Exception): pass + def GET(s, path, errmsg=None): """Get and returns as JSON""" r = s.get(BASEURL + "/" + path) @@ -58,7 +60,7 @@ sems = GET(s, "Notes/formsemestre_list?format=json", "Aucun semestre !") # sems est une liste de semestres (dictionnaires) for sem in sems: - if sem["etat"] == "1": + if sem["etat"]: break if sem["etat"] == "0": diff --git a/scodoc.py b/scodoc.py index 0276364107..48bfd3f22e 100755 --- a/scodoc.py +++ b/scodoc.py @@ -16,7 +16,9 @@ import sys import click import flask from flask.cli import with_appcontext -from app import create_app, cli, db, initialize_scodoc_database +from app import create_app, cli, db +from app import initialize_scodoc_database +from app import clear_scodoc_cache from app.auth.models import User, Role, UserRole from app import models @@ -199,9 +201,5 @@ def clear_cache(): # clear-cache This cache (currently Redis) is persistent between invocation and it may be necessary to clear it during developement or tests. """ - # attaque directement redis, court-circuite ScoDoc: - import redis - - r = redis.Redis() - r.flushall() + clear_scodoc_cache() click.echo("Redis caches flushed.") diff --git a/tests/conftest.py b/tests/conftest.py index 2b19624759..fe1e879964 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,8 @@ from flask import g from flask_login import login_user, logout_user, current_user from config import TestConfig -from app import db, create_app, initialize_scodoc_database +from app import db, create_app +from app import initialize_scodoc_database, clear_scodoc_cache from app import models from app.auth.models import User, Role, UserRole, Permission from app.scodoc import sco_bulletins_standard @@ -40,14 +41,15 @@ def test_client(): u = User(user_name="bach") if not "Admin" in {r.name for r in u.roles}: admin_role = Role.query.filter_by(name="Admin").first() - u.add_role(admin_role, "TEST00") + u.add_role(admin_role, TestConfig.DEPT_TEST) db.session.add(u) db.session.commit() - ndb.set_sco_dept("TEST") # set db connection + ndb.set_sco_dept(TestConfig.DEPT_TEST) # set db connection yield client # ndb.close_dept_connection() # Teardown: db.session.remove() + clear_scodoc_cache() # db.drop_all() # => laisse la base en état (l'efface au début) # utile pour les tests en cours de développement diff --git a/tests/scenarios/__init__.py b/tests/scenarios/__init__.py new file mode 100644 index 0000000000..792d600548 --- /dev/null +++ b/tests/scenarios/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/scenarios/test_scenario1_formation.py b/tests/scenarios/test_scenario1_formation.py index 8027be2164..d82c0e1090 100644 --- a/tests/scenarios/test_scenario1_formation.py +++ b/tests/scenarios/test_scenario1_formation.py @@ -22,6 +22,10 @@ context = None # #context def test_scenario1(test_client): """Applique "scenario 1""" + run_scenario1() + + +def run_scenario1(): G = sco_fake_gen.ScoFake(verbose=False) # Lecture fichier XML local: @@ -56,6 +60,5 @@ def test_scenario1(test_client): mi = G.create_moduleimpl( module_id=mod["module_id"], formsemestre_id=sems[mod["semestre_id"] - 1]["formsemestre_id"], - responsable_id="bach", ) mods_imp.append(mi) diff --git a/tests/unit/sco_fake_gen.py b/tests/unit/sco_fake_gen.py index 3cdde226ad..86cbd2db4a 100644 --- a/tests/unit/sco_fake_gen.py +++ b/tests/unit/sco_fake_gen.py @@ -307,11 +307,13 @@ class ScoFake(object): etud=None, note=None, comment=None, - uid: typing.Optional[int] = None, + user=None, # User instance ): + if user is None: + user = self.default_user return sco_saisie_notes._notes_add( context, - uid, + user, evaluation["evaluation_id"], [(etud["etudid"], note)], comment=comment, @@ -367,7 +369,7 @@ class ScoFake(object): date_fin="30/06/2020", nb_evaluations_per_module=1, titre=None, - responsables=["bach"], + responsables=None, # list of users ids modalite=None, ): """Création semestre, avec modules et évaluations.""" diff --git a/tests/unit/test_caches.py b/tests/unit/test_caches.py index a0598b5a7c..7b6f0542f0 100644 --- a/tests/unit/test_caches.py +++ b/tests/unit/test_caches.py @@ -16,17 +16,20 @@ from app.scodoc import sco_cache from app.scodoc import sco_evaluations from app.scodoc import sco_formsemestre from app.scodoc import notesdb as ndb +from config import TestConfig -DEPT = "RT" # ce département (BD) doit exister + +DEPT = TestConfig.DEPT_TEST context = None # #context def test_notes_table(test_client): - """Test construction et cache de NotesTable. - Attention: utilise une base (departement) existante. - """ + """Test construction et cache de NotesTable.""" ndb.set_sco_dept(DEPT) assert g.scodoc_dept == DEPT + # prépare le département avec quelques semestres: + run_scenario1() + # sems = sco_formsemestre.do_formsemestre_list(context) assert len(sems) sem = sems[0] diff --git a/tests/unit/test_sco_basic.py b/tests/unit/test_sco_basic.py index 75e3976fa8..8e0c405be5 100644 --- a/tests/unit/test_sco_basic.py +++ b/tests/unit/test_sco_basic.py @@ -15,9 +15,9 @@ import random from flask import g +from config import TestConfig from tests.unit import sco_fake_gen -from app import decorators from app.scodoc import notesdb as ndb from app.scodoc import sco_abs from app.scodoc import sco_abs_views @@ -30,6 +30,7 @@ from app.scodoc import sco_saisie_notes from app.scodoc import sco_utils as scu context = None # #context +DEPT = TestConfig.DEPT_TEST def test_sco_basic(test_client): @@ -37,9 +38,15 @@ def test_sco_basic(test_client): Création 10 étudiants, formation, semestre, inscription etudiant, creation 1 evaluation, saisie 10 notes. """ - ndb.set_sco_dept("TEST00") # ce département doit exister ! - G = sco_fake_gen.ScoFake(verbose=False) - G.verbose = True + ndb.set_sco_dept(DEPT) + run_sco_basic() + + +def run_sco_basic(verbose=False): + """Scénario de base: création formation, semestre, étudiants, notes, + décisions jury + """ + G = sco_fake_gen.ScoFake(verbose=verbose) # --- Création d'étudiants etuds = [G.create_etud(code_nip=None) for _ in range(10)] @@ -53,8 +60,8 @@ def test_sco_basic(test_client): code="TSM1", coefficient=1.0, titre="module test", - ue_id=ue["ue_id"], # faiblesse de l'API - formation_id=f["formation_id"], # faiblesse de l'API + ue_id=ue["ue_id"], + formation_id=f["formation_id"], ) # --- Mise place d'un semestre @@ -68,7 +75,6 @@ def test_sco_basic(test_client): mi = G.create_moduleimpl( module_id=mod["module_id"], formsemestre_id=sem["formsemestre_id"], - responsable_id="bach", ) # --- Inscription des étudiants