1
0
forked from ScoDoc/ScoDoc

Base multi-départements. En cours

This commit is contained in:
Emmanuel Viennet 2021-08-13 00:34:58 +02:00
parent 317d60d447
commit 486f20d7f7
34 changed files with 471 additions and 110 deletions

View File

@ -23,9 +23,6 @@ from flask_caching import Cache
from config import DevConfig
from app.scodoc import notesdb as ndb
from app.scodoc import sco_cache
db = SQLAlchemy()
migrate = Migrate()
login = LoginManager()
@ -131,6 +128,18 @@ def create_app(config_class=DevConfig):
return app
def set_sco_dept(scodoc_dept: str):
"""Set global g object to given dept and open db connection if needed"""
# Check that dept exists
dept = Departement.query.filter_by(acronym=scodoc_dept).first()
if not dept:
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"):
ndb.open_db_connection()
def user_db_init():
"""Initialize the users database."""
from app.auth.models import User, Role
@ -214,8 +223,14 @@ def clear_scodoc_cache():
r = redis.Redis()
r.flushall()
# Also clear local caches:
sco_preferences.clear_base_preferences()
from app.models import Departement
from app.scodoc import notesdb as ndb, sco_preferences
from app.scodoc import sco_cache
# admin_role = Role.query.filter_by(name="SuperAdmin").first()
# if admin_role:
# admin = (

View File

