API: modif formsemestre/etudiant. TESTS unitaires OK sauf logos.

This commit is contained in:
Emmanuel Viennet 2022-08-08 10:06:42 +02:00
parent f7a2c1e8e7
commit c065abd0bb
8 changed files with 79 additions and 143 deletions

View File

@ -19,15 +19,13 @@ from app.scodoc import sco_abs
from app.scodoc.sco_groups import get_group_members from app.scodoc.sco_groups import get_group_members
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
# TODO XXX revoir routes web API et calcul des droits
@bp.route("/absences/etudid/<int:etudid>", methods=["GET"]) @bp.route("/absences/etudid/<int:etudid>", methods=["GET"])
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
def absences(etudid: int = None): def absences(etudid: int = None):
""" """
Retourne la liste des absences d'un étudiant donné Liste des absences de cet étudiant
etudid : l'etudid d'un étudiant
Exemple de résultat: Exemple de résultat:
[ [

View File

@ -31,16 +31,16 @@ from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
# Un exemple: # Un exemple:
@bp.route("/api_function/<int:arg>") # @bp.route("/api_function/<int:arg>")
@api_web_bp.route("/api_function/<int:arg>") # @api_web_bp.route("/api_function/<int:arg>")
@login_required # @login_required
@scodoc # @scodoc
@permission_required(Permission.ScoView) # @permission_required(Permission.ScoView)
def api_function(arg: int): # def api_function(arg: int):
"""Une fonction quelconque de l'API""" # """Une fonction quelconque de l'API"""
return jsonify( # return jsonify(
{"current_user": current_user.to_dict(), "arg": arg, "dept": g.scodoc_dept} # {"current_user": current_user.to_dict(), "arg": arg, "dept": g.scodoc_dept}
) # )
@bp.route("/etudiants/courants", defaults={"long": False}) @bp.route("/etudiants/courants", defaults={"long": False})

View File

@ -251,6 +251,10 @@ def formsemestre_programme(formsemestre_id: int):
@bp.route( @bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants", "/formsemestre/<int:formsemestre_id>/etudiants",
defaults={"etat": None},
)
@bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants/actifs",
defaults={"etat": scu.INSCRIT}, defaults={"etat": scu.INSCRIT},
) )
@bp.route( @bp.route(
@ -263,6 +267,10 @@ def formsemestre_programme(formsemestre_id: int):
) )
@api_web_bp.route( @api_web_bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants", "/formsemestre/<int:formsemestre_id>/etudiants",
defaults={"etat": None},
)
@api_web_bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants/actifs",
defaults={"etat": scu.INSCRIT}, defaults={"etat": scu.INSCRIT},
) )
@api_web_bp.route( @api_web_bp.route(
@ -276,18 +284,17 @@ def formsemestre_programme(formsemestre_id: int):
@login_required @login_required
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
def formsemestre_etudiants(formsemestre_id: int, etat: str): def formsemestre_etudiants(formsemestre_id: int, etat: str = None):
""" """Etudiants d'un formsemestre."""
Retourne la liste des étudiants d'un formsemestre
formsemestre_id : l'id d'un formsemestre
"""
query = FormSemestre.query.filter_by(id=formsemestre_id) query = FormSemestre.query.filter_by(id=formsemestre_id)
if g.scodoc_dept: if g.scodoc_dept:
query = query.filter_by(dept_id=g.scodoc_dept_id) query = query.filter_by(dept_id=g.scodoc_dept_id)
formsemestre: FormSemestre = query.first_or_404(formsemestre_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] etuds = [ins.etud.to_dict_short() for ins in inscriptions]
# Ajout des groupes de chaque étudiants # Ajout des groupes de chaque étudiants
# XXX A REVOIR: trop inefficace ! # XXX A REVOIR: trop inefficace !

View File

@ -53,8 +53,8 @@ def users_info_query():
"""Utilisateurs, filtrés par dept, active ou début nom """Utilisateurs, filtrés par dept, active ou début nom
/users/query?departement=dept_acronym&active=1&starts_with=<string:nom> /users/query?departement=dept_acronym&active=1&starts_with=<string:nom>
Si accès via API web, seuls les utilisateurs "accessibles" (selon les Seuls les utilisateurs "accessibles" (selon les permissions) sont retournés.
permissions) sont retournés: le département de l'URL est ignoré, seules Si accès via API web, le département de l'URL est ignoré, seules
les permissions de l'utilisateur sont prises en compte. les permissions de l'utilisateur sont prises en compte.
""" """
query = User.query query = User.query

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Test Logos """Test formsemestre
Utilisation : Utilisation :
créer les variables d'environnement: (indiquer les valeurs 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) 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): def test_formsemestre_etudiants(api_headers):
""" """
Route: /formsemestre/<int:formsemestre_id>/etudiants, Route: /formsemestre/<int:formsemestre_id>/etudiants
/formsemestre/<int:formsemestre_id>/etudiants/demissionnaires, /formsemestre/<int:formsemestre_id>/etudiants/actifs
/formsemestre/<int:formsemestre_id>/etudiants/demissionnaires
/formsemestre/<int:formsemestre_id>/etudiants/defaillants /formsemestre/<int:formsemestre_id>/etudiants/defaillants
""" """
formsemestre_id = 1 formsemestre_id = 1
r = requests.get( etuds = GET(f"/formsemestre/{formsemestre_id}/etudiants", headers=api_headers)
f"{API_URL}/formsemestre/{formsemestre_id}/etudiants", assert isinstance(etuds, list)
headers=api_headers, for etud in etuds:
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
formsemestre_etus = r.json()
assert isinstance(formsemestre_etus, list)
for etud in formsemestre_etus:
assert verify_fields(etud, FORMSEMESTRE_ETUD_FIELDS) is True assert verify_fields(etud, FORMSEMESTRE_ETUD_FIELDS) is True
assert isinstance(etud["id"], int) assert isinstance(etud["id"], int)
assert isinstance(etud["code_nip"], str) assert isinstance(etud["code_nip"], str)
@ -530,77 +515,26 @@ def test_formsemestre_etudiants(api_headers):
assert isinstance(group["group_id"], int) assert isinstance(group["group_id"], int)
assert group["group_name"] is None or isinstance(group["group_name"], int) assert group["group_name"] is None or isinstance(group["group_name"], int)
### demissionnaires ### ### actifs
r_demissionnaires = requests.get( etuds_actifs = GET(
f"{API_URL}/formsemestre/{formsemestre_id}/etudiants/demissionnaires", f"/formsemestre/{formsemestre_id}/etudiants/actifs", headers=api_headers
)
assert isinstance(etuds_actifs, list)
### démissionnaires
etuds_dem = GET(
f"/formsemestre/{formsemestre_id}/etudiants/demissionnaires",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE,
) )
assert r_demissionnaires.status_code == 200 assert isinstance(etuds_dem, list)
formsemestre_etus = r_demissionnaires.json()
assert isinstance(formsemestre_etus, list)
for etud in formsemestre_etus: ### défaillants
assert verify_fields(etud, FORMSEMESTRE_ETUD_FIELDS) is True etuds_def = GET(
assert isinstance(etud["id"], int) f"/formsemestre/{formsemestre_id}/etudiants/defaillants", headers=api_headers
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(etuds_def, list)
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 ### assert len(etuds) == (len(etuds_actifs) + len(etuds_dem) + len(etuds_def))
r_defaillants = requests.get(
f"{API_URL}/formsemestre/{formsemestre_id}/etudiants/defaillants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_defaillants.status_code == 200
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)
assert r.json() != r_demissionnaires.json()
assert r.json() != r_defaillants.json()
assert r_demissionnaires.json() != r_defaillants.json()
### ERROR ### ### ERROR ###
id_formsemestre_inexistant = 265165689619851621685 id_formsemestre_inexistant = 265165689619851621685

View File

@ -17,37 +17,24 @@ Utilisation :
pytest tests/api/test_api_jury.py pytest tests/api/test_api_jury.py
""" """
import requests
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers from tests.api.setup_test_api import (
API_URL,
CHECK_CERTIFICATE,
def test_jury_preparation(api_headers): GET,
""" POST_JSON,
Test 'jury_preparation' api_headers,
)
Route :
- /jury/formsemestre/<int:formsemestre_id>/preparation_jury
"""
r = requests.get(
API_URL
+ "/ScoDoc/api/jury/formsemestre/<int:formsemestre_id>/preparation_jury",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
def test_jury_decisions(api_headers): def test_jury_decisions(api_headers):
""" """
Test 'jury_decisions'
Route : Route :
- /jury/formsemestre/<int:formsemestre_id>/decisions_jury - /formsemestre/<int:formsemestre_id>/decisions_jury
""" """
r = requests.get( formsemestre_id = 1
API_URL + "/jury/formsemestre/<int:formsemestre_id>/decisions_jury", etudiants = GET(f"/formsemestre/{formsemestre_id}/etudiants", headers=api_headers)
headers=api_headers, decisions_jury = GET(
verify=CHECK_CERTIFICATE, f"/formsemestre/{formsemestre_id}/decisions_jury", headers=api_headers
) )
assert r.status_code == 200 assert len(etudiants) == len(decisions_jury)

View File

@ -13,7 +13,6 @@
import requests import requests
import flask
from tests.api.setup_test_api import API_URL, SCODOC_URL, CHECK_CERTIFICATE, api_headers from tests.api.setup_test_api import API_URL, SCODOC_URL, CHECK_CERTIFICATE, api_headers
from tests.api.tools_test_api import verify_fields 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() for r in app.url_map.iter_rules()
if str(r).startswith("/ScoDoc/api") if str(r).startswith("/ScoDoc/api")
and not "logo" in str(r) # ignore logos and not "logo" in str(r) # ignore logos
and not "absence" in str(r) # ignore absences
and "GET" in r.methods and "GET" in r.methods
] ]
assert len(api_rules) > 0 assert len(api_rules) > 0
args = { args = {
"etudid": 1, "acronym": "TAPI",
"dept": "TAPI",
"dept_ident": "TAPI",
"dept_id": 1, "dept_id": 1,
"dept_ident": "TAPI",
"dept": "TAPI",
"etape_apo": "???", "etape_apo": "???",
"etat": "I", "etat": "I",
"etudid": 1,
"evaluation_id": 1, "evaluation_id": 1,
"formation_id": 1, "formation_id": 1,
"formsemestre_id": 1, "formsemestre_id": 1,
@ -54,6 +55,8 @@ def test_permissions(api_headers):
"moduleimpl_id": 1, "moduleimpl_id": 1,
"nip": 1, "nip": 1,
"partition_id": 1, "partition_id": 1,
"role_name": "Ens",
"uid": 1,
} }
for rule in api_rules: for rule in api_rules:
path = rule.build(args)[1] path = rule.build(args)[1]
@ -95,4 +98,4 @@ def test_permissions(api_headers):
headers=headers, headers=headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 403 assert r.status_code == 401

View File

@ -84,6 +84,8 @@ def create_users(depts: list[Departement]) -> tuple:
sys.exit(1) sys.exit(1)
perm_sco_view = Permission.get_by_name("ScoView") perm_sco_view = Permission.get_by_name("ScoView")
role_lecteur.add_permission(perm_sco_view) role_lecteur.add_permission(perm_sco_view)
perm_sco_users = Permission.get_by_name("ScoUsersView")
role_lecteur.add_permission(perm_sco_users)
# Edition billets # Edition billets
perm_billets = Permission.get_by_name("ScoAbsAddBillet") perm_billets = Permission.get_by_name("ScoAbsAddBillet")
role_lecteur.add_permission(perm_billets) role_lecteur.add_permission(perm_billets)
@ -92,8 +94,13 @@ def create_users(depts: list[Departement]) -> tuple:
db.session.add(role_lecteur) db.session.add(role_lecteur)
# Un role pour juste voir les utilisateurs # 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) 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 # Un utilisateur "test" (passwd test) pouvant lire l'API
user_test = User(user_name="test", nom="Doe", prenom="John", dept=dept.acronym) 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 i, u in enumerate(users):
for dept in depts[: i + 1]: for dept in depts[: i + 1]:
u.add_role(role_users_viewer, dept.acronym) 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() db.session.commit()
return user_test, other return user_test, other