From 7589d4cc343ec98330493da1d2107671cabb5be1 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 1 Nov 2021 16:59:56 +0100 Subject: [PATCH] API ScoDoc 7: autorise POSTs, ajoute groups_view, script exemple/test --- app/decorators.py | 14 +++++-- app/views/absences.py | 20 +++++----- app/views/scolar.py | 29 +++++++++++++- tests/api/exemple-api-scodoc7.py | 67 ++++++++++++++++++++++++++++---- 4 files changed, 109 insertions(+), 21 deletions(-) diff --git a/app/decorators.py b/app/decorators.py index ce94743e5..8ebf5deab 100644 --- a/app/decorators.py +++ b/app/decorators.py @@ -50,9 +50,15 @@ def scodoc(func): @wraps(func) def scodoc_function(*args, **kwargs): - # current_app.logger.info("@scodoc") + # print("@scodoc") # interdit les POST si pas loggué - if request.method == "POST" and not current_user.is_authenticated: + if ( + request.method == "POST" + and not current_user.is_authenticated + and not request.form.get( + "__ac_password" + ) # exception pour compat API ScoDoc7 + ): current_app.logger.info( "POST by non authenticated user (request.form=%s)", str(request.form)[:2048], @@ -103,7 +109,7 @@ def permission_required_compat_scodoc7(permission): @wraps(f) def decorated_function(*args, **kwargs): # cherche les paramètre d'auth: - # current_app.logger.info("@permission_required_compat_scodoc7") + # print("@permission_required_compat_scodoc7") auth_ok = False if request.method == "GET": user_name = request.args.get("__ac_name") @@ -154,7 +160,7 @@ def scodoc7func(func): 2. or be called directly from Python. """ - # current_app.logger.info("@scodoc7func") + # print("@scodoc7func") # Détermine si on est appelé via une route ("toplevel") # ou par un appel de fonction python normal. top_level = not hasattr(g, "scodoc7_decorated") diff --git a/app/views/absences.py b/app/views/absences.py index f4cb15969..9fb8f5c8e 100644 --- a/app/views/absences.py +++ b/app/views/absences.py @@ -1058,7 +1058,8 @@ def AddBilletAbsence( code_nip=None, code_ine=None, justified=True, - xml_reply=True, + format="json", + xml_reply=True, # deprecated ): """Mémorise un "billet" begin et end sont au format ISO (eg "1999-01-08 04:05:06") @@ -1082,6 +1083,7 @@ def AddBilletAbsence( raise ValueError("invalid dates") # justified = bool(justified) + xml_reply = bool(xml_reply) # cnx = ndb.GetDBConnexion() billet_id = sco_abs.billet_absence_create( @@ -1095,14 +1097,14 @@ def AddBilletAbsence( "justified": justified, }, ) - if xml_reply: - # Renvoie le nouveau billet en XML - billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id}) - tab = _tableBillets(billets, etud=etud) - log("AddBilletAbsence: new billet_id=%s (%gs)" % (billet_id, time.time() - t0)) - return tab.make_page(format="xml") - else: - return billet_id + if xml_reply: # backward compat + format = "xml" + + # Renvoie le nouveau billet au format demandé + billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id}) + tab = _tableBillets(billets, etud=etud) + log("AddBilletAbsence: new billet_id=%s (%gs)" % (billet_id, time.time() - t0)) + return tab.make_page(format=format) @bp.route("/AddBilletAbsenceForm", methods=["GET", "POST"]) diff --git a/app/views/scolar.py b/app/views/scolar.py index 38f63f063..a3a913032 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -302,7 +302,34 @@ sco_publish( methods=["GET", "POST"], ) -sco_publish("/groups_view", sco_groups_view.groups_view, Permission.ScoView) + +@bp.route("/groups_view") +@scodoc +@permission_required_compat_scodoc7(Permission.ScoView) +@scodoc7func +def groups_view( + group_ids=(), + format="html", + # Options pour listes: + with_codes=0, + etat=None, + with_paiement=0, # si vrai, ajoute colonnes infos paiement droits et finalisation inscription (lent car interrogation portail) + with_archives=0, # ajoute colonne avec noms fichiers archivés + with_annotations=0, + formsemestre_id=None, +): + return sco_groups_view.groups_view( + group_ids=(), + format=format, + # Options pour listes: + with_codes=with_codes, + etat=etat, + with_paiement=with_paiement, # si vrai, ajoute colonnes infos paiement droits et finalisation inscription (lent car interrogation portail) + with_archives=with_archives, # ajoute colonne avec noms fichiers archivés + with_annotations=with_annotations, + formsemestre_id=formsemestre_id, + ) + sco_publish( "/export_groups_as_moodle_csv", diff --git a/tests/api/exemple-api-scodoc7.py b/tests/api/exemple-api-scodoc7.py index c3190717b..5dcbdf051 100644 --- a/tests/api/exemple-api-scodoc7.py +++ b/tests/api/exemple-api-scodoc7.py @@ -6,7 +6,7 @@ à la mode "PHP": les gens passaient directement __ac_name et __ac_password dans chaque requête, en POST ou en GET. -Cela n'a jamais été documenté mais était implitement supporté. C'est "deprecated" +Cela n'a jamais été documenté mais était implicitement supporté. C'est "deprecated" et ne sera plus supporté à partir de juillet 2022. Ce script va tester: @@ -27,6 +27,7 @@ export CHECK_CERTIFICATE=0 # ou 1 si serveur de production avec certif SSL valid """ from dotenv import load_dotenv +import json import os import pdb import requests @@ -66,12 +67,10 @@ def GET(path: str, params=None, errmsg=None): def POST(path: str, data: dict, errmsg=None): """Post""" - data["__ac_name"] = SCODOC_USER - data["__ac_password"] = SCODOC_PASSWORD + data["__ac_name"] = data.get("__ac_name", SCODOC_USER) + data["__ac_password"] = data.get("__ac_password", SCODOC_PASSWORD) r = requests.post(DEPT_URL + "/" + path, data=data, verify=CHECK_CERTIFICATE) - if r.status_code != 200: - raise ScoError(errmsg or "erreur !") - return r.text + return r # --- @@ -91,7 +90,61 @@ if sem["etat"] == "0": # Affiche le semestre trouvé: pp(sem) -# Les fonctions ci-dessous ne fonctionne plus en ScoDoc 9 +# Liste des étudiants dans le 1er semestre non verrouillé: +group_list = GET( + "groups_view", + params={ + "formsemestre_id": sem["formsemestre_id"], + "with_codes": 1, + "format": "json", + }, +) +if not group_list: + # config inadaptée pour les tests... + raise ScoError("aucun étudiant inscrit dans le semestre") + +etud = group_list[0] # le premier étudiant inscrit ici +# test un POST +r = POST( + "Absences/AddBilletAbsence", + { + "begin": "2021-10-25", + "end": "2021-10-26", + "description": "test API scodoc7", + "etudid": etud["etudid"], + }, +) +assert r.status_code == 200 +assert r.text.startswith('') +assert "billet_id" in r.text +# Essai avec un compte invalide +r_invalid = POST( + "Absences/AddBilletAbsence", + { + "__ac_name": "xxx", + "begin": "2021-10-25", + "end": "2021-10-26", + "description": "test API scodoc7", + "etudid": etud["etudid"], + }, +) +assert r_invalid.status_code == 403 # compte invalide => not authorized + +# AddBilletAbsence en json +r = POST( + "Absences/AddBilletAbsence", + { + "begin": "2021-10-25", + "end": "2021-10-26", + "description": "test API scodoc7", + "etudid": etud["etudid"], + "xml_reply": 0, + }, +) +assert r.status_code == 200 +assert isinstance(json.loads(r.text)[0]["billet_id"], int) + +# Les fonctions ci-dessous ne fonctionnent plus en ScoDoc 9 # Voir https://scodoc.org/git/viennet/ScoDoc/issues/149 # # ---- Liste les modules et prend le premier