ScoDoc/app/api/__init__.py

128 lines
3.7 KiB
Python

"""api.__init__
"""
from functools import wraps
from flask_json import as_json
from flask import Blueprint
from flask import current_app, g, request
from flask_login import current_user
from app import db, log
from app.decorators import permission_required
from app.scodoc import sco_utils as scu
from app.scodoc.sco_exceptions import AccessDenied, ScoException
from app.scodoc.sco_permissions import Permission
api_bp = Blueprint("api", __name__)
api_web_bp = Blueprint("apiweb", __name__)
# HTTP ERROR STATUS
API_CLIENT_ERROR = 400 # erreur dans les paramètres fournis par le client
def api_permission_required(permission):
"""Ce décorateur fait la même chose que @permission_required
mais enregistre dans l'attribut .scodoc_permission
de la fonction la valeur de la permission.
Cette valeur n'est utilisée que pour la génération automatique de la documentation.
"""
def decorator(f):
f.scodoc_permission = permission
@wraps(f)
def decorated_function(*args, **kwargs):
scodoc_dept = getattr(g, "scodoc_dept", None)
if not current_user.has_permission(permission, scodoc_dept):
return current_app.login_manager.unauthorized()
return f(*args, **kwargs)
return decorated_function
return decorator
@api_bp.errorhandler(ScoException)
@api_web_bp.errorhandler(ScoException)
@api_bp.errorhandler(404)
def api_error_handler(e):
"erreurs API => json"
log(f"api_error_handler: {e}")
return scu.json_error(404, message=str(e))
@api_bp.errorhandler(AccessDenied)
@api_web_bp.errorhandler(AccessDenied)
def permission_denied_error_handler(exc):
"""
Renvoie message d'erreur pour l'erreur 403
"""
return scu.json_error(
403, f"operation non autorisee ({exc.args[0] if exc.args else ''})"
)
def requested_format(default_format="json", allowed_formats=None):
"""Extract required format from query string.
* default value is json. A list of allowed formats may be provided
(['json'] considered if not provided).
* if the required format is not in allowed list, returns None.
NB: if json in not in allowed_formats, format specification is mandatory.
"""
format_type = request.args.get("format", default_format)
if format_type in (allowed_formats or ["json"]):
return format_type
return None
@as_json
def get_model_api_object(
model_cls: db.Model,
model_id: int,
join_cls: db.Model = None,
restrict: bool | None = None,
):
"""
Retourne une réponse contenant la représentation api de l'objet "Model[model_id]"
Filtrage du département en fonction d'une classe de jointure (eg: Identite, Formsemestre) -> join_cls
exemple d'utilisation : fonction "justificatif()" -> app/api/justificatifs.py
L'agument restrict est passé to_dict, est signale que l'on veut une version restreinte
(sans données personnelles, ou sans informations sur le justificatif d'absence)
"""
query = model_cls.query.filter_by(id=model_id)
if g.scodoc_dept and join_cls is not None:
query = query.join(join_cls).filter_by(dept_id=g.scodoc_dept_id)
unique: model_cls = query.first()
if unique is None:
return scu.json_error(
404,
message=f"{model_cls.__name__} inexistant(e)",
)
if restrict is None:
return unique.to_dict(format_api=True)
return unique.to_dict(format_api=True, restrict=restrict)
from app.api import tokens
from app.api import (
assiduites,
billets_absences,
departements,
etudiants,
evaluations,
formations,
formsemestres,
jury,
justificatifs,
logos,
moduleimpl,
partitions,
semset,
users,
)