1
0
forked from ScoDoc/ScoDoc

API: creation/edition/suppression département

This commit is contained in:
Emmanuel Viennet 2022-08-05 06:55:05 +02:00
parent 371b017245
commit 45a5c8ae81
4 changed files with 131 additions and 13 deletions

View File

@ -1,12 +1,15 @@
############################################### Departements ########################################################## ############################################### Departements ##########################################################
from flask import jsonify from flask import jsonify, request
import app import app
from app import models from app import db, log
from app.api import api_bp as bp from app.api import api_bp as bp
from app.api.errors import error_response
from app.decorators import scodoc, permission_required from app.decorators import scodoc, permission_required
from app.models import Departement, FormSemestre from app.models import Departement, FormSemestre
from app.models import departements
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -24,7 +27,7 @@ def get_departement(dept_ident: str) -> Departement:
@bp.route("/departements", methods=["GET"]) @bp.route("/departements", methods=["GET"])
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
def departements(): def departements_list():
"""Liste les départements""" """Liste les départements"""
return jsonify([dept.to_dict() for dept in Departement.query]) return jsonify([dept.to_dict() for dept in Departement.query])
@ -68,6 +71,66 @@ def departement_by_id(dept_id: int):
return jsonify(dept.to_dict()) return jsonify(dept.to_dict())
@bp.route("/departement/create", methods=["POST"])
@scodoc
@permission_required(Permission.ScoSuperAdmin)
def departement_create():
"""
Création d'un département.
The request content type should be "application/json":
{
"acronym": str,
"visible":bool,
}
"""
data = request.get_json(force=True) # may raise 400 Bad Request
acronym = str(data.get("acronym", ""))
if not acronym:
return error_response(404, "missing acronym")
visible = bool(data.get("visible", True))
try:
dept = departements.create_dept(acronym, visible=visible)
except ScoValueError as exc:
return error_response(404, exc.args[0] if exc.args else "")
return jsonify(dept.to_dict())
@bp.route("/departement/<string:acronym>/edit", methods=["POST"])
@scodoc
@permission_required(Permission.ScoSuperAdmin)
def departement_edit(acronym):
"""
Edition d'un département: seul visible peut être modifié
The request content type should be "application/json":
{
"visible":bool,
}
"""
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
data = request.get_json(force=True) # may raise 400 Bad Request
visible = bool(data.get("visible", None))
if visible is None:
return error_response(404, "missing argument: visible")
visible = bool(visible)
dept.visible = visible
db.session.add(dept)
db.session.commit()
return jsonify(dept.to_dict())
@bp.route("/departement/<string:acronym>/delete", methods=["POST"])
@scodoc
@permission_required(Permission.ScoSuperAdmin)
def departement_delete(acronym):
"""
Suppression d'un département.
"""
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
db.session.delete(dept)
db.session.commit()
return jsonify({"OK": True})
@bp.route("/departement/<string:acronym>/etudiants", methods=["GET"]) @bp.route("/departement/<string:acronym>/etudiants", methods=["GET"])
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
@ -168,11 +231,10 @@ def dept_formsemestres_courants(acronym: str):
... ...
] ]
""" """
# Le département, spécifié par un id ou un acronyme
dept = Departement.query.filter_by(acronym=acronym).first_or_404() dept = Departement.query.filter_by(acronym=acronym).first_or_404()
# Les semestres en cours de ce département # Les semestres en cours de ce département
formsemestres = models.FormSemestre.query.filter( formsemestres = FormSemestre.query.filter(
FormSemestre.dept_id == dept.id, FormSemestre.dept_id == dept.id,
FormSemestre.date_debut <= app.db.func.now(), FormSemestre.date_debut <= app.db.func.now(),
FormSemestre.date_fin >= app.db.func.now(), FormSemestre.date_fin >= app.db.func.now(),
@ -192,7 +254,7 @@ def dept_formsemestres_courants_by_id(dept_id: int):
dept = Departement.query.get_or_404(dept_id) dept = Departement.query.get_or_404(dept_id)
# Les semestres en cours de ce département # Les semestres en cours de ce département
formsemestres = models.FormSemestre.query.filter( formsemestres = FormSemestre.query.filter(
FormSemestre.dept_id == dept.id, FormSemestre.dept_id == dept.id,
FormSemestre.date_debut <= app.db.func.now(), FormSemestre.date_debut <= app.db.func.now(),
FormSemestre.date_fin >= app.db.func.now(), FormSemestre.date_fin >= app.db.func.now(),

View File

@ -25,21 +25,32 @@ SCODOC_URL = os.environ["SCODOC_URL"]
API_URL = SCODOC_URL + "/ScoDoc/api" API_URL = SCODOC_URL + "/ScoDoc/api"
API_USER = os.environ.get("API_USER", "test") API_USER = os.environ.get("API_USER", "test")
API_PASSWORD = os.environ.get("API_PASSWD", "test") API_PASSWORD = os.environ.get("API_PASSWD", "test")
API_USER_ADMIN = os.environ.get("API_USER_ADMIN", "admin_api")
API_PASSWORD_ADMIN = os.environ.get("API_PASSWD_ADMIN", "admin_api")
DEPT_ACRONYM = "TAPI" DEPT_ACRONYM = "TAPI"
print(f"SCODOC_URL={SCODOC_URL}") print(f"SCODOC_URL={SCODOC_URL}")
print(f"API URL={API_URL}") print(f"API URL={API_URL}")
@pytest.fixture def get_auth_headers(user, password) -> dict:
def api_headers() -> dict: "Demande de jeton, dict à utiliser dans les en-têtes de requêtes http"
""" r0 = requests.post(API_URL + "/tokens", auth=(user, password))
Demande un jeton et renvoie un dict à utiliser dans les en-têtes de requêtes http
"""
r0 = requests.post(API_URL + "/tokens", auth=(API_USER, API_PASSWORD))
token = r0.json()["token"] token = r0.json()["token"]
return {"Authorization": f"Bearer {token}"} return {"Authorization": f"Bearer {token}"}
@pytest.fixture
def api_headers() -> dict:
"""Jeton, utilisateur API ordinaire"""
return get_auth_headers(API_USER, API_PASSWORD)
@pytest.fixture
def api_admin_headers() -> dict:
"""Jeton, utilisateur API SuperAdmin"""
return get_auth_headers(API_USER_ADMIN, API_PASSWORD_ADMIN)
class APIError(Exception): class APIError(Exception):
pass pass

View File

@ -19,7 +19,14 @@ Utilisation :
import requests 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,
GET,
POST_JSON,
api_headers,
api_admin_headers,
)
from tests.api.tools_test_api import ( from tests.api.tools_test_api import (
verify_fields, verify_fields,
DEPARTEMENT_FIELDS, DEPARTEMENT_FIELDS,
@ -28,6 +35,34 @@ from tests.api.tools_test_api import (
) )
def test_create_dept(api_admin_headers):
"""
Routes: /departement/create,
/departement/<string:dept_acronym>/edit
/departement/<string:dept_acronym>/delete
"""
dept = POST_JSON(
"/departement/create",
{"acronym": "XTEST", "visible": True},
headers=api_admin_headers,
)
dept_r = GET(f"/departement/{dept['acronym']}", headers=api_admin_headers)
assert dept["acronym"] == dept_r["acronym"]
assert dept_r["visible"] is True
dept_e = POST_JSON(
f"/departement/{dept['acronym']}/edit",
{"visible": False},
headers=api_admin_headers,
)
dept_r = GET(f"/departement/{dept['acronym']}", headers=api_admin_headers)
assert dept_r["visible"] is False
r = POST_JSON(
f"/departement/{dept['acronym']}/delete",
headers=api_admin_headers,
)
assert r["OK"] is True
def test_departements(api_headers): def test_departements(api_headers):
""" """
Routes: /departements_ids, /departement, /departement/<string:dept>/formsemestres_ids Routes: /departements_ids, /departement, /departement/<string:dept>/formsemestres_ids

View File

@ -106,6 +106,16 @@ def create_users(dept: Departement) -> tuple:
other.set_password("other") other.set_password("other")
db.session.add(other) db.session.add(other)
# Un utilisateur "admin_api"
admin_api = User(user_name="admin_api", nom="Admin", prenom="API")
admin_api.set_password("admin_api")
db.session.add(admin_api)
role = Role.query.filter_by(name="SuperAdmin").first()
if role is None:
print("Erreur: rôle SuperAdmin non existant")
sys.exit(1)
admin_api.add_role(role, None)
db.session.commit() db.session.commit()
return user, other return user, other