From c065abd0bb5ccc208b62c198dc081fad5c4e7908 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 8 Aug 2022 10:06:42 +0200 Subject: [PATCH] API: modif formsemestre/etudiant. TESTS unitaires OK sauf logos. --- app/api/absences.py | 6 +- app/api/etudiants.py | 20 ++-- app/api/formsemestres.py | 21 ++-- app/api/users.py | 4 +- tests/api/test_api_formsemestre.py | 108 ++++-------------- tests/api/test_api_jury.py | 39 +++---- tests/api/test_api_permissions.py | 13 ++- .../fakedatabase/create_test_api_database.py | 11 +- 8 files changed, 79 insertions(+), 143 deletions(-) diff --git a/app/api/absences.py b/app/api/absences.py index 39951078a..e098c196a 100644 --- a/app/api/absences.py +++ b/app/api/absences.py @@ -19,15 +19,13 @@ from app.scodoc import sco_abs from app.scodoc.sco_groups import get_group_members from app.scodoc.sco_permissions import Permission - +# TODO XXX revoir routes web API et calcul des droits @bp.route("/absences/etudid/", methods=["GET"]) @scodoc @permission_required(Permission.ScoView) def absences(etudid: int = None): """ - Retourne la liste des absences d'un étudiant donné - - etudid : l'etudid d'un étudiant + Liste des absences de cet étudiant Exemple de résultat: [ diff --git a/app/api/etudiants.py b/app/api/etudiants.py index de195b6b1..81e3cd32f 100644 --- a/app/api/etudiants.py +++ b/app/api/etudiants.py @@ -31,16 +31,16 @@ from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud from app.scodoc.sco_permissions import Permission # Un exemple: -@bp.route("/api_function/") -@api_web_bp.route("/api_function/") -@login_required -@scodoc -@permission_required(Permission.ScoView) -def api_function(arg: int): - """Une fonction quelconque de l'API""" - return jsonify( - {"current_user": current_user.to_dict(), "arg": arg, "dept": g.scodoc_dept} - ) +# @bp.route("/api_function/") +# @api_web_bp.route("/api_function/") +# @login_required +# @scodoc +# @permission_required(Permission.ScoView) +# def api_function(arg: int): +# """Une fonction quelconque de l'API""" +# return jsonify( +# {"current_user": current_user.to_dict(), "arg": arg, "dept": g.scodoc_dept} +# ) @bp.route("/etudiants/courants", defaults={"long": False}) diff --git a/app/api/formsemestres.py b/app/api/formsemestres.py index 8d55cf18e..7827d5839 100644 --- a/app/api/formsemestres.py +++ b/app/api/formsemestres.py @@ -251,6 +251,10 @@ def formsemestre_programme(formsemestre_id: int): @bp.route( "/formsemestre//etudiants", + defaults={"etat": None}, +) +@bp.route( + "/formsemestre//etudiants/actifs", defaults={"etat": scu.INSCRIT}, ) @bp.route( @@ -263,6 +267,10 @@ def formsemestre_programme(formsemestre_id: int): ) @api_web_bp.route( "/formsemestre//etudiants", + defaults={"etat": None}, +) +@api_web_bp.route( + "/formsemestre//etudiants/actifs", defaults={"etat": scu.INSCRIT}, ) @api_web_bp.route( @@ -276,18 +284,17 @@ def formsemestre_programme(formsemestre_id: int): @login_required @scodoc @permission_required(Permission.ScoView) -def formsemestre_etudiants(formsemestre_id: int, etat: str): - """ - Retourne la liste des étudiants d'un formsemestre - - formsemestre_id : l'id d'un formsemestre - """ +def formsemestre_etudiants(formsemestre_id: int, etat: str = None): + """Etudiants d'un formsemestre.""" query = FormSemestre.query.filter_by(id=formsemestre_id) if g.scodoc_dept: query = query.filter_by(dept_id=g.scodoc_dept_id) formsemestre: FormSemestre = query.first_or_404(formsemestre_id) + if etat is None: + inscriptions = formsemestre.inscriptions + else: + inscriptions = [ins for ins in formsemestre.inscriptions if ins.etat == etat] - inscriptions = [ins for ins in formsemestre.inscriptions if ins.etat == etat] etuds = [ins.etud.to_dict_short() for ins in inscriptions] # Ajout des groupes de chaque étudiants # XXX A REVOIR: trop inefficace ! diff --git a/app/api/users.py b/app/api/users.py index ee788f50f..96b7cdbf0 100644 --- a/app/api/users.py +++ b/app/api/users.py @@ -53,8 +53,8 @@ def users_info_query(): """Utilisateurs, filtrés par dept, active ou début nom /users/query?departement=dept_acronym&active=1&starts_with= - Si accès via API web, seuls les utilisateurs "accessibles" (selon les - permissions) sont retournés: le département de l'URL est ignoré, seules + Seuls les utilisateurs "accessibles" (selon les permissions) sont retournés. + Si accès via API web, le département de l'URL est ignoré, seules les permissions de l'utilisateur sont prises en compte. """ query = User.query diff --git a/tests/api/test_api_formsemestre.py b/tests/api/test_api_formsemestre.py index e0168b13a..6ccad0289 100644 --- a/tests/api/test_api_formsemestre.py +++ b/tests/api/test_api_formsemestre.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -"""Test Logos +"""Test formsemestre Utilisation : créer les variables d'environnement: (indiquer les valeurs @@ -481,32 +481,17 @@ def test_bulletins(api_headers): assert isinstance(bulletin_semestre["rang"]["total"], int) -# # jury -# def test_jury(): -# r = requests.get( -# API_URL + "/formsemestre/1/jury", -# headers=api_headers, -# verify=CHECK_CERTIFICATE, -# ) -# assert r.status_code == 200 - - def test_formsemestre_etudiants(api_headers): """ - Route: /formsemestre//etudiants, - /formsemestre//etudiants/demissionnaires, + Route: /formsemestre//etudiants + /formsemestre//etudiants/actifs + /formsemestre//etudiants/demissionnaires /formsemestre//etudiants/defaillants """ formsemestre_id = 1 - r = requests.get( - f"{API_URL}/formsemestre/{formsemestre_id}/etudiants", - headers=api_headers, - verify=CHECK_CERTIFICATE, - ) - assert r.status_code == 200 - formsemestre_etus = r.json() - assert isinstance(formsemestre_etus, list) - for etud in formsemestre_etus: + etuds = GET(f"/formsemestre/{formsemestre_id}/etudiants", headers=api_headers) + assert isinstance(etuds, list) + for etud in etuds: assert verify_fields(etud, FORMSEMESTRE_ETUD_FIELDS) is True assert isinstance(etud["id"], int) assert isinstance(etud["code_nip"], str) @@ -530,77 +515,26 @@ def test_formsemestre_etudiants(api_headers): assert isinstance(group["group_id"], int) assert group["group_name"] is None or isinstance(group["group_name"], int) - ### demissionnaires ### - r_demissionnaires = requests.get( - f"{API_URL}/formsemestre/{formsemestre_id}/etudiants/demissionnaires", - headers=api_headers, - verify=CHECK_CERTIFICATE, + ### actifs + etuds_actifs = GET( + f"/formsemestre/{formsemestre_id}/etudiants/actifs", headers=api_headers ) - assert r_demissionnaires.status_code == 200 - formsemestre_etus = r_demissionnaires.json() - assert isinstance(formsemestre_etus, list) + assert isinstance(etuds_actifs, list) - for etud in formsemestre_etus: - assert verify_fields(etud, FORMSEMESTRE_ETUD_FIELDS) is True - assert isinstance(etud["id"], int) - assert isinstance(etud["code_nip"], str) - assert isinstance(etud["code_ine"], str) - assert isinstance(etud["nom"], str) - assert etud["nom_usuel"] is None or isinstance(etud["nom_usuel"], str) - assert isinstance(etud["prenom"], str) - assert isinstance(etud["civilite"], str) - assert isinstance(etud["groups"], list) - etu_groups = etud["groups"] - for group in etu_groups: - assert isinstance(group["partition_id"], int) - assert isinstance(group["id"], int) - assert isinstance(group["formsemestre_id"], int) - assert group["partition_name"] is None or isinstance( - group["partition_name"], str - ) - assert (group["numero"] is None) or isinstance(group["numero"], int) - assert isinstance(group["bul_show_rank"], bool) - assert isinstance(group["show_in_lists"], bool) - assert isinstance(group["group_id"], int) - assert group["group_name"] is None or isinstance(group["group_name"], int) - - ### defaillants ### - r_defaillants = requests.get( - f"{API_URL}/formsemestre/{formsemestre_id}/etudiants/defaillants", + ### démissionnaires + etuds_dem = GET( + f"/formsemestre/{formsemestre_id}/etudiants/demissionnaires", headers=api_headers, - verify=CHECK_CERTIFICATE, ) - assert r_defaillants.status_code == 200 + assert isinstance(etuds_dem, list) - formsemestre_etus = r_defaillants.json() - assert isinstance(formsemestre_etus, list) - for etud in formsemestre_etus: - assert verify_fields(etud, FORMSEMESTRE_ETUD_FIELDS) is True - assert isinstance(etud["id"], int) - assert isinstance(etud["code_nip"], str) - assert isinstance(etud["code_ine"], str) - assert isinstance(etud["nom"], str) - assert etud["nom_usuel"] is None or isinstance(etud["nom_usuel"], str) - assert isinstance(etud["prenom"], str) - assert isinstance(etud["civilite"], str) - assert isinstance(etud["groups"], list) - etu_groups = etud["groups"] - for group in etu_groups: - assert isinstance(group["partition_id"], int) - assert isinstance(group["id"], int) - assert isinstance(group["formsemestre_id"], int) - assert group["partition_name"] is None or isinstance( - group["partition_name"], str - ) - assert (group["numero"] is None) or isinstance(group["numero"], int) - assert isinstance(group["bul_show_rank"], bool) - assert isinstance(group["show_in_lists"], bool) - assert isinstance(group["group_id"], int) - assert group["group_name"] is None or isinstance(group["group_name"], int) + ### défaillants + etuds_def = GET( + f"/formsemestre/{formsemestre_id}/etudiants/defaillants", headers=api_headers + ) + assert isinstance(etuds_def, list) - assert r.json() != r_demissionnaires.json() - assert r.json() != r_defaillants.json() - assert r_demissionnaires.json() != r_defaillants.json() + assert len(etuds) == (len(etuds_actifs) + len(etuds_dem) + len(etuds_def)) ### ERROR ### id_formsemestre_inexistant = 265165689619851621685 diff --git a/tests/api/test_api_jury.py b/tests/api/test_api_jury.py index 003f148f1..64f59c273 100644 --- a/tests/api/test_api_jury.py +++ b/tests/api/test_api_jury.py @@ -17,37 +17,24 @@ Utilisation : pytest tests/api/test_api_jury.py """ -import requests -from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers - - -def test_jury_preparation(api_headers): - """ - Test 'jury_preparation' - - Route : - - /jury/formsemestre//preparation_jury - """ - r = requests.get( - API_URL - + "/ScoDoc/api/jury/formsemestre//preparation_jury", - headers=api_headers, - verify=CHECK_CERTIFICATE, - ) - assert r.status_code == 200 +from tests.api.setup_test_api import ( + API_URL, + CHECK_CERTIFICATE, + GET, + POST_JSON, + api_headers, +) def test_jury_decisions(api_headers): """ - Test 'jury_decisions' - Route : - - /jury/formsemestre//decisions_jury + - /formsemestre//decisions_jury """ - r = requests.get( - API_URL + "/jury/formsemestre//decisions_jury", - headers=api_headers, - verify=CHECK_CERTIFICATE, + formsemestre_id = 1 + etudiants = GET(f"/formsemestre/{formsemestre_id}/etudiants", headers=api_headers) + decisions_jury = GET( + f"/formsemestre/{formsemestre_id}/decisions_jury", headers=api_headers ) - assert r.status_code == 200 + assert len(etudiants) == len(decisions_jury) diff --git a/tests/api/test_api_permissions.py b/tests/api/test_api_permissions.py index dc9661aa8..c9042a9d4 100644 --- a/tests/api/test_api_permissions.py +++ b/tests/api/test_api_permissions.py @@ -13,7 +13,6 @@ import requests -import flask from tests.api.setup_test_api import API_URL, SCODOC_URL, CHECK_CERTIFICATE, api_headers from tests.api.tools_test_api import verify_fields @@ -35,16 +34,18 @@ def test_permissions(api_headers): for r in app.url_map.iter_rules() if str(r).startswith("/ScoDoc/api") and not "logo" in str(r) # ignore logos + and not "absence" in str(r) # ignore absences and "GET" in r.methods ] assert len(api_rules) > 0 args = { - "etudid": 1, - "dept": "TAPI", - "dept_ident": "TAPI", + "acronym": "TAPI", "dept_id": 1, + "dept_ident": "TAPI", + "dept": "TAPI", "etape_apo": "???", "etat": "I", + "etudid": 1, "evaluation_id": 1, "formation_id": 1, "formsemestre_id": 1, @@ -54,6 +55,8 @@ def test_permissions(api_headers): "moduleimpl_id": 1, "nip": 1, "partition_id": 1, + "role_name": "Ens", + "uid": 1, } for rule in api_rules: path = rule.build(args)[1] @@ -95,4 +98,4 @@ def test_permissions(api_headers): headers=headers, verify=CHECK_CERTIFICATE, ) - assert r.status_code == 403 + assert r.status_code == 401 diff --git a/tools/fakedatabase/create_test_api_database.py b/tools/fakedatabase/create_test_api_database.py index 66e305586..03e4f7bf9 100644 --- a/tools/fakedatabase/create_test_api_database.py +++ b/tools/fakedatabase/create_test_api_database.py @@ -84,6 +84,8 @@ def create_users(depts: list[Departement]) -> tuple: sys.exit(1) perm_sco_view = Permission.get_by_name("ScoView") role_lecteur.add_permission(perm_sco_view) + perm_sco_users = Permission.get_by_name("ScoUsersView") + role_lecteur.add_permission(perm_sco_users) # Edition billets perm_billets = Permission.get_by_name("ScoAbsAddBillet") role_lecteur.add_permission(perm_billets) @@ -92,8 +94,13 @@ def create_users(depts: list[Departement]) -> tuple: db.session.add(role_lecteur) # Un role pour juste voir les utilisateurs - role_users_viewer = Role(name="UsersViewer", permissions=Permission.ScoUsersView) + role_users_viewer = Role( + name="UsersViewer", permissions=Permission.ScoUsersView | Permission.ScoView + ) db.session.add(role_users_viewer) + # Role View sur l'API, pour demander un jeton + role_view = Role(name="Viewer", permissions=Permission.ScoView) + db.session.add(role_view) # Un utilisateur "test" (passwd test) pouvant lire l'API user_test = User(user_name="test", nom="Doe", prenom="John", dept=dept.acronym) @@ -132,7 +139,7 @@ def create_users(depts: list[Departement]) -> tuple: for i, u in enumerate(users): for dept in depts[: i + 1]: u.add_role(role_users_viewer, dept.acronym) - u.add_role(role_lecteur, None) # necessaire pour avoir le jeton + u.add_role(role_view, None) # necessaire pour avoir le jeton db.session.commit() return user_test, other