forked from ScoDoc/ScoDoc
API: unifie traitement errors, messages JSON.
This commit is contained in:
parent
a053afeba6
commit
f7a2c1e8e7
@ -3,11 +3,18 @@
|
||||
|
||||
from flask import Blueprint
|
||||
from flask import request
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
api_bp = Blueprint("api", __name__)
|
||||
api_web_bp = Blueprint("apiweb", __name__)
|
||||
|
||||
|
||||
@api_bp.errorhandler(404)
|
||||
def api_error_handler(e):
|
||||
"erreurs API => json"
|
||||
return scu.json_error(404, message=str(e))
|
||||
|
||||
|
||||
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
|
||||
|
@ -9,7 +9,7 @@
|
||||
from flask import jsonify
|
||||
|
||||
from app.api import api_bp as bp
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.models import Identite
|
||||
|
||||
@ -53,7 +53,7 @@ def absences(etudid: int = None):
|
||||
"""
|
||||
etud = Identite.query.get(etudid)
|
||||
if etud is None:
|
||||
return error_response(404, message="etudiant inexistant")
|
||||
return json_error(404, message="etudiant inexistant")
|
||||
# Absences de l'étudiant
|
||||
ndb.open_db_connection()
|
||||
abs_list = sco_abs.list_abs_date(etud.id)
|
||||
@ -97,7 +97,7 @@ def absences_just(etudid: int = None):
|
||||
"""
|
||||
etud = Identite.query.get(etudid)
|
||||
if etud is None:
|
||||
return error_response(404, message="etudiant inexistant")
|
||||
return json_error(404, message="etudiant inexistant")
|
||||
|
||||
# Absences justifiées de l'étudiant
|
||||
abs_just = [
|
||||
|
@ -15,7 +15,7 @@ import app
|
||||
from app import db
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.models import BilletAbsence
|
||||
from app.models.etudiants import Identite
|
||||
from app.scodoc import sco_abs_billets
|
||||
@ -47,7 +47,7 @@ def billets_absence_create():
|
||||
description = data.get("description", "")
|
||||
justified = data.get("justified", False)
|
||||
if None in (etudid, abs_begin, abs_end):
|
||||
return error_response(
|
||||
return json_error(
|
||||
404, message="Paramètre manquant: etudid, abs_bein, abs_end requis"
|
||||
)
|
||||
query = Identite.query.filter_by(etudid=etudid)
|
||||
|
@ -17,7 +17,7 @@ from flask_login import login_required
|
||||
import app
|
||||
from app import db, log
|
||||
from app.api import api_bp as bp
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.models import Departement, FormSemestre
|
||||
from app.models import departements
|
||||
@ -103,12 +103,12 @@ def departement_create():
|
||||
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")
|
||||
return json_error(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 json_error(404, exc.args[0] if exc.args else "")
|
||||
return jsonify(dept.to_dict())
|
||||
|
||||
|
||||
@ -128,7 +128,7 @@ def departement_edit(acronym):
|
||||
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")
|
||||
return json_error(404, "missing argument: visible")
|
||||
visible = bool(visible)
|
||||
dept.visible = visible
|
||||
db.session.add(dept)
|
||||
|
@ -1,41 +0,0 @@
|
||||
# Authentication code borrowed from Miguel Grinberg's Mega Tutorial
|
||||
# (see https://github.com/miguelgrinberg/microblog)
|
||||
|
||||
# Under The MIT License (MIT)
|
||||
|
||||
# Copyright (c) 2017 Miguel Grinberg
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from flask import jsonify
|
||||
from werkzeug.http import HTTP_STATUS_CODES
|
||||
|
||||
|
||||
def error_response(status_code, message=None):
|
||||
"""Réponse sur erreur"""
|
||||
payload = {"error": HTTP_STATUS_CODES.get(status_code, "Unknown error")}
|
||||
if message:
|
||||
payload["message"] = message
|
||||
response = jsonify(payload)
|
||||
response.status_code = status_code
|
||||
return response
|
||||
|
||||
|
||||
def bad_request(message):
|
||||
"400 Bad Request response"
|
||||
return error_response(400, message)
|
@ -15,7 +15,7 @@ from sqlalchemy import desc, or_
|
||||
|
||||
import app
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.api import tools
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.models import (
|
||||
@ -116,7 +116,7 @@ def etudiant(etudid: int = None, nip: str = None, ine: str = None):
|
||||
etud = tools.get_etud(etudid, nip, ine)
|
||||
|
||||
if etud is None:
|
||||
return error_response(
|
||||
return json_error(
|
||||
404,
|
||||
message="étudiant inconnu",
|
||||
)
|
||||
@ -148,7 +148,7 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
||||
elif ine is not None:
|
||||
query = Identite.query.filter_by(code_ine=ine)
|
||||
else:
|
||||
return error_response(
|
||||
return json_error(
|
||||
404,
|
||||
message="parametre manquant",
|
||||
)
|
||||
@ -185,12 +185,12 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
|
||||
elif ine is not None:
|
||||
q_etud = Identite.query.filter_by(code_ine=ine)
|
||||
else:
|
||||
return error_response(404, message="parametre manquant")
|
||||
return json_error(404, message="parametre manquant")
|
||||
if g.scodoc_dept is not None:
|
||||
q_etud = q_etud.filter_by(dept_id=g.scodoc_dept_id)
|
||||
etud = q_etud.join(Admission).order_by(desc(Admission.annee)).first()
|
||||
if etud is None:
|
||||
return error_response(404, message="etudiant inexistant")
|
||||
return json_error(404, message="etudiant inexistant")
|
||||
query = FormSemestre.query.filter(
|
||||
FormSemestreInscription.etudid == etud.id,
|
||||
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
||||
@ -328,7 +328,7 @@ def etudiant_bulletin_semestre(
|
||||
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
|
||||
dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
||||
if g.scodoc_dept and dept != g.scodoc_dept:
|
||||
return error_response(404, "formsemestre non trouve")
|
||||
return json_error(404, "formsemestre non trouve")
|
||||
if etudid is not None:
|
||||
query = Identite.query.filter_by(id=etudid)
|
||||
elif nip is not None:
|
||||
@ -336,11 +336,11 @@ def etudiant_bulletin_semestre(
|
||||
elif ine is not None:
|
||||
query = Identite.query.filter_by(code_ine=ine, dept_id=dept.id)
|
||||
else:
|
||||
return error_response(404, message="parametre manquant")
|
||||
return json_error(404, message="parametre manquant")
|
||||
|
||||
etud = query.first()
|
||||
if etud is None:
|
||||
return error_response(404, message="etudiant inexistant")
|
||||
return json_error(404, message="etudiant inexistant")
|
||||
|
||||
app.set_sco_dept(dept.acronym)
|
||||
|
||||
@ -400,7 +400,7 @@ def etudiant_groups(formsemestre_id: int, etudid: int = None):
|
||||
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
||||
formsemestre = query.first()
|
||||
if formsemestre is None:
|
||||
return error_response(
|
||||
return json_error(
|
||||
404,
|
||||
message="formsemestre inconnu",
|
||||
)
|
||||
|
@ -15,7 +15,7 @@ import app
|
||||
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.models import Evaluation, ModuleImpl, FormSemestre
|
||||
from app.scodoc import sco_evaluation_db
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
|
@ -13,7 +13,7 @@ from flask_login import login_required
|
||||
|
||||
import app
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.models.formations import Formation
|
||||
from app.models.formsemestre import FormSemestre
|
||||
@ -210,7 +210,7 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
|
||||
try:
|
||||
data = sco_formations.formation_export(formation_id, export_ids)
|
||||
except ValueError:
|
||||
return error_response(500, message="Erreur inconnue")
|
||||
return json_error(500, message="Erreur inconnue")
|
||||
|
||||
return jsonify(data)
|
||||
|
||||
|
@ -13,7 +13,7 @@ from flask_login import login_required
|
||||
import app
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.comp import res_sem
|
||||
from app.comp.moy_mod import ModuleImplResults
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
@ -107,7 +107,7 @@ def formsemestres_query():
|
||||
try:
|
||||
annee_scolaire_int = int(annee_scolaire)
|
||||
except ValueError:
|
||||
return error_response(404, "invalid annee_scolaire: not int")
|
||||
return json_error(404, "invalid annee_scolaire: not int")
|
||||
debut_annee = scu.date_debut_anne_scolaire(annee_scolaire_int)
|
||||
fin_annee = scu.date_fin_anne_scolaire(annee_scolaire_int)
|
||||
formsemestres = formsemestres.filter(
|
||||
@ -119,7 +119,7 @@ def formsemestres_query():
|
||||
try:
|
||||
dept_id = int(dept_id)
|
||||
except ValueError:
|
||||
return error_response(404, "invalid dept_id: not int")
|
||||
return json_error(404, "invalid dept_id: not int")
|
||||
formsemestres = formsemestres.filter_by(dept_id=dept_id)
|
||||
if etape_apo is not None:
|
||||
formsemestres = formsemestres.join(FormSemestreEtape).filter(
|
||||
@ -417,7 +417,7 @@ def formsemestre_resultat(formsemestre_id: int):
|
||||
"""
|
||||
format_spec = request.args.get("format", None)
|
||||
if format_spec is not None and format_spec != "raw":
|
||||
return error_response(404, "invalid format specification")
|
||||
return json_error(404, "invalid format specification")
|
||||
convert_values = format_spec != "raw"
|
||||
|
||||
query = FormSemestre.query.filter_by(id=formsemestre_id)
|
||||
|
@ -15,7 +15,7 @@ import app
|
||||
from app import db, log
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.but import jury_but_recap
|
||||
from app.models import FormSemestre, FormSemestreInscription, Identite
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
|
@ -35,7 +35,7 @@ from flask_login import login_required
|
||||
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.api import requested_format
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.models import Departement
|
||||
from app.scodoc.sco_logos import list_logos, find_logo
|
||||
from app.decorators import scodoc, permission_required
|
||||
@ -49,10 +49,10 @@ from app.scodoc.sco_permissions import Permission
|
||||
@permission_required(Permission.ScoView)
|
||||
def api_get_glob_logos():
|
||||
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
|
||||
return error_response(401, message="accès interdit")
|
||||
return json_error(403, message="accès interdit")
|
||||
required_format = requested_format() # json only
|
||||
if required_format is None:
|
||||
return error_response(400, "Illegal format")
|
||||
return json_error(400, "Illegal format")
|
||||
logos = list_logos()[None]
|
||||
return jsonify(list(logos.keys()))
|
||||
|
||||
@ -62,10 +62,10 @@ def api_get_glob_logos():
|
||||
@permission_required(Permission.ScoView)
|
||||
def api_get_glob_logo(logoname):
|
||||
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
|
||||
return error_response(401, message="accès interdit")
|
||||
return json_error(403, message="accès interdit")
|
||||
logo = find_logo(logoname=logoname)
|
||||
if logo is None:
|
||||
return error_response(404, message="logo not found")
|
||||
return json_error(404, message="logo not found")
|
||||
logo.select()
|
||||
return send_file(
|
||||
logo.filepath,
|
||||
@ -80,7 +80,7 @@ def api_get_glob_logo(logoname):
|
||||
def api_get_local_logos(departement):
|
||||
dept_id = Departement.from_acronym(departement).id
|
||||
if not g.current_user.has_permission(Permission.ScoChangePreferences, departement):
|
||||
return error_response(401, message="accès interdit")
|
||||
return json_error(403, message="accès interdit")
|
||||
logos = list_logos().get(dept_id, dict())
|
||||
return jsonify(list(logos.keys()))
|
||||
|
||||
@ -92,10 +92,10 @@ def api_get_local_logo(departement, logoname):
|
||||
# format = requested_format("jpg", ['png', 'jpg']) XXX ?
|
||||
dept_id = Departement.from_acronym(departement).id
|
||||
if not g.current_user.has_permission(Permission.ScoChangePreferences, departement):
|
||||
return error_response(401, message="accès interdit")
|
||||
return json_error(403, message="accès interdit")
|
||||
logo = find_logo(logoname=logoname, dept_id=dept_id)
|
||||
if logo is None:
|
||||
return error_response(404, message="logo not found")
|
||||
return json_error(404, message="logo not found")
|
||||
logo.select()
|
||||
return send_file(
|
||||
logo.filepath,
|
||||
|
@ -14,7 +14,7 @@ import app
|
||||
from app import db, log
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.models import FormSemestre, FormSemestreInscription, Identite
|
||||
from app.models import GroupDescr, Partition
|
||||
from app.models.groups import group_membership
|
||||
@ -137,7 +137,7 @@ def etud_in_group_query(group_id: int):
|
||||
"""Etudiants du groupe, filtrés par état"""
|
||||
etat = request.args.get("etat")
|
||||
if etat not in {scu.INSCRIT, scu.DEMISSION, scu.DEF}:
|
||||
return error_response(404, "etat: valeur invalide")
|
||||
return json_error(404, "etat: valeur invalide")
|
||||
query = GroupDescr.query.filter_by(id=group_id)
|
||||
if g.scodoc_dept:
|
||||
query = (
|
||||
@ -169,7 +169,7 @@ def set_etud_group(etudid: int, group_id: int):
|
||||
)
|
||||
group = query.first_or_404()
|
||||
if etud.id not in {e.id for e in group.partition.formsemestre.etuds}:
|
||||
return error_response(404, "etud non inscrit au formsemestre du groupe")
|
||||
return json_error(404, "etud non inscrit au formsemestre du groupe")
|
||||
groups = (
|
||||
GroupDescr.query.filter_by(partition_id=group.partition.id)
|
||||
.join(group_membership)
|
||||
@ -261,13 +261,13 @@ def group_create(partition_id: int):
|
||||
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
||||
partition: Partition = query.first_or_404()
|
||||
if not partition.groups_editable:
|
||||
return error_response(404, "partition non editable")
|
||||
return json_error(404, "partition non editable")
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
group_name = data.get("group_name")
|
||||
if group_name is None:
|
||||
return error_response(404, "missing group name or invalid data format")
|
||||
return json_error(404, "missing group name or invalid data format")
|
||||
if not GroupDescr.check_name(partition, group_name):
|
||||
return error_response(404, "invalid group_name")
|
||||
return json_error(404, "invalid group_name")
|
||||
group_name = group_name.strip()
|
||||
|
||||
group = GroupDescr(group_name=group_name, partition_id=partition_id)
|
||||
@ -293,7 +293,7 @@ def group_delete(group_id: int):
|
||||
)
|
||||
group: GroupDescr = query.first_or_404()
|
||||
if not group.partition.groups_editable:
|
||||
return error_response(404, "partition non editable")
|
||||
return json_error(404, "partition non editable")
|
||||
formsemestre_id = group.partition.formsemestre_id
|
||||
log(f"deleting {group}")
|
||||
db.session.delete(group)
|
||||
@ -317,12 +317,12 @@ def group_edit(group_id: int):
|
||||
)
|
||||
group: GroupDescr = query.first_or_404()
|
||||
if not group.partition.groups_editable:
|
||||
return error_response(404, "partition non editable")
|
||||
return json_error(404, "partition non editable")
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
group_name = data.get("group_name")
|
||||
if group_name is not None:
|
||||
if not GroupDescr.check_name(group.partition, group_name, existing=True):
|
||||
return error_response(404, "invalid group_name")
|
||||
return json_error(404, "invalid group_name")
|
||||
group.group_name = group_name.strip()
|
||||
db.session.add(group)
|
||||
db.session.commit()
|
||||
@ -358,14 +358,14 @@ def partition_create(formsemestre_id: int):
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
partition_name = data.get("partition_name")
|
||||
if partition_name is None:
|
||||
return error_response(404, "missing partition_name or invalid data format")
|
||||
return json_error(404, "missing partition_name or invalid data format")
|
||||
if partition_name == scu.PARTITION_PARCOURS:
|
||||
return error_response(404, f"invalid partition_name {scu.PARTITION_PARCOURS}")
|
||||
return json_error(404, f"invalid partition_name {scu.PARTITION_PARCOURS}")
|
||||
if not Partition.check_name(formsemestre, partition_name):
|
||||
return error_response(404, "invalid partition_name")
|
||||
return json_error(404, "invalid partition_name")
|
||||
numero = data.get("numero", 0)
|
||||
if not isinstance(numero, int):
|
||||
return error_response(404, "invalid type for numero")
|
||||
return json_error(404, "invalid type for numero")
|
||||
args = {
|
||||
"formsemestre_id": formsemestre_id,
|
||||
"partition_name": partition_name.strip(),
|
||||
@ -376,7 +376,7 @@ def partition_create(formsemestre_id: int):
|
||||
boolean_field, False if boolean_field != "groups_editable" else True
|
||||
)
|
||||
if not isinstance(value, bool):
|
||||
return error_response(404, f"invalid type for {boolean_field}")
|
||||
return json_error(404, f"invalid type for {boolean_field}")
|
||||
args[boolean_field] = value
|
||||
|
||||
partition = Partition(**args)
|
||||
@ -407,7 +407,7 @@ def formsemestre_order_partitions(formsemestre_id: int):
|
||||
if not isinstance(partition_ids, int) and not all(
|
||||
isinstance(x, int) for x in partition_ids
|
||||
):
|
||||
return error_response(
|
||||
return json_error(
|
||||
404,
|
||||
message="paramètre liste des partitions invalide",
|
||||
)
|
||||
@ -444,7 +444,7 @@ def partition_order_groups(partition_id: int):
|
||||
if not isinstance(group_ids, int) and not all(
|
||||
isinstance(x, int) for x in group_ids
|
||||
):
|
||||
return error_response(
|
||||
return json_error(
|
||||
404,
|
||||
message="paramètre liste de groupe invalide",
|
||||
)
|
||||
@ -487,18 +487,18 @@ def partition_edit(partition_id: int):
|
||||
#
|
||||
if partition_name is not None and partition_name != partition.partition_name:
|
||||
if partition.is_parcours():
|
||||
return error_response(404, f"can't rename {scu.PARTITION_PARCOURS}")
|
||||
return json_error(404, f"can't rename {scu.PARTITION_PARCOURS}")
|
||||
if not Partition.check_name(
|
||||
partition.formsemestre, partition_name, existing=True
|
||||
):
|
||||
return error_response(404, "invalid partition_name")
|
||||
return json_error(404, "invalid partition_name")
|
||||
partition.partition_name = partition_name.strip()
|
||||
modified = True
|
||||
|
||||
numero = data.get("numero")
|
||||
if numero is not None and numero != partition.numero:
|
||||
if not isinstance(numero, int):
|
||||
return error_response(404, "invalid type for numero")
|
||||
return json_error(404, "invalid type for numero")
|
||||
partition.numero = numero
|
||||
modified = True
|
||||
|
||||
@ -506,9 +506,9 @@ def partition_edit(partition_id: int):
|
||||
value = data.get(boolean_field)
|
||||
if value is not None and value != getattr(partition, boolean_field):
|
||||
if not isinstance(value, bool):
|
||||
return error_response(404, f"invalid type for {boolean_field}")
|
||||
return json_error(404, f"invalid type for {boolean_field}")
|
||||
if boolean_field == "groups_editable" and partition.is_parcours():
|
||||
return error_response(404, f"can't change {scu.PARTITION_PARCOURS}")
|
||||
return json_error(404, f"can't change {scu.PARTITION_PARCOURS}")
|
||||
setattr(partition, boolean_field, value)
|
||||
modified = True
|
||||
|
||||
@ -540,7 +540,7 @@ def partition_delete(partition_id: int):
|
||||
query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
||||
partition: Partition = query.first_or_404()
|
||||
if not partition.partition_name:
|
||||
return error_response(404, "ne peut pas supprimer la partition par défaut")
|
||||
return json_error(404, "ne peut pas supprimer la partition par défaut")
|
||||
is_parcours = partition.is_parcours()
|
||||
formsemestre: FormSemestre = partition.formsemestre
|
||||
log(f"deleting partition {partition}")
|
||||
|
@ -10,7 +10,7 @@ from flask_login import current_user
|
||||
from sqlalchemy import desc, or_
|
||||
|
||||
from app import models
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.models import Departement, Identite, Admission
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
|
||||
@ -39,7 +39,7 @@ def get_etud(etudid=None, nip=None, ine=None) -> models.Identite:
|
||||
elif ine is not None:
|
||||
query = Identite.query.filter_by(code_ine=ine)
|
||||
else:
|
||||
return error_response(
|
||||
return json_error(
|
||||
404,
|
||||
message="parametre manquant",
|
||||
)
|
||||
|
@ -12,10 +12,10 @@
|
||||
from flask import g, jsonify, request
|
||||
from flask_login import current_user, login_required
|
||||
|
||||
import app
|
||||
from app import db, log
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.api.errors import error_response
|
||||
from app.models.etudiants import Identite
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.auth.models import User, Role, UserRole
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.models import Departement
|
||||
@ -35,11 +35,11 @@ def user_info(uid: int):
|
||||
"""
|
||||
user: User = User.query.get(uid)
|
||||
if user is None:
|
||||
return error_response(404, "user not found")
|
||||
return json_error(404, "user not found")
|
||||
if g.scodoc_dept:
|
||||
allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersView)
|
||||
if user.dept not in allowed_depts:
|
||||
return error_response(404, "user not found")
|
||||
return json_error(404, "user not found")
|
||||
|
||||
return jsonify(user.to_dict())
|
||||
|
||||
@ -101,20 +101,20 @@ def user_create():
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
user_name = data.get("user_name")
|
||||
if not user_name:
|
||||
return error_response(404, "empty user_name")
|
||||
return json_error(404, "empty user_name")
|
||||
user = User.query.filter_by(user_name=user_name).first()
|
||||
if user:
|
||||
return error_response(404, f"user_create: user {user} already exists\n")
|
||||
return json_error(404, f"user_create: user {user} already exists\n")
|
||||
dept = data.get("dept")
|
||||
if dept == "@all":
|
||||
dept = None
|
||||
allowed_depts = current_user.get_depts_with_permission(Permission.ScoUsersAdmin)
|
||||
if dept not in allowed_depts:
|
||||
return error_response(403, "user_create: departement non autorise")
|
||||
return json_error(403, "user_create: departement non autorise")
|
||||
if (dept is not None) and (
|
||||
Departement.query.filter_by(acronym=dept).first() is None
|
||||
):
|
||||
return error_response(404, "user_create: departement inexistant")
|
||||
return json_error(404, "user_create: departement inexistant")
|
||||
nom = data.get("nom")
|
||||
prenom = data.get("prenom")
|
||||
active = scu.to_bool(data.get("active", True))
|
||||
@ -151,12 +151,12 @@ def user_edit(uid: int):
|
||||
if (None not in allowed_depts) and (
|
||||
(orig_dept not in allowed_depts) or (dest_dept not in allowed_depts)
|
||||
):
|
||||
return error_response(403, "user_edit: departement non autorise")
|
||||
return json_error(403, "user_edit: departement non autorise")
|
||||
if dest_dept != orig_dept:
|
||||
if (dest_dept is not None) and (
|
||||
Departement.query.filter_by(acronym=dest_dept).first() is None
|
||||
):
|
||||
return error_response(404, "user_edit: departement inexistant")
|
||||
return json_error(404, "user_edit: departement inexistant")
|
||||
user.dept = dest_dept
|
||||
|
||||
user.nom = data.get("nom", user.nom)
|
||||
@ -189,7 +189,7 @@ def user_role_add(uid: int, role_name: str, dept: str = None):
|
||||
_ = Departement.query.filter_by(acronym=dept).first_or_404()
|
||||
allowed_depts = current_user.get_depts_with_permission(Permission.ScoSuperAdmin)
|
||||
if (None not in allowed_depts) and (dept not in allowed_depts):
|
||||
return error_response(403, "user_role_add: departement non autorise")
|
||||
return json_error(403, "user_role_add: departement non autorise")
|
||||
user.add_role(role, dept)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
@ -217,7 +217,7 @@ def user_role_remove(uid: int, role_name: str, dept: str = None):
|
||||
_ = Departement.query.filter_by(acronym=dept).first_or_404()
|
||||
allowed_depts = current_user.get_depts_with_permission(Permission.ScoSuperAdmin)
|
||||
if (None not in allowed_depts) and (dept not in allowed_depts):
|
||||
return error_response(403, "user_role_remove: departement non autorise")
|
||||
return json_error(403, "user_role_remove: departement non autorise")
|
||||
|
||||
query = UserRole.query.filter(UserRole.role == role, UserRole.user == user)
|
||||
if dept is not None:
|
||||
@ -276,7 +276,7 @@ def role_permission_add(role_name: str, perm_name: str):
|
||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||
permission = Permission.get_by_name(perm_name)
|
||||
if permission is None:
|
||||
return error_response(404, "role_permission_add: permission inconnue")
|
||||
return json_error(404, "role_permission_add: permission inconnue")
|
||||
role.add_permission(permission)
|
||||
db.session.add(role)
|
||||
db.session.commit()
|
||||
@ -299,7 +299,7 @@ def role_permission_remove(role_name: str, perm_name: str):
|
||||
role: Role = Role.query.filter_by(name=role_name).first_or_404()
|
||||
permission = Permission.get_by_name(perm_name)
|
||||
if permission is None:
|
||||
return error_response(404, "role_permission_remove: permission inconnue")
|
||||
return json_error(404, "role_permission_remove: permission inconnue")
|
||||
role.remove_permission(permission)
|
||||
db.session.add(role)
|
||||
db.session.commit()
|
||||
@ -319,7 +319,7 @@ def role_create(role_name: str):
|
||||
"""
|
||||
role: Role = Role.query.filter_by(name=role_name).first()
|
||||
if role:
|
||||
return error_response(404, "role_create: role already exists")
|
||||
return json_error(404, "role_create: role already exists")
|
||||
role = Role(name=role_name)
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
permissions = data.get("permissions")
|
||||
@ -327,7 +327,7 @@ def role_create(role_name: str):
|
||||
try:
|
||||
role.set_named_permissions(permissions)
|
||||
except ScoValueError:
|
||||
return error_response(404, "role_create: invalid permissions")
|
||||
return json_error(404, "role_create: invalid permissions")
|
||||
db.session.add(role)
|
||||
db.session.commit()
|
||||
return jsonify(role.to_dict())
|
||||
@ -352,12 +352,12 @@ def role_edit(role_name: str):
|
||||
try:
|
||||
role.set_named_permissions(permissions)
|
||||
except ScoValueError:
|
||||
return error_response(404, "role_create: invalid permissions")
|
||||
return json_error(404, "role_create: invalid permissions")
|
||||
role_name = data.get("role_name")
|
||||
if role_name and role_name != role.name:
|
||||
existing_role: Role = Role.query.filter_by(name=role_name).first()
|
||||
if existing_role:
|
||||
return error_response(404, "role_edit: role name already exists")
|
||||
return json_error(404, "role_edit: role name already exists")
|
||||
role.name = role_name
|
||||
db.session.add(role)
|
||||
db.session.commit()
|
||||
|
@ -9,7 +9,7 @@ from flask import g, redirect, request, url_for
|
||||
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
|
||||
import flask_login
|
||||
from app import login
|
||||
from app.api.errors import error_response
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.auth.models import User
|
||||
|
||||
basic_auth = HTTPBasicAuth()
|
||||
@ -80,8 +80,6 @@ def load_user_from_request(req: flask.Request) -> User:
|
||||
@login.unauthorized_handler
|
||||
def unauthorized():
|
||||
"flask-login: si pas autorisé, redirige vers page login, sauf si API"
|
||||
from app.api.errors import error_response as api_error_response
|
||||
|
||||
if request.blueprint == "api" or request.blueprint == "apiweb":
|
||||
return api_error_response(http.HTTPStatus.UNAUTHORIZED, "Non autorise (logic)")
|
||||
return json_error(http.HTTPStatus.UNAUTHORIZED, "Non autorise (logic)")
|
||||
return redirect(url_for("auth.login"))
|
||||
|
@ -10,11 +10,10 @@
|
||||
import collections
|
||||
import datetime
|
||||
import numpy as np
|
||||
from flask import url_for, g
|
||||
from flask import g, has_request_context, url_for
|
||||
|
||||
from app.comp.res_but import ResultatsSemestreBUT
|
||||
from app.models import FormSemestre, Identite
|
||||
from app.models import but_validations
|
||||
from app.models.groups import GroupDescr
|
||||
from app.models.ues import UniteEns
|
||||
from app.scodoc import sco_bulletins, sco_utils as scu
|
||||
@ -170,7 +169,9 @@ class BulletinBUT:
|
||||
"notes.moduleimpl_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
moduleimpl_id=modimpl.id,
|
||||
),
|
||||
)
|
||||
if has_request_context()
|
||||
else "na",
|
||||
"moyenne": {
|
||||
# # moyenne indicative de module: moyenne des UE,
|
||||
# # ignorant celles sans notes (nan)
|
||||
@ -228,7 +229,9 @@ class BulletinBUT:
|
||||
"notes.evaluation_listenotes",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
evaluation_id=e.id,
|
||||
),
|
||||
)
|
||||
if has_request_context()
|
||||
else "na",
|
||||
}
|
||||
return d
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
import datetime
|
||||
from functools import cached_property
|
||||
from flask import abort, url_for
|
||||
from flask import abort, has_request_context, url_for
|
||||
from flask import g, request
|
||||
import sqlalchemy
|
||||
from sqlalchemy import desc, text
|
||||
@ -196,7 +196,8 @@ class Identite(db.Model):
|
||||
"nationalite": self.nationalite or "",
|
||||
"boursier": self.boursier or "",
|
||||
}
|
||||
if include_urls:
|
||||
if include_urls and has_request_context():
|
||||
# test request context so we can use this func in tests under the flask shell
|
||||
d["fiche_url"] = url_for(
|
||||
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=self.id
|
||||
)
|
||||
|
@ -37,6 +37,7 @@ from flask_login import current_user
|
||||
|
||||
from app import email
|
||||
from app import log
|
||||
from app.scodoc.sco_utils import json_error
|
||||
from app.but import bulletin_but
|
||||
from app.comp import res_sem
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
@ -74,9 +75,13 @@ def get_formsemestre_bulletin_etud_json(
|
||||
) -> str:
|
||||
"""Le JSON du bulletin d'un étudiant, quel que soit le type de formation."""
|
||||
if formsemestre.formation.is_apc():
|
||||
r = bulletin_but.BulletinBUT(formsemestre)
|
||||
bul = bulletin_but.BulletinBUT(formsemestre)
|
||||
if not etud.id in bul.res.identdict:
|
||||
return error_response(
|
||||
404, "get_formsemestre_bulletin_etud_json: invalid etud"
|
||||
)
|
||||
return jsonify(
|
||||
r.bulletin_etud(
|
||||
bul.bulletin_etud(
|
||||
etud,
|
||||
formsemestre,
|
||||
force_publishing=force_publishing,
|
||||
|
@ -53,6 +53,7 @@ import requests
|
||||
import flask
|
||||
from flask import g, request
|
||||
from flask import flash, url_for, make_response, jsonify
|
||||
from werkzeug.http import HTTP_STATUS_CODES
|
||||
|
||||
from config import Config
|
||||
from app import log
|
||||
@ -819,15 +820,26 @@ def get_request_args():
|
||||
return vals
|
||||
|
||||
|
||||
def json_error(message, success=False, status=404):
|
||||
def json_error(status_code, message=None):
|
||||
"""Simple JSON response, for errors"""
|
||||
response = {
|
||||
"success": success,
|
||||
"status": status,
|
||||
"message": message,
|
||||
payload = {
|
||||
"error": HTTP_STATUS_CODES.get(status_code, "Unknown error"),
|
||||
"status": status_code,
|
||||
}
|
||||
if message:
|
||||
payload["message"] = message
|
||||
response = jsonify(payload)
|
||||
response.status_code = status_code
|
||||
log(f"Error: {response}")
|
||||
return jsonify(response), status
|
||||
return response
|
||||
|
||||
|
||||
def json_ok_response(status_code=200, payload=None):
|
||||
"""Simple JSON respons for "success" """
|
||||
payload = payload or {"OK": True}
|
||||
response = jsonify(payload)
|
||||
response.status_code = status_code
|
||||
return response
|
||||
|
||||
|
||||
def get_scodoc_version():
|
||||
|
@ -36,17 +36,22 @@ import time
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import flask
|
||||
from flask import abort, flash, jsonify, redirect, render_template, url_for
|
||||
from flask import abort, flash, redirect, render_template, url_for
|
||||
from flask import current_app, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
from app import db
|
||||
from app import models
|
||||
from app.auth.models import User
|
||||
from app.but import apc_edit_ue, jury_but_recap
|
||||
from app.but import jury_but, jury_but_validation_auto
|
||||
from app.but.forms import jury_but_forms
|
||||
from app.but import jury_but_pv
|
||||
from app.but import jury_but_view
|
||||
|
||||
from app.comp import res_sem
|
||||
from app.comp.res_but import ResultatsSemestreBUT
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
from app.models import ScolarNews
|
||||
from app.models.config import ScoDocSiteConfig
|
||||
from app.models.etudiants import Identite
|
||||
from app.models.formsemestre import FormSemestre
|
||||
@ -54,13 +59,8 @@ from app.models.formsemestre import FormSemestreUEComputationExpr
|
||||
from app.models.moduleimpls import ModuleImpl
|
||||
from app.models.modules import Module
|
||||
from app.models.ues import UniteEns
|
||||
from app.views import notes_bp as bp
|
||||
|
||||
from app import api
|
||||
from app import db
|
||||
from app import models
|
||||
from app.models import ScolarNews, but_validations
|
||||
from app.auth.models import User
|
||||
from app.but import apc_edit_ue, jury_but_recap
|
||||
from app.decorators import (
|
||||
scodoc,
|
||||
scodoc7func,
|
||||
@ -68,7 +68,6 @@ from app.decorators import (
|
||||
permission_required_compat_scodoc7,
|
||||
)
|
||||
|
||||
from app.views import notes_bp as bp
|
||||
|
||||
# ---------------
|
||||
|
||||
@ -775,7 +774,7 @@ def formsemestre_list(
|
||||
formsemestre_id = int(formsemestre_id) if formsemestre_id is not None else None
|
||||
formation_id = int(formation_id) if formation_id is not None else None
|
||||
except ValueError:
|
||||
return api.errors.error_response(404, "invalid id")
|
||||
return scu.json_error(404, "invalid id")
|
||||
# XAPI: new json api
|
||||
args = {}
|
||||
L = locals()
|
||||
|
@ -128,26 +128,26 @@ def set_module_ue_coef():
|
||||
try:
|
||||
module_id = int(request.form["module_id"])
|
||||
except ValueError:
|
||||
return scu.json_error("invalid module_id", 400)
|
||||
return scu.json_error(404, "invalid module_id")
|
||||
try:
|
||||
ue_id = int(request.form["ue_id"])
|
||||
except ValueError:
|
||||
return scu.json_error("invalid ue_id", 400)
|
||||
return scu.json_error(404, "invalid ue_id")
|
||||
try:
|
||||
coef = float(request.form["coef"].replace(",", "."))
|
||||
except ValueError:
|
||||
return scu.json_error("invalid coef", 400)
|
||||
return scu.json_error(404, "invalid coef")
|
||||
module = models.Module.query.get(module_id)
|
||||
if module is None:
|
||||
return scu.json_error(f"module not found ({module_id})", 404)
|
||||
return scu.json_error(404, f"module not found ({module_id})")
|
||||
ue = models.UniteEns.query.get(ue_id)
|
||||
if not ue:
|
||||
return scu.json_error(f"UE not found ({ue_id})", 404)
|
||||
return scu.json_error(404, f"UE not found ({ue_id})")
|
||||
module.set_ue_coef(ue, coef)
|
||||
db.session.commit()
|
||||
module.formation.invalidate_cached_sems()
|
||||
|
||||
return scu.json_error("ok", success=True, status=201)
|
||||
return scu.json_ok_response(201)
|
||||
|
||||
|
||||
@bp.route("/edit_modules_ue_coefs")
|
||||
|
@ -55,24 +55,24 @@ class APIError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def GET(path: str, headers={}, errmsg=None, dept=None):
|
||||
def GET(path: str, headers: dict = None, errmsg=None, dept=None):
|
||||
"""Get and returns as JSON"""
|
||||
if dept:
|
||||
url = SCODOC_URL + f"/ScoDoc/{dept}/api" + path
|
||||
else:
|
||||
url = API_URL + path
|
||||
r = requests.get(url, headers=headers or CUR_HEADERS, verify=CHECK_CERTIFICATE)
|
||||
r = requests.get(url, headers=headers or {}, verify=CHECK_CERTIFICATE)
|
||||
if r.status_code != 200:
|
||||
raise APIError(errmsg or f"""erreur status={r.status_code} !\n{r.text}""")
|
||||
return r.json() # decode la reponse JSON
|
||||
|
||||
|
||||
def POST_JSON(path: str, data: dict = {}, headers={}, errmsg=None):
|
||||
def POST_JSON(path: str, data: dict = {}, headers: dict = None, errmsg=None):
|
||||
"""Post"""
|
||||
r = requests.post(
|
||||
API_URL + path,
|
||||
json=data,
|
||||
headers=headers,
|
||||
headers=headers or {},
|
||||
verify=CHECK_CERTIFICATE,
|
||||
)
|
||||
if r.status_code != 200:
|
||||
|
@ -46,7 +46,7 @@ def test_admin_access(create_admin_token):
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
with app.test_client() as client:
|
||||
response = client.get(API_URL + "/logos", headers=headers)
|
||||
assert response.status_code == 401
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_lambda_access(create_lambda_token):
|
||||
@ -57,7 +57,7 @@ def test_lambda_access(create_lambda_token):
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
with app.test_client() as client:
|
||||
response = client.get(API_URL + "/logos", headers=headers)
|
||||
assert response.status_code == 401
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_initial_with_header_and_footer(create_super_token):
|
||||
|
@ -201,31 +201,21 @@ def create_formsemestre(
|
||||
|
||||
|
||||
def inscrit_etudiants(etuds: list, formsemestre: FormSemestre):
|
||||
"""Inscrit les etudiants aux semestres et à tous ses modules"""
|
||||
for etud in etuds:
|
||||
aleatoire = random.randint(0, 10)
|
||||
if aleatoire <= 3:
|
||||
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
|
||||
formsemestre.id,
|
||||
etud.id,
|
||||
group_ids=[],
|
||||
etat="I",
|
||||
method="init db test",
|
||||
)
|
||||
elif 3 < aleatoire <= 6:
|
||||
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
|
||||
formsemestre.id,
|
||||
etud.id,
|
||||
group_ids=[],
|
||||
etat="D",
|
||||
method="init db test",
|
||||
)
|
||||
"""Inscrit les étudiants au semestre et à tous ses modules.
|
||||
1/5 DEF, 1/5 DEF
|
||||
"""
|
||||
for i, etud in enumerate(etuds):
|
||||
if (i + 1) % 5 == 0:
|
||||
etat = "D"
|
||||
elif (i + 2) % 5 == 0:
|
||||
etat = "DEF"
|
||||
else:
|
||||
etat = "I"
|
||||
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
|
||||
formsemestre.id,
|
||||
etud.id,
|
||||
group_ids=[],
|
||||
etat="DEF",
|
||||
etat=etat,
|
||||
method="init db test",
|
||||
)
|
||||
|
||||
@ -368,11 +358,11 @@ def init_test_database():
|
||||
mapp.set_sco_dept(dept.acronym)
|
||||
user_lecteur, user_autre = create_users(depts)
|
||||
with sco_cache.DeferredSemCacheManager():
|
||||
etuds = create_etuds(dept)
|
||||
etuds = create_etuds(dept, nb=20)
|
||||
formation = import_formation(dept.id)
|
||||
formsemestre = create_formsemestre(formation, user_lecteur)
|
||||
create_evaluations(formsemestre)
|
||||
inscrit_etudiants(etuds, formsemestre)
|
||||
inscrit_etudiants(etuds[:16], formsemestre)
|
||||
saisie_notes_evaluations(formsemestre, user_lecteur)
|
||||
add_absences(formsemestre)
|
||||
create_etape_apo(formsemestre)
|
||||
|
Loading…
Reference in New Issue
Block a user