@ -98,10 +98,37 @@ class ZResponse(object):
self.headers[header.lower()] = value
def scodoc(func):
"""Décorateur pour toutes les fonctions ScoDoc
Affecte le département à g
et ouvre la connexion à la base
Set `g.scodoc_dept` and `g.scodoc_dept_id` if `scodoc_dept` is present
in the argument (for routes like
`/<scodoc_dept>/Scolarite/sco_exemple`).
"""
@wraps(func)
def scodoc_function(*args, **kwargs):
if "scodoc_dept" in kwargs:
dept_acronym = kwargs["scodoc_dept"]
current_app.logger.info("setting dept to " + dept_acronym)
app.set_sco_dept(dept_acronym)
del kwargs["scodoc_dept"]
elif not hasattr(g, "scodoc_dept"):
current_app.logger.info("setting dept to None")
g.scodoc_dept = None
g.scodoc_dept_id = -1 # invalide
return func(*args, **kwargs)
return scodoc_function
def permission_required(permission):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# current_app.logger.info("PERMISSION; kwargs=%s" % str(kwargs))
if "scodoc_dept" in kwargs:
g.scodoc_dept = kwargs["scodoc_dept"]
del kwargs["scodoc_dept"]
@ -142,7 +169,6 @@ def scodoc7func(context):
2. or be called directly from Python.
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`).
"""
# Détermine si on est appelé via une route ("toplevel")
# ou par un appel de fonction python normal.
@ -150,14 +176,6 @@ def scodoc7func(context):
if not top_level:
# ne "redécore" pas
return func(*args, **kwargs)
#
if "scodoc_dept" in kwargs:
g.scodoc_dept = kwargs["scodoc_dept"]
del kwargs["scodoc_dept"]
elif not hasattr(g, "scodoc_dept"):
g.scodoc_dept = None
# --- Open DB connection
app.views.open_dept_db_connection()
# --- Emulate Zope's REQUEST
REQUEST = ZRequest()
g.zrequest = REQUEST

View File

@ -11,6 +11,9 @@ APO_CODE_STR_LEN = 16 # nb de car max d'un code Apogée
from app.models.raw_sql_init import create_database_functions
from app.models.absences import Absence, AbsenceNotification, BilletAbsence
from app.models.departements import Departement
from app.models.entreprises import (
Entreprise,
EntrepriseCorrespondant,
@ -56,4 +59,4 @@ from app.models.notes import (
NotesNotes,
NotesNotesLog,
)
from app.models.preferences import ScoPreferences
from app.models.preferences import ScoPreference

View File

@ -0,0 +1,36 @@
# -*- coding: UTF-8 -*
"""ScoDoc8 models : departements
"""
from typing import Any
from app import db
from app.models import SHORT_STR_LEN
class Departement(db.Model):
"""Un département ScoDoc"""
id = db.Column(db.Integer, primary_key=True)
acronym = db.Column(db.String(SHORT_STR_LEN), nullable=False, index=True)
description = db.Column(db.Text())
date_creation = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
visible = db.Column(
db.Boolean(), nullable=False, default=True, server_default="true"
) # sur page d'accueil
entreprises = db.relationship("Entreprise", lazy="dynamic", backref="departement")
etudiants = db.relationship("Identite", lazy="dynamic", backref="departement")
formations = db.relationship(
"NotesFormation", lazy="dynamic", backref="departement"
)
formsemestres = db.relationship(
"FormSemestre", lazy="dynamic", backref="departement"
)
preferences = db.relationship(
"ScoPreference", lazy="dynamic", backref="departement"
)
semsets = db.relationship("NotesSemSet", lazy="dynamic", backref="departement")
def __repr__(self):
return f"<Departement {self.acronym}>"

View File

@ -15,7 +15,7 @@ class Entreprise(db.Model):
__tablename__ = "entreprises"
id = db.Column(db.Integer, primary_key=True)
entreprise_id = db.synonym("id")
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True)
nom = db.Column(db.Text)
adresse = db.Column(db.Text)
ville = db.Column(db.Text)

View File

@ -17,6 +17,7 @@ class Identite(db.Model):
id = db.Column(db.Integer, primary_key=True)
etudid = db.synonym("id")
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True)
nom = db.Column(db.Text())
prenom = db.Column(db.Text())

View File

@ -15,6 +15,8 @@ class NotesFormation(db.Model):
id = db.Column(db.Integer, primary_key=True)
formation_id = db.synonym("id")
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True)
acronyme = db.Column(db.String(SHORT_STR_LEN), nullable=False)
titre = db.Column(db.Text(), nullable=False)
titre_officiel = db.Column(db.Text(), nullable=False)
@ -28,6 +30,8 @@ class NotesFormation(db.Model):
type_parcours = db.Column(db.Integer, default=0, server_default="0")
code_specialite = db.Column(db.String(SHORT_STR_LEN))
formsemestres = db.relationship("FormSemestre", lazy="dynamic", backref="formation")
class NotesUE(db.Model):
"""Unité d'Enseignement"""
@ -106,6 +110,8 @@ class NotesTag(db.Model):
__tablename__ = "notes_tags"
id = db.Column(db.Integer, primary_key=True)
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True)
tag_id = db.synonym("id")
title = db.Column(db.String(SHORT_STR_LEN), nullable=False, unique=True)

View File

@ -19,6 +19,9 @@ class FormSemestre(db.Model):
id = db.Column(db.Integer, primary_key=True)
formsemestre_id = db.synonym("id")
# dept_id est aussi dans la formation, ajpouté ici pour
# simplifier et accélérer les selects dans notesdb
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True)
formation_id = db.Column(db.Integer, db.ForeignKey("notes_formations.id"))
semestre_id = db.Column(db.Integer, nullable=False, default=1, server_default="1")
titre = db.Column(db.Text())
@ -309,6 +312,8 @@ class NotesSemSet(db.Model):
id = db.Column(db.Integer, primary_key=True)
semset_id = db.synonym("id")
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"))
title = db.Column(db.Text)
annee_scolaire = db.Column(db.Integer, nullable=True, default=None)
# periode: 0 (année), 1 (Simpair), 2 (Spair)

View File

@ -2,17 +2,18 @@
"""Model : preferences
"""
from app import db
class ScoPreferences(db.Model):
class ScoPreference(db.Model):
"""ScoDoc preferences"""
__tablename__ = "sco_prefs"
id = db.Column(db.Integer, primary_key=True)
pref_id = db.synonym("id")
dept = db.Column(db.String(16), index=True)
name = db.Column(db.String(128), nullable=False)
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"))
name = db.Column(db.String(128), nullable=False, index=True)
value = db.Column(db.Text())
formsemestre_id = db.Column(db.Integer, db.ForeignKey("notes_formsemestre.id"))

View File

@ -7,8 +7,9 @@ import psycopg2
import psycopg2.pool
import psycopg2.extras
from flask import g
from flask import g, current_app
import app
import app.scodoc.sco_utils as scu
from app.scodoc.notes_log import log
from app.scodoc.sco_exceptions import ScoException, ScoValueError, NoteProcessError
@ -33,28 +34,17 @@ def unquote(s):
return s.replace("&amp;", "&")
def open_dept_connection(scodoc_dept=None):
"""Open a connection to the current dept db (g.scodoc_dept)
or to the argument scodoc_dept
"""
return psycopg2.connect(scu.get_db_cnx_string(scodoc_dept))
def open_db_connection():
"""Open a connection to the database"""
g.db_conn = psycopg2.connect(current_app.config["SQLALCHEMY_DATABASE_URI"])
def close_dept_connection():
"""Commit and close dept db."""
# log("close_dept_connection to " + scu.get_db_cnx_string())
g.db_conn.commit()
g.db_conn.close()
def set_sco_dept(scodoc_dept):
"""Set "context" to given dept
open db connection
"""
g.scodoc_dept = scodoc_dept
def close_db_connection():
"""Commit and close database."""
if hasattr(g, "db_conn"):
close_dept_connection()
g.db_conn = open_dept_connection()
g.db_conn.commit()
g.db_conn.close()
del g.db_conn
def GetDBConnexion(autocommit=True): # on n'utilise plus autocommit
@ -277,6 +267,7 @@ class EditableTable(object):
html_quote=True,
fields_creators={}, # { field : [ sql_command_to_create_it ] }
filter_nulls=True, # dont allow to set fields to null
filter_dept=False, # ajoute selection sur g.scodoc_dept_id
):
self.table_name = table_name
self.id_name = id_name
@ -295,6 +286,7 @@ class EditableTable(object):
self.html_quote = html_quote
self.fields_creators = fields_creators
self.filter_nulls = filter_nulls
self.filter_dept = filter_dept
self.sql_default_values = None
def create(self, cnx, args):
@ -304,6 +296,8 @@ class EditableTable(object):
del vals[self.id_name]
if "id" in vals:
del vals["id"]
if self.filter_dept:
vals["dept_id"] = g.scodoc_dept_id
if self.html_quote:
quote_dict(vals) # quote all HTML markup
# format value
@ -334,6 +328,8 @@ class EditableTable(object):
vals = dictfilter(args, self.dbfields, self.filter_nulls)
if (id_value is not None) and (not "id" in vals):
vals["id"] = id_value
if self.filter_dept:
vals["dept_id"] = g.scodoc_dept_id
if not sortkey:
sortkey = self.sortkey
res = DBSelectArgs(

View File

@ -118,7 +118,10 @@ def abs_notify_send(
msg["To"] = email
sco_emails.sendEmail(context, msg)
ndb.SimpleQuery(
"""insert into absences_notifications (etudid, email, nbabs, nbabsjust, formsemestre_id) values (%(etudid)s, %(email)s, %(nbabs)s, %(nbabsjust)s, %(formsemestre_id)s)""",
"""insert into absences_notifications
(etudid, email, nbabs, nbabsjust, formsemestre_id)
VALUES (%(etudid)s, %(email)s, %(nbabs)s, %(nbabsjust)s, %(formsemestre_id)s)
""",
vars(),
cursor=cursor,
)

View File

@ -138,9 +138,18 @@ class EvaluationCache(ScoDocCache):
@classmethod
def invalidate_all_sems(cls):
"delete all evaluations from cache"
"delete all evaluations in current dept from cache"
evaluation_ids = [
x[0] for x in ndb.SimpleQuery("SELECT id FROM notes_evaluation", "")
x[0]
for x in ndb.SimpleQuery(
"""SELECT e.id
FROM notes_evaluation e, notes_moduleimpl mi, notes_formsemestre s
WHERE s.dept_id=%(dept_id)s
AND s.id = mi.formsemestre_id
AND mi.id = e.moduleimpl_id;
""",
{"dept_id": g.scodoc_dept_id},
)
]
cls.delete_many(evaluation_ids)
@ -243,7 +252,13 @@ def invalidate_formsemestre( # was inval_cache( context, formsemestre_id=None,
# clear all caches
log("----- invalidate_formsemestre: clearing all caches -----")
formsemestre_ids = [
x[0] for x in ndb.SimpleQuery("SELECT id FROM notes_formsemestre", "")
x[0]
for x in ndb.SimpleQuery(
"""SELECT id FROM notes_formsemestre s
WHERE s.dept_id=%(dept_id)s
""",
{"dept_id": g.scodoc_dept_id},
)
]
else:
formsemestre_ids = [

View File

@ -65,7 +65,7 @@ SCO_DUMP_LOCK = "/tmp/scodump.lock"
def sco_dump_and_send_db(context, REQUEST=None):
"""Dump base de données du département courant et l'envoie anonymisée pour debug"""
"""Dump base de données et l'envoie anonymisée pour debug"""
H = [html_sco_header.sco_header(page_title="Assistance technique")]
# get currect (dept) DB name:
cursor = ndb.SimpleQuery("SELECT current_database()", {})
@ -75,7 +75,7 @@ def sco_dump_and_send_db(context, REQUEST=None):
try:
x = open(SCO_DUMP_LOCK, "w+")
fcntl.flock(x, fcntl.LOCK_EX | fcntl.LOCK_NB)
except (IOError, OSError): # exception changed from Python 2 to 3
except (IOError, OSError):
raise ScoValueError(
"Un envoi de la base "
+ db_name

View File

@ -148,6 +148,7 @@ _entreprisesEditor = EntreprisesEditor(
"note",
"date_creation",
),
filter_dept=True,
sortkey="nom",
input_formators={
"nom": _format_nom,

View File

@ -258,6 +258,7 @@ _identiteEditor = ndb.EditableTable(
"code_ine",
"code_nip",
),
filter_dept=True,
sortkey="nom",
input_formators={
"nom": force_uppercase,

View File

@ -32,6 +32,7 @@ from flask import url_for, g
from flask_login import current_user
from scodoc_manager import sco_mgr
import app
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc.gen_tables import GenTable
@ -48,7 +49,7 @@ def form_search_etud(
dest_url=None,
parameters=None,
parameters_keys=None,
title="Rechercher un &eacute;tudiant par nom&nbsp;: ",
title="Rechercher un étudiant par nom&nbsp;: ",
add_headers=False, # complete page
):
"form recherche par nom"
@ -274,7 +275,7 @@ def search_etud_in_accessible_depts(expnom=None, code_nip=None):
if current_user.has_permission(Permission.ScoView, dept=dept):
if expnom or code_nip:
accessible_depts.append(dept)
ndb.set_sco_dept(dept)
app.set_sco_dept(dept)
etuds = search_etuds_infos(expnom=expnom, code_nip=code_nip)
else:
etuds = []

View File

@ -64,6 +64,7 @@ _formationEditor = ndb.EditableTable(
"type_parcours",
"code_specialite",
),
filter_dept=True,
sortkey="acronyme",
)

View File

@ -65,6 +65,7 @@ _formsemestreEditor = ndb.EditableTable(
"elt_sem_apo",
"elt_annee_apo",
),
filter_dept=True,
sortkey="date_debut",
output_formators={
"date_debut": ndb.DateISOtoDMY,

View File

@ -113,6 +113,7 @@ get_base_preferences(formsemestre_id)
import flask
from flask import g
from app.models import Departement
from app.scodoc import sco_cache
from app.scodoc.notes_log import log
from app.scodoc.sco_exceptions import ScoValueError, ScoException
@ -120,15 +121,22 @@ from app.scodoc.TrivialFormulator import TrivialFormulator
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
_SCO_BASE_PREFERENCES = {} # { URL: BasePreferences instance }
_SCO_BASE_PREFERENCES = {} # { dept_acronym: BasePreferences instance }
def clear_base_preferences():
"""Clear cached preferences"""
# usefull only for tests, where the same process may run
# successively on several databases
_SCO_BASE_PREFERENCES.clear()
def get_base_preferences():
"""Return global preferences for the current department"""
dept = g.scodoc_dept
if not dept in _SCO_BASE_PREFERENCES:
_SCO_BASE_PREFERENCES[dept] = BasePreferences()
return _SCO_BASE_PREFERENCES[dept]
dept_acronym = g.scodoc_dept
if not dept_acronym in _SCO_BASE_PREFERENCES:
_SCO_BASE_PREFERENCES[dept_acronym] = BasePreferences(dept_acronym)
return _SCO_BASE_PREFERENCES[dept_acronym]
def get_preference(name, formsemestre_id=None):
@ -198,7 +206,8 @@ class BasePreferences(object):
_editor = ndb.EditableTable(
"sco_prefs",
"pref_id",
("pref_id", "name", "value", "formsemestre_id"),
("pref_id", "dept_id", "name", "value", "formsemestre_id"),
filter_dept=True,
sortkey="name",
convert_null_outputs_to_empty=False,
# allow_set_id=True, #sco8
@ -206,7 +215,11 @@ class BasePreferences(object):
filter_nulls=False,
)
def __init__(self):
def __init__(self, dept_acronym: str):
dept = Departement.query.filter_by(acronym=dept_acronym).first()
if not dept:
raise ScoValueError(f"Invalid departement: {dept_acronym}")
self.dept_id = dept.id
self.init()
self.load()
@ -1785,11 +1798,11 @@ class BasePreferences(object):
def load(self):
"""Load all preferences from db"""
log("loading preferences")
log(f"loading preferences for dept_id={self.dept_id}")
try:
scu.GSL.acquire()
cnx = ndb.GetDBConnexion()
preflist = self._editor.list(cnx)
preflist = self._editor.list(cnx, {"dept_id": self.dept_id})
self.prefs = {None: {}} # { formsemestre_id (or None) : { name : value } }
self.default = {} # { name : default_value }
for p in preflist:
@ -1850,7 +1863,9 @@ class BasePreferences(object):
self.prefs[None][name] = value
log("creating missing preference for %s=%s" % (name, value))
# add to db table
self._editor.create(cnx, {"name": name, "value": value})
self._editor.create(
cnx, {"dept_id": self.dept_id, "name": name, "value": value}
)
finally:
scu.GSL.release()
@ -1898,14 +1913,20 @@ class BasePreferences(object):
value = "1" if value else "0"
# existe deja ?
pdb = self._editor.list(
cnx, args={"formsemestre_id": formsemestre_id, "name": name}
cnx,
args={
"dept_id": self.dept_id,
"formsemestre_id": formsemestre_id,
"name": name,
},
)
if not pdb:
# cree preference
# crée préférence
log("create pref sem=%s %s=%s" % (formsemestre_id, name, value))
self._editor.create(
cnx,
{
"dept_id": self.dept_id,
"name": name,
"value": value,
"formsemestre_id": formsemestre_id,
@ -1959,6 +1980,7 @@ class BasePreferences(object):
)
if pdb:
log("deleting pref sem=%s %s" % (formsemestre_id, name))
assert pdb[0]["dept_id"] == self.dept_id
self._editor.delete(cnx, pdb[0]["pref_id"])
sco_cache.invalidate_formsemestre() # > modif preferences
finally:

View File

@ -40,6 +40,7 @@ sem_set_list(context)
"""
import flask
from flask import g
from app.scodoc import html_sco_header
from app.scodoc import sco_cache
@ -58,7 +59,10 @@ import app.scodoc.sco_utils as scu
_semset_editor = ndb.EditableTable(
"notes_semset", "semset_id", ("semset_id", "title", "annee_scolaire", "sem_id")
"notes_semset",
"semset_id",
("semset_id", "title", "annee_scolaire", "sem_id"),
filter_dept=True,
)
semset_create = _semset_editor.create
@ -156,14 +160,24 @@ class SemSet(dict):
)
ndb.SimpleQuery(
"INSERT INTO notes_semset_formsemestre (formsemestre_id, semset_id) VALUES (%(formsemestre_id)s, %(semset_id)s)",
{"formsemestre_id": formsemestre_id, "semset_id": self.semset_id},
"""INSERT INTO notes_semset_formsemestre
(dept_id, id, semset_id)
VALUES (%(dept_id)s, %(formsemestre_id)s, %(semset_id)s)
""",
{
"dept_id": g.scodoc_dept_id,
"formsemestre_id": formsemestre_id,
"semset_id": self.semset_id,
},
)
self.load_sems() # update our list
def remove(self, formsemestre_id):
ndb.SimpleQuery(
"DELETE FROM notes_semset_formsemestre WHERE semset_id=%(semset_id)s AND formsemestre_id=%(formsemestre_id)s",
"""DELETE FROM notes_semset_formsemestre
WHERE id=%(semset_id)s
AND formsemestre_id=%(formsemestre_id)s
""",
{"formsemestre_id": formsemestre_id, "semset_id": self.semset_id},
)
self.load_sems() # update our list

View File

@ -75,8 +75,8 @@ class ScoTag(object):
r = ndb.SimpleDictFetch(
"SELECT id as tag_id, * FROM "
+ self.tag_table
+ " WHERE title = %(title)s",
{"title": self.title},
+ " WHERE dept_id=%(dept_id) AND title = %(title)s",
{"dept_id": g.scodoc_dept_id, "title": self.title},
)
if r:
self.tag_id = r[0]["tag_id"]
@ -182,8 +182,8 @@ class ModuleTag(ScoTag):
r = ndb.SimpleDictFetch(
"""SELECT mt.module_id
FROM notes_modules_tags mt, notes_modules m, notes_formations f
WHERE mt.tag_id = %(tag_id)s
AND m.id = mt.module_id
WHERE mt.tag_id = %(tag_id)s
AND m.id = mt.module_id
AND m.formation_id = f.id
AND f.formation_code = %(formation_code)s
""",

View File

@ -52,7 +52,7 @@ STRING_TYPES = six.string_types
from PIL import Image as PILImage
from flask import g, url_for, request, current_app
from flask import g, url_for, request
from scodoc_manager import sco_mgr
@ -319,19 +319,10 @@ BULLETINS_VERSIONS = ("short", "selectedevals", "long")
# Support for ScoDoc7 compatibility
def get_dept_id():
"acronyme du dept courant"
return g.scodoc_dept # en scodoc 8.1 #sco8
# if g.scodoc_dept in sco_mgr.get_dept_ids():
# return g.scodoc_dept
# raise sco_exceptions.ScoInvalidDept("département invalide: %s" % g.scodoc_dept)
def get_db_cnx_string(scodoc_dept=None):
return current_app.config["SQLALCHEMY_DATABASE_URI"]
# return "dbname=SCO" + (scodoc_dept or g.scodoc_dept)
def ScoURL():
"""base URL for this sco instance.
e.g. https://scodoc.xxx.fr/ScoDoc/DEPT/Scolarite

View File

@ -2,7 +2,7 @@
"""ScoDoc Flask views
"""
from flask import Blueprint
from flask import g
from flask import g, current_app
from app.scodoc import notesdb as ndb
scodoc_bp = Blueprint("scodoc", __name__)
@ -12,19 +12,18 @@ users_bp = Blueprint("users", __name__)
absences_bp = Blueprint("absences", __name__)
essais_bp = Blueprint("essais", __name__)
from app.views import scodoc, notes, scolar, absences, users, essais
# Cette fonction est bien appelée avant toutes les requêtes
# de tous les blueprints
# mais apparemment elle n'a pas acces aux arguments
@scodoc_bp.before_app_request
def open_dept_db_connection():
# current_app.logger.info("open_dept_db_connection")
if hasattr(g, "scodoc_dept") and not hasattr(g, "db_conn") and g.scodoc_dept:
g.db_conn = ndb.open_dept_connection()
def start_scodoc_request():
"""Affecte toutes les requêtes, de tous les blueprints"""
ndb.open_db_connection()
@scodoc_bp.teardown_app_request
def close_dept_db_connection(arg):
# current_app.logger.info("close_dept_db_connection")
if hasattr(g, "db_conn"):
ndb.close_dept_connection()
from app.views import scodoc, notes, scolar, absences, users, essais
# current_app.logger.info("close_db_connection")
ndb.close_db_connection()

View File

@ -63,6 +63,7 @@ from flask import url_for
from flask import current_app
from app.decorators import (
scodoc,
scodoc7func,
ScoDoc7Context,
permission_required,
@ -109,7 +110,7 @@ def sco_publish(route, function, permission, methods=["GET"]):
protected by permission and called following ScoDoc 7 Zope standards.
"""
return bp.route(route, methods=methods)(
permission_required(permission)(scodoc7func(context)(function))
permission_required(permission)(scodoc7func(context)(scodoc(function)))
)
@ -127,6 +128,7 @@ def _toboolean(x):
@bp.route("/")
@bp.route("/index_html")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def index_html(context, REQUEST=None):
@ -267,6 +269,7 @@ sco_publish("/CountAbsJust", sco_abs.count_abs_just, Permission.ScoView)
@bp.route("/doSignaleAbsenceGrSemestre", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoAbsChange)
@scodoc7func(context)
def doSignaleAbsenceGrSemestre(
@ -310,6 +313,7 @@ def doSignaleAbsenceGrSemestre(
# ------------ HTML Interfaces
@bp.route("/SignaleAbsenceGrHebdo", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoAbsChange)
@scodoc7func(context)
def SignaleAbsenceGrHebdo(
@ -473,6 +477,7 @@ def SignaleAbsenceGrHebdo(
@bp.route("/SignaleAbsenceGrSemestre", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoAbsChange)
@scodoc7func(context)
def SignaleAbsenceGrSemestre(
@ -846,6 +851,7 @@ def _gen_form_saisie_groupe(
@bp.route("/EtatAbsencesGr")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context) # ported from dtml
def EtatAbsencesGr(
@ -984,6 +990,7 @@ ou entrez une date pour visualiser les absents un jour donné&nbsp;:
@bp.route("/EtatAbsencesDate")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def EtatAbsencesDate(
@ -1068,6 +1075,7 @@ def EtatAbsencesDate(
# ----- Gestion des "billets d'absence": signalement par les etudiants eux mêmes (à travers le portail)
@bp.route("/AddBilletAbsence")
@scodoc
@permission_required(Permission.ScoAbsAddBillet)
@scodoc7func(context)
def AddBilletAbsence(
@ -1127,6 +1135,7 @@ def AddBilletAbsence(
@bp.route("/AddBilletAbsenceForm")
@scodoc
@permission_required(Permission.ScoAbsAddBillet)
@scodoc7func(context)
def AddBilletAbsenceForm(context, etudid, REQUEST=None):
@ -1239,6 +1248,7 @@ def _tableBillets(context, billets, etud=None, title=""):
@bp.route("/listeBilletsEtud")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def listeBilletsEtud(context, etudid=False, REQUEST=None, format="html"):
@ -1255,6 +1265,7 @@ def listeBilletsEtud(context, etudid=False, REQUEST=None, format="html"):
@bp.route("/XMLgetBilletsEtud")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def XMLgetBilletsEtud(context, etudid=False, REQUEST=None):
@ -1268,6 +1279,7 @@ def XMLgetBilletsEtud(context, etudid=False, REQUEST=None):
@bp.route("/listeBillets")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def listeBillets(context, REQUEST=None):
@ -1296,6 +1308,7 @@ def listeBillets(context, REQUEST=None):
@bp.route("/deleteBilletAbsence")
@scodoc
@permission_required(Permission.ScoAbsChange)
@scodoc7func(context)
def deleteBilletAbsence(context, billet_id, REQUEST=None, dialog_confirmed=False):
@ -1390,6 +1403,7 @@ def _ProcessBilletAbsence(context, billet, estjust, description, REQUEST):
@bp.route("/ProcessBilletAbsenceForm")
@scodoc
@permission_required(Permission.ScoAbsChange)
@scodoc7func(context)
def ProcessBilletAbsenceForm(context, billet_id, REQUEST=None):
@ -1480,6 +1494,7 @@ def ProcessBilletAbsenceForm(context, billet_id, REQUEST=None):
@bp.route("/XMLgetAbsEtud")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def XMLgetAbsEtud(context, beg_date="", end_date="", REQUEST=None):

View File

@ -14,6 +14,7 @@ from flask import request
from flask import url_for
from app.decorators import (
scodoc,
scodoc7func,
ScoDoc7Context,
permission_required,
@ -31,6 +32,7 @@ context = ScoDoc7Context(globals())
@bp.route("/<scodoc_dept>/Scolarite/sco_exemple")
@scodoc
@scodoc7func(context)
def sco_exemple(etudid="NON"):
"""Un exemple de fonction ScoDoc 7"""
@ -56,6 +58,7 @@ def sco_exemple(etudid="NON"):
@bp.route("/<scodoc_dept>/Scolarite/sco_exemple3")
@scodoc
@login_required
@scodoc7func(context)
def sco_exemple3(toto):
@ -63,6 +66,7 @@ def sco_exemple3(toto):
@bp.route("/<scodoc_dept>/Scolarite/sco_exemple4")
@scodoc
@login_required
@scodoc7func(context)
def sco_exemple4(toto):
@ -71,6 +75,7 @@ def sco_exemple4(toto):
# Test avec un seul argument REQUEST positionnel
@bp.route("/<scodoc_dept>/Scolarite/sco_get_version")
@scodoc
@scodoc7func(context)
def sco_get_version(REQUEST):
return "ok"
@ -78,8 +83,9 @@ def sco_get_version(REQUEST):
# Fonction ressemblant à une méthode Zope protégée
@bp.route("/<scodoc_dept>/Scolarite/sco_test_view")
@scodoc7func(context)
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def sco_test_view(REQUEST=None):
return """Vous avez vu sco_test_view !"""

View File

@ -48,6 +48,7 @@ import scodoc_manager
from app.auth.models import User
from app.decorators import (
scodoc,
scodoc7func,
ScoDoc7Context,
permission_required,
@ -150,6 +151,7 @@ def sco_publish(route, function, permission, methods=["GET"]):
# --------------------- Quelques essais élémentaires:
@bp.route("/essai")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def essai(context, REQUEST=None):
@ -380,6 +382,7 @@ sco_publish(
#
@bp.route("/")
@bp.route("/index_html")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def index_html(context, REQUEST=None):
@ -432,6 +435,7 @@ sco_publish(
@bp.route("/formation_list")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formation_list(context, format=None, REQUEST=None, formation_id=None, args={}):
@ -443,6 +447,7 @@ def formation_list(context, format=None, REQUEST=None, formation_id=None, args={
@bp.route("/formation_export")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formation_export(
@ -455,6 +460,7 @@ def formation_export(
@bp.route("/formation_import_xml")
@scodoc
@permission_required(Permission.ScoChangeFormation)
@scodoc7func(context)
def formation_import_xml(context, file):
@ -465,6 +471,7 @@ def formation_import_xml(context, file):
@bp.route("/formation_import_xml_form", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoChangeFormation)
@scodoc7func(context)
def formation_import_xml_form(context, REQUEST):
@ -546,6 +553,7 @@ sco_publish(
@bp.route("/formation_count_sems")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formation_count_sems(context, formation_id):
@ -578,6 +586,7 @@ sco_publish("/ue_move", sco_edit_formation.ue_move, Permission.ScoChangeFormatio
@bp.route("/formsemestre_list")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_list(
@ -604,6 +613,7 @@ def formsemestre_list(
@bp.route("/XMLgetFormsemestres")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def XMLgetFormsemestres(context, etape_apo=None, formsemestre_id=None, REQUEST=None):
@ -656,6 +666,7 @@ sco_publish(
@bp.route("/formsemestre_custommenu_edit", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_custommenu_edit(context, REQUEST, formsemestre_id):
@ -668,6 +679,7 @@ def formsemestre_custommenu_edit(context, REQUEST, formsemestre_id):
# --- dialogue modif enseignants/moduleimpl
@bp.route("/edit_enseignants_form", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def edit_enseignants_form(context, REQUEST, moduleimpl_id):
@ -778,6 +790,7 @@ def edit_enseignants_form(context, REQUEST, moduleimpl_id):
@bp.route("/edit_moduleimpl_resp", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def edit_moduleimpl_resp(context, REQUEST, moduleimpl_id):
@ -885,6 +898,7 @@ _EXPR_HELP = """<p class="help">Expérimental: formule de calcul de la moyenne %
@bp.route("/edit_moduleimpl_expr", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def edit_moduleimpl_expr(context, REQUEST, moduleimpl_id):
@ -953,6 +967,7 @@ def edit_moduleimpl_expr(context, REQUEST, moduleimpl_id):
@bp.route("/view_module_abs")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def view_module_abs(context, REQUEST, moduleimpl_id, format="html"):
@ -1033,6 +1048,7 @@ def view_module_abs(context, REQUEST, moduleimpl_id, format="html"):
@bp.route("/edit_ue_expr", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def edit_ue_expr(context, REQUEST, formsemestre_id, ue_id):
@ -1107,6 +1123,7 @@ def edit_ue_expr(context, REQUEST, formsemestre_id, ue_id):
@bp.route("/formsemestre_enseignants_list")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_enseignants_list(context, REQUEST, formsemestre_id, format="html"):
@ -1191,6 +1208,7 @@ def formsemestre_enseignants_list(context, REQUEST, formsemestre_id, format="htm
@bp.route("/edit_enseignants_form_delete", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def edit_enseignants_form_delete(context, REQUEST, moduleimpl_id, ens_id):
@ -1230,6 +1248,7 @@ sco_publish(
@bp.route("/do_formsemestre_inscription_listinscrits")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def do_formsemestre_inscription_listinscrits(
@ -1243,6 +1262,7 @@ def do_formsemestre_inscription_listinscrits(
@bp.route("/formsemestre_desinscription")
@scodoc
@permission_required(Permission.ScoImplement)
@scodoc7func(context)
def formsemestre_desinscription(
@ -1329,6 +1349,7 @@ sco_publish(
@bp.route("/etud_desinscrit_ue")
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def etud_desinscrit_ue(context, etudid, formsemestre_id, ue_id, REQUEST=None):
@ -1344,6 +1365,7 @@ def etud_desinscrit_ue(context, etudid, formsemestre_id, ue_id, REQUEST=None):
@bp.route("/etud_inscrit_ue")
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def etud_inscrit_ue(context, etudid, formsemestre_id, ue_id, REQUEST=None):
@ -1407,6 +1429,7 @@ sco_publish(
@bp.route("/evaluation_delete", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEnsView)
@scodoc7func(context)
def evaluation_delete(context, REQUEST, evaluation_id):
@ -1490,6 +1513,7 @@ sco_publish(
@bp.route("/evaluation_edit", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEnsView)
@scodoc7func(context)
def evaluation_edit(evaluation_id, REQUEST):
@ -1500,6 +1524,7 @@ def evaluation_edit(evaluation_id, REQUEST):
@bp.route("/evaluation_create", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEnsView)
@scodoc7func(context)
def evaluation_create(moduleimpl_id, REQUEST):
@ -1510,6 +1535,7 @@ def evaluation_create(moduleimpl_id, REQUEST):
@bp.route("/evaluation_listenotes")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def evaluation_listenotes(context, REQUEST=None):
@ -1591,6 +1617,7 @@ sco_publish(
# --- Bulletins
@bp.route("/formsemestre_bulletins_pdf")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_bulletins_pdf(
@ -1607,6 +1634,7 @@ _EXPL_BULL = """Versions des bulletins:<ul><li><bf>courte</bf>: moyennes des mod
@bp.route("/formsemestre_bulletins_pdf_choice")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_bulletins_pdf_choice(context, REQUEST, formsemestre_id, version=None):
@ -1626,6 +1654,7 @@ def formsemestre_bulletins_pdf_choice(context, REQUEST, formsemestre_id, version
@bp.route("/etud_bulletins_pdf")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def etud_bulletins_pdf(context, etudid, REQUEST, version="selectedevals"):
@ -1637,6 +1666,7 @@ def etud_bulletins_pdf(context, etudid, REQUEST, version="selectedevals"):
@bp.route("/formsemestre_bulletins_mailetuds_choice")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_bulletins_mailetuds_choice(
@ -1703,6 +1733,7 @@ def formsemestre_bulletins_choice(
@bp.route("/formsemestre_bulletins_mailetuds")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_bulletins_mailetuds(
@ -1766,6 +1797,7 @@ sco_publish(
@bp.route("/appreciation_add_form", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEnsView)
@scodoc7func(context)
def appreciation_add_form(
@ -1875,6 +1907,7 @@ def appreciation_add_form(
@bp.route("/formsemestre_validation_etud_form")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_validation_etud_form(
@ -1903,6 +1936,7 @@ def formsemestre_validation_etud_form(
@bp.route("/formsemestre_validation_etud")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_validation_etud(
@ -1934,6 +1968,7 @@ def formsemestre_validation_etud(
@bp.route("/formsemestre_validation_etud_manu")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_validation_etud_manu(
@ -1971,6 +2006,7 @@ def formsemestre_validation_etud_manu(
@bp.route("/formsemestre_validate_previous_ue")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_validate_previous_ue(
@ -1997,6 +2033,7 @@ sco_publish(
@bp.route("/formsemestre_ext_edit_ue_validations", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_ext_edit_ue_validations(
@ -2022,6 +2059,7 @@ sco_publish(
@bp.route("/etud_ue_suppress_validation")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def etud_ue_suppress_validation(context, etudid, formsemestre_id, ue_id, REQUEST=None):
@ -2038,6 +2076,7 @@ def etud_ue_suppress_validation(context, etudid, formsemestre_id, ue_id, REQUEST
@bp.route("/formsemestre_validation_auto")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_validation_auto(context, formsemestre_id, REQUEST):
@ -2055,6 +2094,7 @@ def formsemestre_validation_auto(context, formsemestre_id, REQUEST):
@bp.route("/do_formsemestre_validation_auto")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def do_formsemestre_validation_auto(context, formsemestre_id, REQUEST):
@ -2072,6 +2112,7 @@ def do_formsemestre_validation_auto(context, formsemestre_id, REQUEST):
@bp.route("/formsemestre_validation_suppress_etud", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_validation_suppress_etud(
@ -2312,6 +2353,7 @@ sco_publish(
@bp.route("/check_sem_integrity")
@scodoc
@permission_required(Permission.ScoImplement)
@scodoc7func(context)
def check_sem_integrity(context, formsemestre_id, REQUEST, fix=False):
@ -2384,6 +2426,7 @@ def check_sem_integrity(context, formsemestre_id, REQUEST, fix=False):
@bp.route("/check_form_integrity")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def check_form_integrity(context, formation_id, fix=False, REQUEST=None):
@ -2423,6 +2466,7 @@ def check_form_integrity(context, formation_id, fix=False, REQUEST=None):
@bp.route("/check_formsemestre_integrity")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def check_formsemestre_integrity(context, formsemestre_id, REQUEST=None):
@ -2473,6 +2517,7 @@ def check_formsemestre_integrity(context, formsemestre_id, REQUEST=None):
@bp.route("/check_integrity_all")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def check_integrity_all(context, REQUEST=None):

View File

@ -45,7 +45,7 @@ from app.scodoc.sco_permissions import Permission
@bp.route("/ScoDoc")
@bp.route("/ScoDoc/index")
def index():
def index(): # XXX TODO A REECRIRE
dept_ids = sco_mgr.get_dept_ids()
return render_template(
"scodoc.html",

View File

@ -48,6 +48,7 @@ from flask_login import current_user
from config import Config
import scodoc_manager
from app.decorators import (
scodoc,
scodoc7func,
ScoDoc7Context,
permission_required,
@ -120,7 +121,7 @@ def sco_publish(route, function, permission, methods=("GET",)):
protected by permission and called following ScoDoc 7 Zope standards.
"""
return bp.route(route, methods=methods)(
permission_required(permission)(scodoc7func(context)(function))
permission_required(permission)(scodoc7func(context)(scodoc(function)))
)
@ -135,6 +136,7 @@ log("ScoDoc8 restarting...")
@bp.route("/about")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def about(context, REQUEST):
@ -169,6 +171,7 @@ def about(context, REQUEST):
@bp.route("/edit_preferences", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoChangePreferences)
@scodoc7func(context)
def edit_preferences(context, REQUEST):
@ -177,6 +180,7 @@ def edit_preferences(context, REQUEST):
@bp.route("/formsemestre_edit_preferences", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def formsemestre_edit_preferences(context, formsemestre_id, REQUEST):
@ -196,6 +200,7 @@ def formsemestre_edit_preferences(context, formsemestre_id, REQUEST):
@bp.route("/doc_preferences")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def doc_preferences(REQUEST):
@ -212,6 +217,7 @@ def doc_preferences(REQUEST):
@bp.route("/showEtudLog")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def showEtudLog(context, etudid, format="html", REQUEST=None):
@ -248,8 +254,22 @@ def showEtudLog(context, etudid, format="html", REQUEST=None):
# ---------- PAGE ACCUEIL (listes) --------------
@bp.route("/")
@bp.route("/index_html")
# @bp.route("/")
@bp.route("/kimo")
@scodoc
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def kimo(context, REQUEST=None, showcodes=0, showsemtable=0):
import time
return f"{time.time()} := {g.scodoc_dept}"
# @bp.route("/")
@bp.route("/index_html2")
@scodoc
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def index_html(context, REQUEST=None, showcodes=0, showsemtable=0):
@ -287,6 +307,7 @@ sco_publish(
# -------------------------- INFOS SUR ETUDIANTS --------------------------
@bp.route("/getEtudInfo")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def getEtudInfo(
@ -321,6 +342,7 @@ sco_publish(
# XMLgetEtudInfos était le nom dans l'ancienne API ScoDoc 6
@bp.route("/etud_info")
@bp.route("/XMLgetEtudInfos")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def etud_info(context, etudid=None, format="xml", REQUEST=None):
@ -494,6 +516,7 @@ sco_publish(
@bp.route("/doAddAnnotation", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudAddAnnotations)
@scodoc7func(context)
def doAddAnnotation(etudid, comment):
@ -514,6 +537,7 @@ def doAddAnnotation(etudid, comment):
@bp.route("/doSuppressAnnotation", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def doSuppressAnnotation(context, etudid, annotation_id, REQUEST):
@ -541,6 +565,7 @@ def doSuppressAnnotation(context, etudid, annotation_id, REQUEST):
@bp.route("/formChangeCoordonnees", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudChangeAdr)
@scodoc7func(context)
def formChangeCoordonnees(context, etudid, REQUEST):
@ -704,6 +729,7 @@ sco_publish(
methods=["GET", "POST"],
)
# @bp.route("/partition_create", methods=["GET", "POST"])
# @scodoc
# @permission_required(Permission.ScoView)
# @scodoc7func(context)
# def partition_create(
@ -725,6 +751,7 @@ sco_publish("/etud_photo_html", sco_photos.etud_photo_html, Permission.ScoView)
@bp.route("/etud_photo_orig_page")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def etud_photo_orig_page(context, etudid=None, REQUEST=None):
@ -743,6 +770,7 @@ def etud_photo_orig_page(context, etudid=None, REQUEST=None):
@bp.route("/formChangePhoto", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudChangeAdr)
@scodoc7func(context)
def formChangePhoto(context, etudid=None, REQUEST=None):
@ -802,6 +830,7 @@ def formChangePhoto(context, etudid=None, REQUEST=None):
@bp.route("/formSuppressPhoto")
@scodoc
@permission_required(Permission.ScoEtudChangeAdr)
@scodoc7func(context)
def formSuppressPhoto(context, etudid=None, REQUEST=None, dialog_confirmed=False):
@ -826,6 +855,7 @@ def formSuppressPhoto(context, etudid=None, REQUEST=None, dialog_confirmed=False
#
@bp.route("/formDem")
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def formDem(context, etudid, formsemestre_id, REQUEST):
@ -841,6 +871,7 @@ def formDem(context, etudid, formsemestre_id, REQUEST):
@bp.route("/formDef")
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def formDef(context, etudid, formsemestre_id, REQUEST):
@ -902,6 +933,7 @@ def _formDem_of_Def(
@bp.route("/doDemEtudiant")
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def doDemEtudiant(context, etudid, formsemestre_id, event_date=None, REQUEST=None):
@ -919,6 +951,7 @@ def doDemEtudiant(context, etudid, formsemestre_id, event_date=None, REQUEST=Non
@bp.route("/doDefEtudiant")
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def doDefEtudiant(context, etudid, formsemestre_id, event_date=None, REQUEST=None):
@ -980,6 +1013,7 @@ def _do_dem_or_def_etud(
@bp.route("/doCancelDem", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def doCancelDem(
@ -1002,6 +1036,7 @@ def doCancelDem(
@bp.route("/doCancelDef", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def doCancelDef(
@ -1087,6 +1122,7 @@ def _do_cancel_dem_or_def(
@bp.route("/etudident_create_form", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def etudident_create_form(context, REQUEST=None):
@ -1095,6 +1131,7 @@ def etudident_create_form(context, REQUEST=None):
@bp.route("/etudident_edit_form", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def etudident_edit_form(context, REQUEST=None):
@ -1501,6 +1538,7 @@ def _etudident_create_or_edit_form(context, REQUEST, edit):
@bp.route("/etudident_delete", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def etudident_delete(context, etudid, dialog_confirmed=False, REQUEST=None):
@ -1573,6 +1611,7 @@ def etudident_delete(context, etudid, dialog_confirmed=False, REQUEST=None):
@bp.route("/check_group_apogee")
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def check_group_apogee(
@ -1724,6 +1763,7 @@ def check_group_apogee(
@bp.route("/form_students_import_excel")
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def form_students_import_excel(context, REQUEST, formsemestre_id=None):
@ -1864,6 +1904,7 @@ Les champs avec un astérisque (*) doivent être présents (nulls non autorisés
@bp.route("/import_generate_excel_sample")
@scodoc
@permission_required(Permission.ScoEtudInscrit)
@scodoc7func(context)
def import_generate_excel_sample(context, REQUEST, with_codesemestre="1"):
@ -1881,6 +1922,7 @@ def import_generate_excel_sample(context, REQUEST, with_codesemestre="1"):
# --- Données admission
@bp.route("/import_generate_admission_sample")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def import_generate_admission_sample(context, REQUEST, formsemestre_id):
@ -1902,6 +1944,7 @@ def import_generate_admission_sample(context, REQUEST, formsemestre_id):
# --- Données admission depuis fichier excel (version nov 2016)
@bp.route("/form_students_import_infos_admissions", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def form_students_import_infos_admissions(context, REQUEST, formsemestre_id=None):
@ -2009,6 +2052,7 @@ def form_students_import_infos_admissions(context, REQUEST, formsemestre_id=None
@bp.route("/formsemestre_import_etud_admission")
@scodoc
@permission_required(Permission.ScoEtudChangeAdr)
@scodoc7func(context)
def formsemestre_import_etud_admission(
@ -2057,6 +2101,7 @@ sco_publish(
# --- Statistiques
@bp.route("/stat_bac")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def stat_bac(context, formsemestre_id):

View File

@ -48,6 +48,7 @@ from app.auth.models import User
from app.auth.models import Role
from app.auth.models import UserRole
from app.decorators import (
scodoc,
scodoc7func,
ScoDoc7Context,
permission_required,
@ -71,6 +72,7 @@ context = ScoDoc7Context("users") # sco8
@bp.route("/")
@bp.route("/index_html")
@scodoc
@permission_required(Permission.ScoUsersView)
@scodoc7func(context)
def index_html(context, REQUEST, all_depts=False, with_inactives=False, format="html"):
@ -84,6 +86,7 @@ def index_html(context, REQUEST, all_depts=False, with_inactives=False, format="
@bp.route("/user_info")
@scodoc
@permission_required(Permission.ScoUsersView)
@scodoc7func(context)
def user_info(user_name, format="json", REQUEST=None):
@ -92,6 +95,7 @@ def user_info(user_name, format="json", REQUEST=None):
@bp.route("/create_user_form", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoUsersAdmin)
@scodoc7func(context)
def create_user_form(context, REQUEST, user_name=None, edit=0):
@ -464,6 +468,7 @@ def import_users_form():
@bp.route("/user_info_page")
@scodoc
@permission_required(Permission.ScoUsersView)
@scodoc7func(context)
def user_info_page(user_name, REQUEST=None):
@ -471,6 +476,7 @@ def user_info_page(user_name, REQUEST=None):
@bp.route("/get_user_list_xml")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def get_user_list_xml(dept=None, start="", limit=25, REQUEST=None):
@ -496,6 +502,7 @@ def get_user_list_xml(dept=None, start="", limit=25, REQUEST=None):
@bp.route("/form_change_password")
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def form_change_password(REQUEST, user_name=None):
@ -534,6 +541,7 @@ def form_change_password(REQUEST, user_name=None):
@bp.route("/change_password", methods=["POST"])
@scodoc
@permission_required(Permission.ScoView)
@scodoc7func(context)
def change_password(user_name, password, password2, REQUEST):
@ -593,6 +601,7 @@ def change_password(user_name, password, password2, REQUEST):
@bp.route("/delete_user_form", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoUsersAdmin)
@scodoc7func(context)
def delete_user_form(REQUEST, user_name, dialog_confirmed=False):

View File

@ -146,11 +146,22 @@ def user_password(username, password=None): # user-password
@app.cli.command()
@click.argument("dept")
def sco_delete_dept(dept): # sco-delete-dept
"Delete existing departement"
raise NotImplementedError()
if os.system('tools/delete_dept.sh -n "{}"'.format(dept)):
sys.stderr.write("error deleting dept " + dept)
return 1
"""Delete existing departement"""
click.confirm(
f"""Attention: Cela va effacer toutes les données du département {dept}
(étudiants, notes, formations, etc)
Voulez-vous vraiment continuer ?
""",
abort=True,
)
db.reflect()
d = models.Departement.query.filter_by(acronym=dept).first()
if d is None:
sys.stderr.write(f"Erreur: le departement {dept} n'existe pas !")
return 2
# XXX TODO: détruire les objets du département !
db.session.delete(d)
db.session.commit()
return 0
@ -158,10 +169,9 @@ def sco_delete_dept(dept): # sco-delete-dept
@click.argument("dept")
def sco_create_dept(dept): # sco-create-dept
"Create new departement"
raise NotImplementedError()
if os.system(f'tools/create_dept.sh -n "{dept}"'):
sys.stderr.write(f"error creating dept {dept}\n")
return 1
d = models.Departement(acronym=dept)
db.session.add(d)
db.session.commit()
return 0

View File

@ -4,6 +4,7 @@ from flask import g
from flask_login import login_user, logout_user, current_user
from config import TestConfig
import app
from app import db, create_app
from app import initialize_scodoc_database, clear_scodoc_cache
from app import models
@ -44,9 +45,13 @@ def test_client():
u.add_role(admin_role, TestConfig.DEPT_TEST)
db.session.add(u)
db.session.commit()
ndb.set_sco_dept(TestConfig.DEPT_TEST) # set db connection
# Creation département de Test
d = models.Departement(acronym=TestConfig.DEPT_TEST)
db.session.add(d)
db.session.commit()
app.set_sco_dept(TestConfig.DEPT_TEST) # set db connection
yield client
# ndb.close_dept_connection()
ndb.close_db_connection()
# Teardown:
db.session.commit()
db.session.remove()

View File

@ -11,6 +11,7 @@ Usage: pytest tests/unit/test_caches.py
from flask import current_app, g
import app
from app import db
from app.scodoc import sco_cache
from app.scodoc import sco_evaluations
@ -25,7 +26,7 @@ context = None # #context
def test_notes_table(test_client):
"""Test construction et cache de NotesTable."""
ndb.set_sco_dept(DEPT)
app.set_sco_dept(DEPT)
assert g.scodoc_dept == DEPT
# prépare le département avec quelques semestres:
run_sco_basic()
@ -49,7 +50,7 @@ def test_notes_table(test_client):
def test_cache_evaluations(test_client):
""""""
# cherche un semestre ayant des evaluations
ndb.set_sco_dept(DEPT)
app.set_sco_dept(DEPT)
# prépare le département avec quelques semestres:
run_sco_basic()
#

View File

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
"""Test ORM departement/formation/preferences
Utiliser comme:
pytest tests/unit/test_departements.py
"""
from flask import g
import app
from app import db
from app.models import Departement, ScoPreference, FormSemestre, formsemestre
from app.scodoc import notesdb as ndb
from app.scodoc import sco_formsemestre
from app.scodoc import sco_preferences
from tests.unit import test_sco_basic
def test_preferences_orm(test_client):
"""preferences, via ORM and legacy ScoDoc"""
d = Departement(acronym="TT")
p1 = ScoPreference(name="temperature", value="24", departement=d)
p2 = ScoPreference(name="couleur", value="bleue", departement=d)
db.session.add(d)
db.session.add(p1)
db.session.add(p2)
db.session.commit()
prefs = d.preferences.all()
assert isinstance(prefs, list)
assert len(prefs) == 2
def test_preferences(test_client):
"""ScoDoc preferences"""
# preferences "globales" d'un département:
current_dept = Departement.query.filter_by(acronym=g.scodoc_dept).first()
prefs = sco_preferences.get_base_preferences()
assert isinstance(prefs, sco_preferences.BasePreferences)
assert prefs.dept_id == current_dept.id
# Compare nombre de d'items
assert len(ScoPreference.query.filter_by(dept_id=current_dept.id).all()) == len(
prefs
)
# Accès à une valeur via ORM
assert (
len(
ScoPreference.query.filter_by(
dept_id=current_dept.id, name="abs_notification_mail_tmpl"
).all()
)
== 1
)
orm_val = (
ScoPreference.query.filter_by(
dept_id=current_dept.id, name="abs_notification_mail_tmpl"
)
.first()
.value
)
# Compare valeurs
sco_val = prefs.get(None, "abs_notification_mail_tmpl")
assert orm_val.strip() == sco_val.strip()
# nb: I don't understand why SQLAlchemy strips the string ?!
# --- Charge dans un autre département
# departement fictif créé ici:
d = Departement(acronym="D2")
db.session.add(d)
db.session.commit()
app.set_sco_dept("D2")
prefs2 = sco_preferences.get_base_preferences()
assert len(prefs2) == len(prefs)
prefs2.set(None, "abs_notification_mail_tmpl", "toto")
assert prefs2.get(None, "abs_notification_mail_tmpl") == "toto"
assert prefs.get(None, "abs_notification_mail_tmpl") != "toto"
orm_val = (
ScoPreference.query.filter_by(dept_id=d.id, name="abs_notification_mail_tmpl")
.first()
.value
)
assert orm_val == "toto"
# --- Preferences d'un semestre
# rejoure ce test pour avoir un semestre créé
test_sco_basic.run_sco_basic()
sem = sco_formsemestre.do_formsemestre_list(None)[0]
formsemestre_id = sem["formsemestre_id"]
semp = sco_preferences.SemPreferences(formsemestre_id=formsemestre_id)
assert semp["abs_notification_mail_tmpl"] == "toto"
assert semp.is_global("abs_notification_mail_tmpl")
# donne une valeur pour le semestre:
prefs2.set(formsemestre_id, "abs_notification_mail_tmpl", "foo")
assert not semp.is_global("abs_notification_mail_tmpl")
assert semp["abs_notification_mail_tmpl"] == "foo"

View File

@ -7,8 +7,8 @@
Utiliser comme:
pytest tests/unit/test_sco_basic.py
Au préalable, créer un département de test neuf:
flask sco-delete-dept TEST00 && flask sco-create-dept TEST00
Au besoin, créer un base de test neuve:
./tools/create_database.sh SCODOC_TEST
"""
import random
@ -18,6 +18,7 @@ from flask import g
from config import TestConfig
from tests.unit import sco_fake_gen
import app
from app.scodoc import notesdb as ndb
from app.scodoc import sco_abs
from app.scodoc import sco_abs_views
@ -38,7 +39,7 @@ def test_sco_basic(test_client):
Création 10 étudiants, formation, semestre, inscription etudiant,
creation 1 evaluation, saisie 10 notes.
"""
ndb.set_sco_dept(DEPT)
app.set_sco_dept(DEPT)
run_sco_basic()