forked from ScoDoc/ScoDoc
WIP: migration de ZNotes, decorateurs, etc.
This commit is contained in:
parent
4864fa5040
commit
369b45a8c4
@ -33,6 +33,13 @@ pour régénerer ce fichier:
|
|||||||
|
|
||||||
pip freeze > requirements.txt
|
pip freeze > requirements.txt
|
||||||
|
|
||||||
|
### Bidouilles temporaires
|
||||||
|
|
||||||
|
Installer le bon vieux `pyExcelerator` dans l'environnement:
|
||||||
|
|
||||||
|
(cd /tmp; tar xfz /opt/scodoc/Products/ScoDoc/config/softs/pyExcelerator-0.6.3a.patched.tgz )
|
||||||
|
(cd /tmp/pyExcelerator-0.6.3a.patched/; python setup.py install)
|
||||||
|
|
||||||
## Lancement serveur (développement, sur VM Linux)
|
## Lancement serveur (développement, sur VM Linux)
|
||||||
|
|
||||||
export FLASK_APP=scodoc.py
|
export FLASK_APP=scodoc.py
|
||||||
|
17
app/__init__.py
Executable file → Normal file
17
app/__init__.py
Executable file → Normal file
@ -43,9 +43,22 @@ def create_app(config_class=Config):
|
|||||||
|
|
||||||
app.register_blueprint(auth_bp, url_prefix="/auth")
|
app.register_blueprint(auth_bp, url_prefix="/auth")
|
||||||
|
|
||||||
from app.views import notes_bp
|
from app.views import essais_bp
|
||||||
|
|
||||||
app.register_blueprint(notes_bp, url_prefix="/ScoDoc")
|
app.register_blueprint(essais_bp, url_prefix="/Essais")
|
||||||
|
|
||||||
|
from app.views import scolar_bp
|
||||||
|
from app.views import notes_bp
|
||||||
|
from app.views import absences_bp
|
||||||
|
|
||||||
|
# https://scodoc.fr/ScoDoc/RT/Scolarite/...
|
||||||
|
app.register_blueprint(scolar_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite")
|
||||||
|
# https://scodoc.fr/ScoDoc/RT/Scolarite/Notes/...
|
||||||
|
app.register_blueprint(notes_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite/Notes")
|
||||||
|
# https://scodoc.fr/ScoDoc/RT/Scolarite/Absences/...
|
||||||
|
app.register_blueprint(
|
||||||
|
absences_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite/Absences"
|
||||||
|
)
|
||||||
|
|
||||||
from app.main import bp as main_bp
|
from app.main import bp as main_bp
|
||||||
|
|
||||||
|
@ -16,26 +16,6 @@ from werkzeug.exceptions import BadRequest
|
|||||||
from app.auth.models import Permission
|
from app.auth.models import Permission
|
||||||
|
|
||||||
|
|
||||||
def permission_required(permission):
|
|
||||||
def decorator(f):
|
|
||||||
@wraps(f)
|
|
||||||
def decorated_function(*args, **kwargs):
|
|
||||||
current_app.logger.info(
|
|
||||||
"permission_required: %s in %s" % (permission, g.scodoc_dept)
|
|
||||||
)
|
|
||||||
if not current_user.has_permission(permission, g.scodoc_dept):
|
|
||||||
abort(403)
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
|
|
||||||
return decorated_function
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def admin_required(f):
|
|
||||||
return permission_required(Permission.ScoSuperAdmin)(f)
|
|
||||||
|
|
||||||
|
|
||||||
class ZUser(object):
|
class ZUser(object):
|
||||||
"Emulating Zope User"
|
"Emulating Zope User"
|
||||||
|
|
||||||
@ -99,90 +79,118 @@ class ZResponse(object):
|
|||||||
self.headers[header.tolower()] = value
|
self.headers[header.tolower()] = value
|
||||||
|
|
||||||
|
|
||||||
def scodoc7func(func):
|
def permission_required(permission):
|
||||||
|
def decorator(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if "scodoc_dept" in kwargs:
|
||||||
|
g.scodoc_dept = kwargs["scodoc_dept"]
|
||||||
|
del kwargs["scodoc_dept"]
|
||||||
|
current_app.logger.info(
|
||||||
|
"permission_required: %s in %s" % (permission, g.scodoc_dept)
|
||||||
|
)
|
||||||
|
if not current_user.has_permission(permission, g.scodoc_dept):
|
||||||
|
abort(403)
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def admin_required(f):
|
||||||
|
return permission_required(Permission.ScoSuperAdmin)(f)
|
||||||
|
|
||||||
|
|
||||||
|
def scodoc7func(context):
|
||||||
"""Décorateur pour intégrer les fonctions Zope 2 de ScoDoc 7.
|
"""Décorateur pour intégrer les fonctions Zope 2 de ScoDoc 7.
|
||||||
Si on a un kwarg `scodoc_dept`(venant de la route), le stocke dans `g.scodoc_dept`.
|
Si on a un kwarg `scodoc_dept`(venant de la route), le stocke dans `g.scodoc_dept`.
|
||||||
Ajoute l'argument REQUEST s'il est dans la signature de la fonction.
|
Ajoute l'argument REQUEST s'il est dans la signature de la fonction.
|
||||||
Les paramètres de la query string deviennent des (keywords) paramètres de la fonction.
|
Les paramètres de la query string deviennent des (keywords) paramètres de la fonction.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@wraps(func)
|
def s7_decorator(func):
|
||||||
def scodoc7func_decorator(*args, **kwargs):
|
@wraps(func)
|
||||||
"""Decorator allowing legacy Zope published methods to be called via Flask
|
def scodoc7func_decorator(*args, **kwargs):
|
||||||
routes without modification.
|
"""Decorator allowing legacy Zope published methods to be called via Flask
|
||||||
|
routes without modification.
|
||||||
|
|
||||||
There are two cases: the function can be called
|
There are two cases: the function can be called
|
||||||
1. via a Flask route ("top level call")
|
1. via a Flask route ("top level call")
|
||||||
2. or be called directly from Python.
|
2. or be called directly from Python.
|
||||||
|
|
||||||
If called via a route, this decorator setups a REQUEST object (emulating Zope2 REQUEST)
|
If called via a route, this decorator setups a REQUEST object (emulating Zope2 REQUEST)
|
||||||
and `g.scodoc_dept` if present in the argument (for routes like `/<scodoc_dept>/Scolarite/sco_exemple`).
|
and `g.scodoc_dept` if present in the argument (for routes like `/<scodoc_dept>/Scolarite/sco_exemple`).
|
||||||
"""
|
"""
|
||||||
assert not args
|
assert not args
|
||||||
if hasattr(g, "zrequest"):
|
# Détermine si on est appelé via une route ("toplevel")
|
||||||
top_level = False
|
# ou par un appel de fonction python normal.
|
||||||
else:
|
top_level = not hasattr(g, "zrequest")
|
||||||
g.zrequest = None
|
if top_level:
|
||||||
top_level = True
|
g.zrequest = None
|
||||||
#
|
#
|
||||||
if "scodoc_dept" in kwargs:
|
if "scodoc_dept" in kwargs:
|
||||||
g.scodoc_dept = kwargs["scodoc_dept"]
|
g.scodoc_dept = kwargs["scodoc_dept"]
|
||||||
del kwargs["scodoc_dept"]
|
del kwargs["scodoc_dept"]
|
||||||
elif not hasattr(g, "scodoc_dept"): # if toplevel call
|
elif not hasattr(g, "scodoc_dept"): # if toplevel call
|
||||||
g.scodoc_dept = None
|
g.scodoc_dept = None
|
||||||
# --- Emulate Zope's REQUEST
|
# --- Emulate Zope's REQUEST
|
||||||
REQUEST = ZRequest()
|
REQUEST = ZRequest()
|
||||||
g.zrequest = REQUEST
|
g.zrequest = REQUEST
|
||||||
req_args = REQUEST.form # args from query string (get) or form (post)
|
req_args = REQUEST.form # args from query string (get) or form (post)
|
||||||
# --- Add positional arguments
|
# --- Add positional arguments
|
||||||
pos_arg_values = []
|
pos_arg_values = []
|
||||||
# PY3 à remplacer par inspect.getfullargspec en py3:
|
# PY3 à remplacer par inspect.getfullargspec en py3:
|
||||||
argspec = inspect.getargspec(func)
|
argspec = inspect.getargspec(func)
|
||||||
current_app.logger.info("argspec=%s" % str(argspec))
|
current_app.logger.info("argspec=%s" % str(argspec))
|
||||||
nb_default_args = len(argspec.defaults) if argspec.defaults else 0
|
nb_default_args = len(argspec.defaults) if argspec.defaults else 0
|
||||||
if nb_default_args:
|
if nb_default_args:
|
||||||
arg_names = argspec.args[:-nb_default_args]
|
arg_names = argspec.args[:-nb_default_args]
|
||||||
else:
|
|
||||||
arg_names = argspec.args
|
|
||||||
for arg_name in arg_names:
|
|
||||||
if arg_name == "REQUEST": # special case
|
|
||||||
pos_arg_values.append(REQUEST)
|
|
||||||
else:
|
else:
|
||||||
pos_arg_values.append(req_args[arg_name])
|
arg_names = argspec.args
|
||||||
current_app.logger.info("pos_arg_values=%s" % pos_arg_values)
|
for arg_name in arg_names:
|
||||||
# Add keyword arguments
|
|
||||||
if nb_default_args:
|
|
||||||
for arg_name in argspec.args[-nb_default_args:]:
|
|
||||||
if arg_name == "REQUEST": # special case
|
if arg_name == "REQUEST": # special case
|
||||||
kwargs[arg_name] = REQUEST
|
pos_arg_values.append(REQUEST)
|
||||||
elif arg_name in req_args:
|
elif arg_name == "context":
|
||||||
# set argument kw optionnel
|
pos_arg_values.append(context)
|
||||||
kwargs[arg_name] = req_args[arg_name]
|
else:
|
||||||
current_app.logger.info(
|
pos_arg_values.append(req_args[arg_name])
|
||||||
"scodoc7func_decorator: top_level=%s, pos_arg_values=%s, kwargs=%s"
|
current_app.logger.info("pos_arg_values=%s" % pos_arg_values)
|
||||||
% (top_level, pos_arg_values, kwargs)
|
# Add keyword arguments
|
||||||
)
|
if nb_default_args:
|
||||||
value = func(*pos_arg_values, **kwargs)
|
for arg_name in argspec.args[-nb_default_args:]:
|
||||||
|
if arg_name == "REQUEST": # special case
|
||||||
|
kwargs[arg_name] = REQUEST
|
||||||
|
elif arg_name in req_args:
|
||||||
|
# set argument kw optionnel
|
||||||
|
kwargs[arg_name] = req_args[arg_name]
|
||||||
|
current_app.logger.info(
|
||||||
|
"scodoc7func_decorator: top_level=%s, pos_arg_values=%s, kwargs=%s"
|
||||||
|
% (top_level, pos_arg_values, kwargs)
|
||||||
|
)
|
||||||
|
value = func(*pos_arg_values, **kwargs)
|
||||||
|
|
||||||
if not top_level:
|
if not top_level:
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
# Build response, adding collected http headers:
|
# Build response, adding collected http headers:
|
||||||
headers = []
|
headers = []
|
||||||
kw = {"response": value, "status": 200}
|
kw = {"response": value, "status": 200}
|
||||||
if g.zrequest:
|
if g.zrequest:
|
||||||
headers = g.zrequest.RESPONSE.headers
|
headers = g.zrequest.RESPONSE.headers
|
||||||
if not headers:
|
if not headers:
|
||||||
# no customized header, speedup:
|
# no customized header, speedup:
|
||||||
return value
|
return value
|
||||||
if "content-type" in headers:
|
if "content-type" in headers:
|
||||||
kw["mimetype"] = headers["content-type"]
|
kw["mimetype"] = headers["content-type"]
|
||||||
r = flask.Response(**kw)
|
r = flask.Response(**kw)
|
||||||
for h in headers:
|
for h in headers:
|
||||||
r.headers[h] = headers[h]
|
r.headers[h] = headers[h]
|
||||||
return r
|
return r
|
||||||
|
|
||||||
return scodoc7func_decorator
|
return scodoc7func_decorator
|
||||||
|
|
||||||
|
return s7_decorator
|
||||||
|
|
||||||
|
|
||||||
# Le "context" de ScoDoc7
|
# Le "context" de ScoDoc7
|
||||||
@ -193,3 +201,6 @@ class ScoDoc7Context(object):
|
|||||||
|
|
||||||
def __init__(self, globals_dict):
|
def __init__(self, globals_dict):
|
||||||
self.__dict__ = globals_dict
|
self.__dict__ = globals_dict
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "ScoDoc7Context()"
|
||||||
|
@ -14,6 +14,8 @@ from app.main import bp
|
|||||||
|
|
||||||
from app.decorators import scodoc7func, admin_required
|
from app.decorators import scodoc7func, admin_required
|
||||||
|
|
||||||
|
context = None
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/")
|
@bp.route("/")
|
||||||
@bp.route("/index")
|
@bp.route("/index")
|
||||||
@ -47,7 +49,7 @@ D = {"count": 0}
|
|||||||
|
|
||||||
@bp.route("/zopefunction", methods=["POST", "GET"])
|
@bp.route("/zopefunction", methods=["POST", "GET"])
|
||||||
@login_required
|
@login_required
|
||||||
@scodoc7func
|
@scodoc7func(context)
|
||||||
def a_zope_function(y, x="defaut", REQUEST=None):
|
def a_zope_function(y, x="defaut", REQUEST=None):
|
||||||
"""Une fonction typique de ScoDoc7"""
|
"""Une fonction typique de ScoDoc7"""
|
||||||
H = get_request_infos() + [
|
H = get_request_infos() + [
|
||||||
@ -64,7 +66,7 @@ def a_zope_function(y, x="defaut", REQUEST=None):
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/zopeform_get")
|
@bp.route("/zopeform_get")
|
||||||
@scodoc7func
|
@scodoc7func(context)
|
||||||
def a_zope_form_get(REQUEST=None):
|
def a_zope_form_get(REQUEST=None):
|
||||||
H = [
|
H = [
|
||||||
"""<h2>Formulaire GET</h2>
|
"""<h2>Formulaire GET</h2>
|
||||||
@ -81,7 +83,7 @@ def a_zope_form_get(REQUEST=None):
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/zopeform_post")
|
@bp.route("/zopeform_post")
|
||||||
@scodoc7func
|
@scodoc7func(context)
|
||||||
def a_zope_form_post(REQUEST=None):
|
def a_zope_form_post(REQUEST=None):
|
||||||
H = [
|
H = [
|
||||||
"""<h2>Formulaire POST</h2>
|
"""<h2>Formulaire POST</h2>
|
||||||
@ -98,7 +100,7 @@ def a_zope_form_post(REQUEST=None):
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/ScoDoc/<dept_id>/Scolarite/Notes/formsemestre_status")
|
@bp.route("/ScoDoc/<dept_id>/Scolarite/Notes/formsemestre_status")
|
||||||
@scodoc7func
|
@scodoc7func(context)
|
||||||
def formsemestre_status(dept_id=None, formsemestre_id=None, REQUEST=None):
|
def formsemestre_status(dept_id=None, formsemestre_id=None, REQUEST=None):
|
||||||
"""Essai méthode de département
|
"""Essai méthode de département
|
||||||
Le contrôle d'accès doit vérifier les bons rôles : ici Ens<dept_id>
|
Le contrôle d'accès doit vérifier les bons rôles : ici Ens<dept_id>
|
||||||
|
3316
app/scodoc/ZNotes.py
3316
app/scodoc/ZNotes.py
File diff suppressed because it is too large
Load Diff
@ -416,11 +416,6 @@ REQUEST.URL0=%s<br/>
|
|||||||
# GESTION DE LA BD
|
# GESTION DE LA BD
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
security.declareProtected(ScoSuperAdmin, "GetDBConnexionString")
|
|
||||||
|
|
||||||
def GetDBConnexionString(self):
|
|
||||||
# should not be published (but used from contained classes via acquisition)
|
|
||||||
return self._db_cnx_string
|
|
||||||
|
|
||||||
security.declareProtected(ScoSuperAdmin, "GetDBConnexion")
|
security.declareProtected(ScoSuperAdmin, "GetDBConnexion")
|
||||||
GetDBConnexion = ndb.GetDBConnexion
|
GetDBConnexion = ndb.GetDBConnexion
|
||||||
@ -780,7 +775,9 @@ REQUEST.URL0=%s<br/>
|
|||||||
# -------------------------- INFOS SUR ETUDIANTS --------------------------
|
# -------------------------- INFOS SUR ETUDIANTS --------------------------
|
||||||
security.declareProtected(ScoView, "getEtudInfo")
|
security.declareProtected(ScoView, "getEtudInfo")
|
||||||
|
|
||||||
def getEtudInfo(self, etudid=False, code_nip=False, filled=False, REQUEST=None, format=None):
|
def getEtudInfo(
|
||||||
|
self, etudid=False, code_nip=False, filled=False, REQUEST=None, format=None
|
||||||
|
):
|
||||||
"""infos sur un etudiant pour utilisation en Zope DTML
|
"""infos sur un etudiant pour utilisation en Zope DTML
|
||||||
On peut specifier etudid
|
On peut specifier etudid
|
||||||
ou bien cherche dans REQUEST.form: etudid, code_nip, code_ine
|
ou bien cherche dans REQUEST.form: etudid, code_nip, code_ine
|
||||||
|
@ -66,6 +66,12 @@ class FormatError(ScoValueError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ScoConfigurationError(ScoValueError):
|
||||||
|
"""Configuration invalid"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ScoLockedFormError(ScoException):
|
class ScoLockedFormError(ScoException):
|
||||||
def __init__(self, msg="", REQUEST=None):
|
def __init__(self, msg="", REQUEST=None):
|
||||||
msg = (
|
msg = (
|
||||||
|
@ -49,6 +49,8 @@ class Permission:
|
|||||||
def init_permissions():
|
def init_permissions():
|
||||||
for (perm, symbol, description) in _SCO_PERMISSIONS:
|
for (perm, symbol, description) in _SCO_PERMISSIONS:
|
||||||
setattr(Permission, symbol, perm)
|
setattr(Permission, symbol, perm)
|
||||||
|
# Crée aussi les attributs dans le module (ScoDoc7 compat)
|
||||||
|
globals()[symbol] = perm
|
||||||
Permission.description[symbol] = description
|
Permission.description[symbol] = description
|
||||||
Permission.NBITS = len(_SCO_PERMISSIONS)
|
Permission.NBITS = len(_SCO_PERMISSIONS)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% import 'bootstrap/wtf.html' as wtf %}
|
{% import 'bootstrap/wtf.html' as wtf %}
|
||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
<h1>Essais Flask pour ScoDoc 8: accueil</h1>
|
<h1>Protoype ScoDoc 8: accueil</h1>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h2>Avec login requis</h2>
|
<h2>Avec login requis</h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
"""
|
"""
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
|
|
||||||
|
scolar_bp = Blueprint("scolar", __name__)
|
||||||
notes_bp = Blueprint("notes", __name__)
|
notes_bp = Blueprint("notes", __name__)
|
||||||
|
absences_bp = Blueprint("absences", __name__)
|
||||||
|
essais_bp = Blueprint("essais", __name__)
|
||||||
|
|
||||||
from app.views import notes
|
from app.views import notes, scolar, absences
|
37
app/views/absences.py
Normal file
37
app/views/absences.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Module absences: issu de ScoDoc7 / ZAbsences.py
|
||||||
|
|
||||||
|
Emmanuel Viennet, 2021
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import g
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from app.decorators import (
|
||||||
|
scodoc7func,
|
||||||
|
ScoDoc7Context,
|
||||||
|
permission_required,
|
||||||
|
admin_required,
|
||||||
|
login_required,
|
||||||
|
)
|
||||||
|
from app.auth.models import Permission
|
||||||
|
|
||||||
|
from app.views import absences_bp as bp
|
||||||
|
|
||||||
|
context = ScoDoc7Context(globals())
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/")
|
||||||
|
@scodoc7func(context)
|
||||||
|
def index_html():
|
||||||
|
"""Un exemple de fonction ScoDoc 7 dans ZAbsences"""
|
||||||
|
return """<html>
|
||||||
|
<body><h1>ScoDoc 8 ZAbsences !</h1>
|
||||||
|
<p>ZAbsences ScoDoc 8</p>
|
||||||
|
<p>g.scodoc_dept=%(scodoc_dept)s</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""" % {
|
||||||
|
"scodoc_dept": g.scodoc_dept,
|
||||||
|
}
|
65
app/views/essais.py
Normal file
65
app/views/essais.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# -*- coding: UTF-8 -*
|
||||||
|
"""
|
||||||
|
Module Essais: divers essais pour la migration vers Flask
|
||||||
|
|
||||||
|
Emmanuel Viennet, 2021
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import g
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from app.decorators import (
|
||||||
|
scodoc7func,
|
||||||
|
ScoDoc7Context,
|
||||||
|
permission_required,
|
||||||
|
admin_required,
|
||||||
|
login_required,
|
||||||
|
)
|
||||||
|
from app.auth.models import Permission
|
||||||
|
|
||||||
|
from app.views import notes_bp as bp
|
||||||
|
|
||||||
|
# import sco_core deviendra:
|
||||||
|
from app.ScoDoc import sco_core
|
||||||
|
|
||||||
|
context = ScoDoc7Context(globals())
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/<scodoc_dept>/Scolarite/sco_exemple")
|
||||||
|
@scodoc7func(context)
|
||||||
|
def sco_exemple(etudid="NON"):
|
||||||
|
"""Un exemple de fonction ScoDoc 7"""
|
||||||
|
return """<html>
|
||||||
|
<body><h1>ScoDoc 7 rules !</h1>
|
||||||
|
<p>etudid=%(etudid)s</p>
|
||||||
|
<p>g.scodoc_dept=%(scodoc_dept)s</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""" % {
|
||||||
|
"etudid": etudid,
|
||||||
|
"scodoc_dept": g.scodoc_dept,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# En ScoDoc 7, on a souvent des vues qui en appellent d'autres
|
||||||
|
# avec context.sco_exemple( etudid="E12" )
|
||||||
|
@bp.route("/<scodoc_dept>/Scolarite/sco_exemple2")
|
||||||
|
@login_required
|
||||||
|
@scodoc7func(context)
|
||||||
|
def sco_exemple2():
|
||||||
|
return "Exemple 2" + context.sco_exemple(etudid="deux")
|
||||||
|
|
||||||
|
|
||||||
|
# Test avec un seul argument REQUEST positionnel
|
||||||
|
@bp.route("/<scodoc_dept>/Scolarite/sco_get_version")
|
||||||
|
@scodoc7func(context)
|
||||||
|
def sco_get_version(REQUEST):
|
||||||
|
return sco_core.sco_get_version(REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
# Fonction ressemblant à une méthode Zope protégée
|
||||||
|
@bp.route("/<scodoc_dept>/Scolarite/sco_test_view")
|
||||||
|
@scodoc7func(context)
|
||||||
|
@permission_required(Permission.ScoView)
|
||||||
|
def sco_test_view(REQUEST=None):
|
||||||
|
return """Vous avez vu sco_test_view !"""
|
3548
app/views/notes.py
3548
app/views/notes.py
File diff suppressed because it is too large
Load Diff
37
app/views/scolar.py
Normal file
37
app/views/scolar.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Module scolar: issu de ScoDoc7 / ZScolar.py
|
||||||
|
|
||||||
|
Emmanuel Viennet, 2021
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import g
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from app.decorators import (
|
||||||
|
scodoc7func,
|
||||||
|
ScoDoc7Context,
|
||||||
|
permission_required,
|
||||||
|
admin_required,
|
||||||
|
login_required,
|
||||||
|
)
|
||||||
|
from app.auth.models import Permission
|
||||||
|
|
||||||
|
from app.views import scolar_bp as bp
|
||||||
|
|
||||||
|
context = ScoDoc7Context(globals())
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/about")
|
||||||
|
@scodoc7func(context)
|
||||||
|
def about():
|
||||||
|
"""Un exemple de fonction ScoDoc 7 dans ZScolar"""
|
||||||
|
return """<html>
|
||||||
|
<body><h1>ScoDoc 7 ZScolar !</h1>
|
||||||
|
<p>ZScolar ScoDoc 8</p>
|
||||||
|
<p>g.scodoc_dept=%(scodoc_dept)s</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""" % {
|
||||||
|
"scodoc_dept": g.scodoc_dept,
|
||||||
|
}
|
20
config.py
Normal file → Executable file
20
config.py
Normal file → Executable file
@ -7,12 +7,13 @@ BASEDIR = os.path.abspath(os.path.dirname(__file__))
|
|||||||
load_dotenv(os.path.join(BASEDIR, ".env"))
|
load_dotenv(os.path.join(BASEDIR, ".env"))
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
class ConfigClass(object):
|
||||||
"""General configution. Mostly loaded from environment via .env"""
|
"""General configuration. Mostly loaded from environment via .env"""
|
||||||
|
|
||||||
SECRET_KEY = os.environ.get("SECRET_KEY") or "un-grand-secret-introuvable"
|
SECRET_KEY = os.environ.get("SECRET_KEY") or "un-grand-secret-introuvable"
|
||||||
SQLALCHEMY_DATABASE_URI = (
|
SQLALCHEMY_DATABASE_URI = (
|
||||||
os.environ.get("DATABASE_URL") or "postgresql://scodoc@localhost:5432/SCO8USERS"
|
os.environ.get("USERS_DATABASE_URI")
|
||||||
|
or "postgresql://scodoc@localhost:5432/SCO8USERS"
|
||||||
)
|
)
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
LOG_TO_STDOUT = os.environ.get("LOG_TO_STDOUT")
|
LOG_TO_STDOUT = os.environ.get("LOG_TO_STDOUT")
|
||||||
@ -28,4 +29,15 @@ class Config(object):
|
|||||||
SCODOC_ERR_MAIL = os.environ.get("SCODOC_ERR_MAIL")
|
SCODOC_ERR_MAIL = os.environ.get("SCODOC_ERR_MAIL")
|
||||||
BOOTSTRAP_SERVE_LOCAL = os.environ.get("BOOTSTRAP_SERVE_LOCAL")
|
BOOTSTRAP_SERVE_LOCAL = os.environ.get("BOOTSTRAP_SERVE_LOCAL")
|
||||||
# for ScoDoc 7 compat (à changer)
|
# for ScoDoc 7 compat (à changer)
|
||||||
INSTANCE_HOME = os.environ.get("INSTANCE_HOME", "/opt/scodoc")
|
INSTANCE_HOME = os.environ.get("INSTANCE_HOME", "/opt/scodoc")
|
||||||
|
|
||||||
|
# For legacy ScoDoc7 installs: postgresql user
|
||||||
|
SCODOC7_SQL_USER = os.environ.get("SCODOC7_SQL_USER", "www-data")
|
||||||
|
DEFAULT_SQL_PORT = os.environ.get("DEFAULT_SQL_PORT", "5432")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Used to build some config variable at startup time"""
|
||||||
|
self.SCODOC_VAR_DIR = os.path.join(self.INSTANCE_HOME, "var", "scodoc")
|
||||||
|
|
||||||
|
|
||||||
|
Config = ConfigClass()
|
||||||
|
@ -15,6 +15,7 @@ Flask-Migrate==2.7.0
|
|||||||
Flask-Moment==0.11.0
|
Flask-Moment==0.11.0
|
||||||
Flask-SQLAlchemy==2.4.4
|
Flask-SQLAlchemy==2.4.4
|
||||||
Flask-WTF==0.14.3
|
Flask-WTF==0.14.3
|
||||||
|
icalendar==4.0.7
|
||||||
idna==2.10
|
idna==2.10
|
||||||
itsdangerous==1.1.0
|
itsdangerous==1.1.0
|
||||||
jaxml==3.2
|
jaxml==3.2
|
||||||
@ -24,13 +25,17 @@ MarkupSafe==1.1.1
|
|||||||
Pillow==6.2.2
|
Pillow==6.2.2
|
||||||
pkg-resources==0.0.0
|
pkg-resources==0.0.0
|
||||||
psycopg2==2.8.6
|
psycopg2==2.8.6
|
||||||
|
pyExcelerator==0.6.3a0
|
||||||
PyJWT==1.7.1
|
PyJWT==1.7.1
|
||||||
|
PyRSS2Gen==1.1
|
||||||
python-dateutil==2.8.1
|
python-dateutil==2.8.1
|
||||||
python-dotenv==0.15.0
|
python-dotenv==0.15.0
|
||||||
python-editor==1.0.4
|
python-editor==1.0.4
|
||||||
pytz==2021.1
|
pytz==2021.1
|
||||||
|
reportlab==3.5.59
|
||||||
six==1.15.0
|
six==1.15.0
|
||||||
SQLAlchemy==1.3.23
|
SQLAlchemy==1.3.23
|
||||||
|
stripogram==1.5
|
||||||
typing==3.7.4.3
|
typing==3.7.4.3
|
||||||
visitor==0.1.3
|
visitor==0.1.3
|
||||||
Werkzeug==1.0.1
|
Werkzeug==1.0.1
|
||||||
|
70
scodoc_manager.py
Normal file
70
scodoc_manager.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# -*- coding: UTF-8 -*
|
||||||
|
|
||||||
|
"""
|
||||||
|
Manage departments, databases.
|
||||||
|
|
||||||
|
Each departement `X` has its own database, `SCOX`
|
||||||
|
and a small configuration file `.../config/depts/X.cfg`
|
||||||
|
containing the database URI
|
||||||
|
Old ScoDoc7 installs config files contained `dbname=SCOX`
|
||||||
|
which translates as
|
||||||
|
"postgresql://<user>@localhost:5432/<dbname>"
|
||||||
|
<user> being given by `SCODOC7_SQL_USER` env variable.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import glob
|
||||||
|
|
||||||
|
from config import Config
|
||||||
|
|
||||||
|
from app.scodoc.sco_exceptions import ScoConfigurationError
|
||||||
|
|
||||||
|
|
||||||
|
class ScoDeptDescription:
|
||||||
|
def __init__(self, filename):
|
||||||
|
"""Read dept description from dept file"""
|
||||||
|
if os.path.split(filename)[1][-4:] != ".cfg":
|
||||||
|
raise ScoConfigurationError("Invalid dept config filename: %s" % filename)
|
||||||
|
self.dept_id = os.path.split(filename)[1][:-4]
|
||||||
|
if not self.dept_id:
|
||||||
|
raise ScoConfigurationError("Invalid dept config filename: %s" % filename)
|
||||||
|
try:
|
||||||
|
db_uri = open(filename).read().strip()
|
||||||
|
except:
|
||||||
|
raise ScoConfigurationError("Department config file missing: %s" % filename)
|
||||||
|
m = re.match(r"dbname=SCO([a-zA-Z0-9]+$)", db_uri)
|
||||||
|
if m:
|
||||||
|
# ScoDoc7 backward compat
|
||||||
|
dept = m.group(1) # unused in ScoDoc7
|
||||||
|
db_name = "SCO" + self.dept_id.upper()
|
||||||
|
db_uri = "postgresql://%(db_user)s@localhost:%(db_port)s/%(db_name)s" % {
|
||||||
|
"db_user": Config.SCODOC7_SQL_USER,
|
||||||
|
"db_name": db_name,
|
||||||
|
"db_port": Config.DEFAULT_SQL_PORT,
|
||||||
|
}
|
||||||
|
self.db_uri = db_uri
|
||||||
|
|
||||||
|
|
||||||
|
class ScoDocManager:
|
||||||
|
def __init__(self):
|
||||||
|
filenames = glob.glob(Config.SCODOC_VAR_DIR + "/config/depts/*.cfg")
|
||||||
|
descr_list = [ScoDeptDescription(f) for f in filenames]
|
||||||
|
self.dept_descriptions = {d.dept_id: d for d in descr_list}
|
||||||
|
|
||||||
|
def get_dept_db_uri(self, dept_id):
|
||||||
|
"DB URI for this dept id"
|
||||||
|
return self.dept_descriptions[dept_id].db_uri
|
||||||
|
|
||||||
|
def get_dept_ids(self):
|
||||||
|
"get (unsorted) dept ids"
|
||||||
|
return [d.dept_id for d in descr_list]
|
||||||
|
|
||||||
|
def get_db_uri(self):
|
||||||
|
"""
|
||||||
|
Returns DB URI for the "current" departement.
|
||||||
|
Replaces ScoDoc7 GetDBConnexionString()
|
||||||
|
"""
|
||||||
|
return self.get_dept_db_uri(g.scodoc_dept)
|
||||||
|
|
||||||
|
|
||||||
|
sco_mgr = ScoDocManager()
|
Loading…
Reference in New Issue
Block a user