forked from ScoDoc/ScoDoc
Fix: scodoc_dept : API
This commit is contained in:
parent
10e0aa28b2
commit
6632fce008
@ -250,6 +250,7 @@ def create_app(config_class=DevConfig):
|
||||
from app.views import users_bp
|
||||
from app.views import absences_bp
|
||||
from app.api import bp as api_bp
|
||||
from app.api import api_web_bp as api_web_bp
|
||||
|
||||
# https://scodoc.fr/ScoDoc
|
||||
app.register_blueprint(scodoc_bp)
|
||||
@ -264,6 +265,8 @@ def create_app(config_class=DevConfig):
|
||||
absences_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite/Absences"
|
||||
)
|
||||
app.register_blueprint(api_bp, url_prefix="/ScoDoc/api")
|
||||
app.register_blueprint(api_web_bp, url_prefix="/ScoDoc/<scodoc_dept>/apiweb")
|
||||
|
||||
scodoc_log_formatter = LogRequestFormatter(
|
||||
"[%(asctime)s] %(sco_user)s@%(remote_addr)s requested %(url)s\n"
|
||||
"%(levelname)s: %(message)s"
|
||||
@ -351,7 +354,7 @@ def create_app(config_class=DevConfig):
|
||||
return app
|
||||
|
||||
|
||||
def set_sco_dept(scodoc_dept: str):
|
||||
def set_sco_dept(scodoc_dept: str, open_cnx=True):
|
||||
"""Set global g object to given dept and open db connection if needed"""
|
||||
# Check that dept exists
|
||||
try:
|
||||
@ -362,7 +365,7 @@ def set_sco_dept(scodoc_dept: str):
|
||||
raise ScoValueError(f"Invalid dept: {scodoc_dept}")
|
||||
g.scodoc_dept = scodoc_dept # l'acronyme
|
||||
g.scodoc_dept_id = dept.id # l'id
|
||||
if not hasattr(g, "db_conn"):
|
||||
if open_cnx and not hasattr(g, "db_conn"):
|
||||
ndb.open_db_connection()
|
||||
if not hasattr(g, "stored_get_formsemestre"):
|
||||
g.stored_get_formsemestre = {}
|
||||
|
@ -5,6 +5,7 @@ from flask import Blueprint
|
||||
from flask import request
|
||||
|
||||
bp = Blueprint("api", __name__)
|
||||
api_web_bp = Blueprint("apiweb", __name__)
|
||||
|
||||
|
||||
def requested_format(default_format="json", allowed_formats=None):
|
||||
|
@ -33,7 +33,9 @@ from flask_login import current_user
|
||||
|
||||
from app import log
|
||||
from app.auth.models import User
|
||||
from app.api import bp, api_web_bp
|
||||
from app.api.errors import error_response
|
||||
from app.decorators import scodoc, permission_required
|
||||
|
||||
basic_auth = HTTPBasicAuth()
|
||||
token_auth = HTTPTokenAuth()
|
||||
@ -42,7 +44,7 @@ token_auth = HTTPTokenAuth()
|
||||
@basic_auth.verify_password
|
||||
def verify_password(username, password):
|
||||
"Verify password for this user"
|
||||
user = User.query.filter_by(user_name=username).first()
|
||||
user: User = User.query.filter_by(user_name=username).first()
|
||||
if user and user.check_password(password):
|
||||
g.current_user = user
|
||||
# note: est aussi basic_auth.current_user()
|
||||
@ -85,7 +87,6 @@ def token_permission_required(permission):
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
# token_auth.login_required()
|
||||
current_user = basic_auth.current_user()
|
||||
if not current_user or not current_user.has_permission(permission, None):
|
||||
if current_user:
|
||||
@ -95,6 +96,8 @@ def token_permission_required(permission):
|
||||
log(message)
|
||||
# raise werkzeug.exceptions.Forbidden(description=message)
|
||||
return error_response(403, message=None)
|
||||
if not hasattr(g, "scodoc_dept"):
|
||||
g.scodoc_dept = None
|
||||
return f(*args, **kwargs)
|
||||
|
||||
# return decorated_function(token_auth.login_required())
|
||||
@ -125,3 +128,21 @@ def permission_required_api(permission_web, permission_api):
|
||||
return decorated_function
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def web_publish(route, function, permission, methods=("GET",)):
|
||||
"""Declare a route for a python function protected by permission
|
||||
using web http cookie
|
||||
"""
|
||||
return api_web_bp.route(route, methods=methods)(
|
||||
scodoc(permission_required(permission)(function))
|
||||
)
|
||||
|
||||
|
||||
def api_publish(route, function, permission, methods=("GET",)):
|
||||
"""Declare a route for a python function protected by permission
|
||||
using API token
|
||||
"""
|
||||
return bp.route(route, methods=methods)(
|
||||
token_auth.login_required(token_permission_required(permission)(function))
|
||||
)
|
||||
|
@ -15,8 +15,9 @@ from sqlalchemy import or_
|
||||
import app
|
||||
from app.api import bp
|
||||
from app.api.errors import error_response
|
||||
from app.api.auth import permission_required_api
|
||||
from app.api.auth import permission_required_api, api_publish, web_publish
|
||||
from app.api import tools
|
||||
|
||||
from app.models import Departement, FormSemestreInscription, FormSemestre, Identite
|
||||
from app.scodoc import sco_bulletins
|
||||
from app.scodoc import sco_groups
|
||||
@ -24,6 +25,17 @@ from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
|
||||
|
||||
def api_function(arg: int):
|
||||
"""Une fonction quelconque de l'API"""
|
||||
# u = current_user
|
||||
# dept = g.scodoc_dept # peut être None si accès API
|
||||
return jsonify({"current_user": current_user.to_dict(), "dept": g.scodoc_dept})
|
||||
|
||||
|
||||
api_publish("/api_function/<int:arg>", api_function, Permission.APIView)
|
||||
web_publish("/api_function/<int:arg>", api_function, Permission.ScoView)
|
||||
|
||||
|
||||
@bp.route("/etudiants/courants", defaults={"long": False})
|
||||
@bp.route("/etudiants/courants/long", defaults={"long": True})
|
||||
@permission_required_api(Permission.ScoView, Permission.APIView)
|
||||
@ -164,6 +176,9 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
||||
Dans 99% des cas, la liste contient un seul étudiant, mais si l'étudiant a
|
||||
été inscrit dans plusieurs départements, on a plusieurs objets (1 par dept.).
|
||||
"""
|
||||
allowed_depts = current_user.get_depts_with_permission(
|
||||
Permission.APIView | Permission.ScoView
|
||||
)
|
||||
if etudid is not None:
|
||||
query = Identite.query.filter_by(id=etudid)
|
||||
elif nip is not None:
|
||||
@ -175,7 +190,11 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
||||
404,
|
||||
message="parametre manquant",
|
||||
)
|
||||
|
||||
if not None in allowed_depts:
|
||||
# restreint aux départements autorisés:
|
||||
etuds = etuds.join(Departement).filter(
|
||||
or_(Departement.acronym == acronym for acronym in allowed_depts)
|
||||
)
|
||||
return jsonify([etud.to_dict_bul(include_urls=False) for etud in query])
|
||||
|
||||
|
||||
|
@ -182,6 +182,7 @@ def partition_remove_etud(partition_id: int, etudid: int):
|
||||
for g in groups:
|
||||
g.etuds.remove(etud)
|
||||
db.session.commit()
|
||||
app.set_sco_dept(partition.formsemestre.departement.acronym)
|
||||
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
|
||||
return jsonify({"partition_id": partition_id, "etudid": etudid})
|
||||
|
||||
@ -319,6 +320,7 @@ def formsemestre_order_partitions(formsemestre_id: int):
|
||||
p.numero = numero
|
||||
db.session.add(p)
|
||||
db.session.commit()
|
||||
app.set_sco_dept(formsemestre.departement.acronym)
|
||||
sco_cache.invalidate_formsemestre(formsemestre_id)
|
||||
return jsonify(formsemestre.to_dict())
|
||||
|
||||
@ -343,6 +345,7 @@ def partition_order_groups(partition_id: int):
|
||||
group.numero = numero
|
||||
db.session.add(group)
|
||||
db.session.commit()
|
||||
app.set_sco_dept(partition.formsemestre.departement.acronym)
|
||||
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
|
||||
return jsonify(partition.to_dict(with_groups=True))
|
||||
|
||||
|
@ -6,11 +6,13 @@
|
||||
"""ScoDoc 9 API : outils
|
||||
"""
|
||||
|
||||
from sqlalchemy import desc
|
||||
from flask_login import current_user
|
||||
from sqlalchemy import desc, or_
|
||||
|
||||
from app import models
|
||||
from app.api.errors import error_response
|
||||
from app.models import Identite, Admission
|
||||
from app.models import Departement, Identite, Admission
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
|
||||
|
||||
def get_etud(etudid=None, nip=None, ine=None) -> models.Identite:
|
||||
@ -24,8 +26,15 @@ def get_etud(etudid=None, nip=None, ine=None) -> models.Identite:
|
||||
|
||||
Return None si étudiant inexistant.
|
||||
"""
|
||||
allowed_depts = current_user.get_depts_with_permission(
|
||||
Permission.APIView | Permission.ScoView
|
||||
)
|
||||
|
||||
if etudid is not None:
|
||||
return Identite.query.get(etudid)
|
||||
etud: Identite = Identite.query.get(etudid)
|
||||
if (None in allowed_depts) or etud.departement.acronym in allowed_depts:
|
||||
return etud
|
||||
return None # accès interdit => pas d'étudiant
|
||||
|
||||
if nip is not None:
|
||||
query = Identite.query.filter_by(code_nip=nip)
|
||||
@ -36,4 +45,9 @@ def get_etud(etudid=None, nip=None, ine=None) -> models.Identite:
|
||||
404,
|
||||
message="parametre manquant",
|
||||
)
|
||||
if None not in allowed_depts:
|
||||
# restreint aux départements autorisés:
|
||||
etuds = etuds.join(Departement).filter(
|
||||
or_(Departement.acronym == acronym for acronym in allowed_depts)
|
||||
)
|
||||
return query.join(Admission).order_by(desc(Admission.annee)).first()
|
||||
|
@ -53,16 +53,18 @@ import traceback
|
||||
|
||||
from flask import g
|
||||
|
||||
import app
|
||||
from app import log
|
||||
from app.scodoc import notesdb as ndb
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_exceptions import ScoException
|
||||
|
||||
CACHE = None # set in app.__init__.py
|
||||
|
||||
|
||||
class ScoDocCache:
|
||||
"""Cache for ScoDoc objects.
|
||||
keys are prefixed by the current departement.
|
||||
keys are prefixed by the current departement: g.scodoc_dept MUST be set.
|
||||
"""
|
||||
|
||||
timeout = None # ttl, infinite by default
|
||||
@ -240,6 +242,15 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
|
||||
if getattr(g, "defer_cache_invalidation", 0) > 0:
|
||||
g.sem_to_invalidate.add(formsemestre_id)
|
||||
return
|
||||
if getattr(g, "scodoc_dept") is None:
|
||||
# appel via API ou tests sans dept:
|
||||
formsemestre = None
|
||||
if formsemestre_id:
|
||||
formsemestre = FormSemestre.query.get(formsemestre_id)
|
||||
if formsemestre is None:
|
||||
raise ScoException("invalidate_formsemestre: departement must be set")
|
||||
app.set_sco_dept(formsemestre.departement.acronym, open_cnx=False)
|
||||
|
||||
if formsemestre_id is None:
|
||||
# clear all caches
|
||||
log(
|
||||
|
@ -153,34 +153,6 @@ def sco_publish(route, function, permission, methods=("GET",)):
|
||||
)
|
||||
|
||||
|
||||
# --------------------- Quelques essais élémentaires:
|
||||
# @bp.route("/essai")
|
||||
# @scodoc
|
||||
# @permission_required(Permission.ScoView)
|
||||
# @scodoc7func
|
||||
# def essai():
|
||||
# return essai_()
|
||||
|
||||
|
||||
# def essai_():
|
||||
# return "<html><body><h2>essai !</h2><p>%s</p></body></html>" % ()
|
||||
|
||||
|
||||
# def essai2():
|
||||
# err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
|
||||
# <p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
|
||||
# laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
|
||||
# </p>
|
||||
# <a href="url_for('notes.ue_table', scodoc-dept=g.scodoc_dept, formation_id='XXX')">reprendre</a>
|
||||
# """
|
||||
# raise ScoGenError(err_page)
|
||||
# # raise ScoGenError("une erreur banale")
|
||||
# return essai_("sans request")
|
||||
|
||||
|
||||
# sco_publish("/essai2", essai2, Permission.ScoImplement)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
#
|
||||
# Notes/ methods
|
||||
|
@ -169,6 +169,7 @@ pp(partitions)
|
||||
POST_JSON(f"/group/5559/delete")
|
||||
POST_JSON(f"/group/5327/edit", data={"group_name": "TDXXX"})
|
||||
|
||||
# --------- XXX à passer en dans les tests unitaires
|
||||
# 1- Crée une partition, puis la change de nom
|
||||
js = POST_JSON(
|
||||
f"/formsemestre/{formsemestre_id}/partition/create",
|
||||
@ -192,8 +193,10 @@ POST_JSON(f"/group/{group_id}/set_etudiant/{etudid}")
|
||||
# 4- retire du groupe
|
||||
POST_JSON(f"/group/{group_id}/remove_etudiant/{etudid}")
|
||||
|
||||
# Suppression
|
||||
# 5- Suppression
|
||||
POST_JSON(f"/partition/{partition_id}/delete")
|
||||
# ------
|
||||
|
||||
|
||||
#
|
||||
POST_JSON(
|
||||
|
Loading…
Reference in New Issue
Block a user