Merge pull request 'master' (#2) from ScoDoc/ScoDoc:master into master
Reviewed-on: https://scodoc.org/git/lehmann/ScoDoc-Front/pulls/2
This commit is contained in:
commit
e0edde3f46
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: UTF-8 -*
|
# -*- coding: UTF-8 -*
|
||||||
# Authentication code borrowed from Miguel Grinberg's Mega Tutorial
|
# Authentication code borrowed from Miguel Grinberg's Mega Tutorial
|
||||||
# (see https://github.com/miguelgrinberg/microblog)
|
# (see https://github.com/miguelgrinberg/microblog)
|
||||||
|
# and modified for ScoDoc
|
||||||
|
|
||||||
# Under The MIT License (MIT)
|
# Under The MIT License (MIT)
|
||||||
|
|
||||||
@ -23,6 +24,7 @@
|
|||||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
from flask import g
|
||||||
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
|
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.api.errors import error_response
|
from app.api.errors import error_response
|
||||||
@ -35,6 +37,7 @@ token_auth = HTTPTokenAuth()
|
|||||||
def verify_password(username, password):
|
def verify_password(username, password):
|
||||||
user = User.query.filter_by(user_name=username).first()
|
user = User.query.filter_by(user_name=username).first()
|
||||||
if user and user.check_password(password):
|
if user and user.check_password(password):
|
||||||
|
g.current_user = user
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
@ -45,7 +48,9 @@ def basic_auth_error(status):
|
|||||||
|
|
||||||
@token_auth.verify_token
|
@token_auth.verify_token
|
||||||
def verify_token(token):
|
def verify_token(token):
|
||||||
return User.check_token(token) if token else None
|
user = User.check_token(token) if token else None
|
||||||
|
g.current_user = user
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
@token_auth.error_handler
|
@token_auth.error_handler
|
||||||
@ -53,15 +58,20 @@ def token_auth_error(status):
|
|||||||
return error_response(status)
|
return error_response(status)
|
||||||
|
|
||||||
|
|
||||||
def token_permission_required(permission):
|
@token_auth.get_user_roles
|
||||||
def decorator(f):
|
def get_user_roles(user):
|
||||||
@wraps(f)
|
return user.roles
|
||||||
def decorated_function(*args, **kwargs):
|
|
||||||
scodoc_dept = getattr(g, "scodoc_dept", None)
|
|
||||||
if not current_user.has_permission(permission, scodoc_dept):
|
|
||||||
abort(403)
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
|
|
||||||
return login_required(decorated_function)
|
|
||||||
|
|
||||||
return decorator
|
# def token_permission_required(permission):
|
||||||
|
# def decorator(f):
|
||||||
|
# @wraps(f)
|
||||||
|
# def decorated_function(*args, **kwargs):
|
||||||
|
# scodoc_dept = getattr(g, "scodoc_dept", None)
|
||||||
|
# if not current_user.has_permission(permission, scodoc_dept):
|
||||||
|
# abort(403)
|
||||||
|
# return f(*args, **kwargs)
|
||||||
|
|
||||||
|
# return login_required(decorated_function)
|
||||||
|
|
||||||
|
# return decorator
|
||||||
|
@ -39,13 +39,18 @@
|
|||||||
# Scolarite/Notes/moduleimpl_status
|
# Scolarite/Notes/moduleimpl_status
|
||||||
# Scolarite/setGroups
|
# Scolarite/setGroups
|
||||||
|
|
||||||
from flask import jsonify, request, url_for, abort
|
from flask import jsonify, request, url_for, abort, g
|
||||||
from app import db
|
from flask_login import current_user
|
||||||
|
from sqlalchemy.sql import func
|
||||||
|
|
||||||
|
from app import db, log
|
||||||
from app.api import bp
|
from app.api import bp
|
||||||
from app.api.auth import token_auth
|
from app.api.auth import token_auth
|
||||||
from app.api.errors import bad_request
|
from app.api.errors import bad_request, error_response
|
||||||
|
from app.decorators import permission_required
|
||||||
from app import models
|
from app import models
|
||||||
|
from app.models import FormSemestre, FormSemestreInscription, Identite
|
||||||
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
|
||||||
|
|
||||||
@bp.route("list_depts", methods=["GET"])
|
@bp.route("list_depts", methods=["GET"])
|
||||||
@ -54,3 +59,23 @@ def list_depts():
|
|||||||
depts = models.Departement.query.filter_by(visible=True).all()
|
depts = models.Departement.query.filter_by(visible=True).all()
|
||||||
data = [d.to_dict() for d in depts]
|
data = [d.to_dict() for d in depts]
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/etudiants/courant", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
|
def etudiants():
|
||||||
|
"""Liste de tous les étudiants actuellement inscrits à un semestre
|
||||||
|
en cours.
|
||||||
|
"""
|
||||||
|
# Vérification de l'accès: permission Observateir sur tous les départements
|
||||||
|
# (c'est un exemple à compléter)
|
||||||
|
if not g.current_user.has_permission(Permission.ScoObservateur, None):
|
||||||
|
return error_response(401, message="accès interdit")
|
||||||
|
|
||||||
|
query = db.session.query(Identite).filter(
|
||||||
|
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
||||||
|
FormSemestreInscription.etudid == Identite.id,
|
||||||
|
FormSemestre.date_debut <= func.now(),
|
||||||
|
FormSemestre.date_fin >= func.now(),
|
||||||
|
)
|
||||||
|
return jsonify([e.to_dict_bul(include_photo=False) for e in query])
|
||||||
|
@ -65,7 +65,7 @@ class User(UserMixin, db.Model):
|
|||||||
date_created = db.Column(db.DateTime, default=datetime.utcnow)
|
date_created = db.Column(db.DateTime, default=datetime.utcnow)
|
||||||
date_expiration = db.Column(db.DateTime, default=None)
|
date_expiration = db.Column(db.DateTime, default=None)
|
||||||
passwd_temp = db.Column(db.Boolean, default=False)
|
passwd_temp = db.Column(db.Boolean, default=False)
|
||||||
token = db.Column(db.String(32), index=True, unique=True)
|
token = db.Column(db.Text(), index=True, unique=True)
|
||||||
token_expiration = db.Column(db.DateTime)
|
token_expiration = db.Column(db.DateTime)
|
||||||
|
|
||||||
roles = db.relationship("Role", secondary="user_role", viewonly=True)
|
roles = db.relationship("Role", secondary="user_role", viewonly=True)
|
||||||
@ -272,7 +272,7 @@ class User(UserMixin, db.Model):
|
|||||||
"""string repr. of user's roles (with depts)
|
"""string repr. of user's roles (with depts)
|
||||||
e.g. "Ens_RT, Ens_Info, Secr_CJ"
|
e.g. "Ens_RT, Ens_Info, Secr_CJ"
|
||||||
"""
|
"""
|
||||||
return ",".join(f"{r.role.name}_{r.dept or ''}" for r in self.user_roles)
|
return ",".join(f"{r.role.name or ''}_{r.dept or ''}" for r in self.user_roles)
|
||||||
|
|
||||||
def is_administrator(self):
|
def is_administrator(self):
|
||||||
"True if i'm an active SuperAdmin"
|
"True if i'm an active SuperAdmin"
|
||||||
|
@ -25,7 +25,6 @@ class ResultatsSemestreBUT:
|
|||||||
"""Structure légère pour stocker les résultats du semestre et
|
"""Structure légère pour stocker les résultats du semestre et
|
||||||
générer les bulletins.
|
générer les bulletins.
|
||||||
__init__ : charge depuis le cache ou calcule
|
__init__ : charge depuis le cache ou calcule
|
||||||
invalidate(): invalide données cachées
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_cached_attrs = (
|
_cached_attrs = (
|
||||||
|
@ -35,12 +35,12 @@ from app.models.modules import Module, ModuleUECoef, NotesTag, notes_modules_tag
|
|||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
from app.models.formsemestre import (
|
from app.models.formsemestre import (
|
||||||
FormSemestre,
|
FormSemestre,
|
||||||
FormsemestreEtape,
|
FormSemestreEtape,
|
||||||
FormationModalite,
|
FormationModalite,
|
||||||
FormsemestreUECoef,
|
FormSemestreUECoef,
|
||||||
FormsemestreUEComputationExpr,
|
FormSemestreUEComputationExpr,
|
||||||
FormsemestreCustomMenu,
|
FormSemestreCustomMenu,
|
||||||
FormsemestreInscription,
|
FormSemestreInscription,
|
||||||
notes_formsemestre_responsables,
|
notes_formsemestre_responsables,
|
||||||
NotesSemSet,
|
NotesSemSet,
|
||||||
notes_semset_formsemestre,
|
notes_semset_formsemestre,
|
||||||
@ -57,7 +57,7 @@ from app.models.evaluations import (
|
|||||||
from app.models.groups import Partition, GroupDescr, group_membership
|
from app.models.groups import Partition, GroupDescr, group_membership
|
||||||
from app.models.notes import (
|
from app.models.notes import (
|
||||||
ScolarEvent,
|
ScolarEvent,
|
||||||
ScolarFormsemestreValidation,
|
ScolarFormSemestreValidation,
|
||||||
ScolarAutorisationInscription,
|
ScolarAutorisationInscription,
|
||||||
BulAppreciations,
|
BulAppreciations,
|
||||||
NotesNotes,
|
NotesNotes,
|
||||||
|
@ -9,7 +9,6 @@ from app import models
|
|||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.models import CODE_STR_LEN
|
from app.models import CODE_STR_LEN
|
||||||
from app.scodoc import sco_photos
|
|
||||||
|
|
||||||
|
|
||||||
class Identite(db.Model):
|
class Identite(db.Model):
|
||||||
@ -71,12 +70,14 @@ class Identite(db.Model):
|
|||||||
"le mail associé à la première adrese de l'étudiant, ou None"
|
"le mail associé à la première adrese de l'étudiant, ou None"
|
||||||
return self.adresses[0].email or None if self.adresses.count() > 0 else None
|
return self.adresses[0].email or None if self.adresses.count() > 0 else None
|
||||||
|
|
||||||
def to_dict_bul(self):
|
def to_dict_bul(self, include_photo=True):
|
||||||
"""Infos exportées dans les bulletins"""
|
"""Infos exportées dans les bulletins"""
|
||||||
return {
|
from app.scodoc import sco_photos
|
||||||
|
|
||||||
|
d = {
|
||||||
"civilite": self.civilite,
|
"civilite": self.civilite,
|
||||||
"code_ine": self.code_nip,
|
"code_ine": self.code_ine,
|
||||||
"code_nip": self.code_ine,
|
"code_nip": self.code_nip,
|
||||||
"date_naissance": self.date_naissance.isoformat()
|
"date_naissance": self.date_naissance.isoformat()
|
||||||
if self.date_naissance
|
if self.date_naissance
|
||||||
else None,
|
else None,
|
||||||
@ -84,9 +85,11 @@ class Identite(db.Model):
|
|||||||
"emailperso": self.get_first_email("emailperso"),
|
"emailperso": self.get_first_email("emailperso"),
|
||||||
"etudid": self.id,
|
"etudid": self.id,
|
||||||
"nom": self.nom_disp(),
|
"nom": self.nom_disp(),
|
||||||
"photo_url": sco_photos.get_etud_photo_url(self.id),
|
|
||||||
"prenom": self.prenom,
|
"prenom": self.prenom,
|
||||||
}
|
}
|
||||||
|
if include_photo:
|
||||||
|
d["photo_url"] = (sco_photos.get_etud_photo_url(self.id),)
|
||||||
|
return d
|
||||||
|
|
||||||
def inscription_courante(self):
|
def inscription_courante(self):
|
||||||
"""La première inscription à un formsemestre _actuellement_ en cours.
|
"""La première inscription à un formsemestre _actuellement_ en cours.
|
||||||
@ -104,7 +107,7 @@ class Identite(db.Model):
|
|||||||
False si pas inscrit, ou scu.INSCRIT, DEMISSION, DEF
|
False si pas inscrit, ou scu.INSCRIT, DEMISSION, DEF
|
||||||
"""
|
"""
|
||||||
# voir si ce n'est pas trop lent:
|
# voir si ce n'est pas trop lent:
|
||||||
ins = models.FormsemestreInscription.query.filter_by(
|
ins = models.FormSemestreInscription.query.filter_by(
|
||||||
etudid=self.id, formsemestre_id=formsemestre_id
|
etudid=self.id, formsemestre_id=formsemestre_id
|
||||||
).first()
|
).first()
|
||||||
if ins:
|
if ins:
|
||||||
|
@ -82,7 +82,7 @@ class FormSemestre(db.Model):
|
|||||||
|
|
||||||
# Relations:
|
# Relations:
|
||||||
etapes = db.relationship(
|
etapes = db.relationship(
|
||||||
"FormsemestreEtape", cascade="all,delete", backref="formsemestre"
|
"FormSemestreEtape", cascade="all,delete", backref="formsemestre"
|
||||||
)
|
)
|
||||||
modimpls = db.relationship("ModuleImpl", backref="formsemestre", lazy="dynamic")
|
modimpls = db.relationship("ModuleImpl", backref="formsemestre", lazy="dynamic")
|
||||||
etuds = db.relationship(
|
etuds = db.relationship(
|
||||||
@ -119,7 +119,7 @@ class FormSemestre(db.Model):
|
|||||||
return d
|
return d
|
||||||
|
|
||||||
def query_ues(self, with_sport=False) -> flask_sqlalchemy.BaseQuery:
|
def query_ues(self, with_sport=False) -> flask_sqlalchemy.BaseQuery:
|
||||||
"""UE des modules de ce semestre.
|
"""UE des modules de ce semestre, triées par numéro.
|
||||||
- Formations classiques: les UEs auxquelles appartiennent
|
- Formations classiques: les UEs auxquelles appartiennent
|
||||||
les modules mis en place dans ce semestre.
|
les modules mis en place dans ce semestre.
|
||||||
- Formations APC / BUT: les UEs de la formation qui ont
|
- Formations APC / BUT: les UEs de la formation qui ont
|
||||||
@ -262,7 +262,7 @@ notes_formsemestre_responsables = db.Table(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class FormsemestreEtape(db.Model):
|
class FormSemestreEtape(db.Model):
|
||||||
"""Étape Apogée associées au semestre"""
|
"""Étape Apogée associées au semestre"""
|
||||||
|
|
||||||
__tablename__ = "notes_formsemestre_etapes"
|
__tablename__ = "notes_formsemestre_etapes"
|
||||||
@ -331,7 +331,7 @@ class FormationModalite(db.Model):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
class FormsemestreUECoef(db.Model):
|
class FormSemestreUECoef(db.Model):
|
||||||
"""Coef des UE capitalisees arrivant dans ce semestre"""
|
"""Coef des UE capitalisees arrivant dans ce semestre"""
|
||||||
|
|
||||||
__tablename__ = "notes_formsemestre_uecoef"
|
__tablename__ = "notes_formsemestre_uecoef"
|
||||||
@ -350,7 +350,7 @@ class FormsemestreUECoef(db.Model):
|
|||||||
coefficient = db.Column(db.Float, nullable=False)
|
coefficient = db.Column(db.Float, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
class FormsemestreUEComputationExpr(db.Model):
|
class FormSemestreUEComputationExpr(db.Model):
|
||||||
"""Formules utilisateurs pour calcul moyenne UE"""
|
"""Formules utilisateurs pour calcul moyenne UE"""
|
||||||
|
|
||||||
__tablename__ = "notes_formsemestre_ue_computation_expr"
|
__tablename__ = "notes_formsemestre_ue_computation_expr"
|
||||||
@ -370,7 +370,7 @@ class FormsemestreUEComputationExpr(db.Model):
|
|||||||
computation_expr = db.Column(db.Text())
|
computation_expr = db.Column(db.Text())
|
||||||
|
|
||||||
|
|
||||||
class FormsemestreCustomMenu(db.Model):
|
class FormSemestreCustomMenu(db.Model):
|
||||||
"""Menu custom associe au semestre"""
|
"""Menu custom associe au semestre"""
|
||||||
|
|
||||||
__tablename__ = "notes_formsemestre_custommenu"
|
__tablename__ = "notes_formsemestre_custommenu"
|
||||||
@ -386,7 +386,7 @@ class FormsemestreCustomMenu(db.Model):
|
|||||||
idx = db.Column(db.Integer, default=0, server_default="0") # rang dans le menu
|
idx = db.Column(db.Integer, default=0, server_default="0") # rang dans le menu
|
||||||
|
|
||||||
|
|
||||||
class FormsemestreInscription(db.Model):
|
class FormSemestreInscription(db.Model):
|
||||||
"""Inscription à un semestre de formation"""
|
"""Inscription à un semestre de formation"""
|
||||||
|
|
||||||
__tablename__ = "notes_formsemestre_inscription"
|
__tablename__ = "notes_formsemestre_inscription"
|
||||||
@ -410,7 +410,7 @@ class FormsemestreInscription(db.Model):
|
|||||||
backref=db.backref(
|
backref=db.backref(
|
||||||
"inscriptions",
|
"inscriptions",
|
||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
order_by="FormsemestreInscription.etudid",
|
order_by="FormSemestreInscription.etudid",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
# I inscrit, D demission en cours de semestre, DEF si "defaillant"
|
# I inscrit, D demission en cours de semestre, DEF si "defaillant"
|
||||||
|
@ -40,7 +40,7 @@ class ScolarEvent(db.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ScolarFormsemestreValidation(db.Model):
|
class ScolarFormSemestreValidation(db.Model):
|
||||||
"""Décisions de jury"""
|
"""Décisions de jury"""
|
||||||
|
|
||||||
__tablename__ = "scolar_formsemestre_validation"
|
__tablename__ = "scolar_formsemestre_validation"
|
||||||
|
@ -32,7 +32,7 @@ from flask_login import current_user
|
|||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.models import Formation, UniteEns, Matiere, Module, FormSemestre, ModuleImpl
|
from app.models import Formation, UniteEns, Matiere, Module, FormSemestre, ModuleImpl
|
||||||
from app.models.notes import ScolarFormsemestreValidation
|
from app.models.notes import ScolarFormSemestreValidation
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc.sco_utils import ModuleType
|
from app.scodoc.sco_utils import ModuleType
|
||||||
@ -152,7 +152,7 @@ def html_ue_infos(ue):
|
|||||||
)
|
)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
nb_etuds_valid_ue = ScolarFormsemestreValidation.query.filter_by(
|
nb_etuds_valid_ue = ScolarFormSemestreValidation.query.filter_by(
|
||||||
ue_id=ue.id
|
ue_id=ue.id
|
||||||
).count()
|
).count()
|
||||||
can_safely_be_suppressed = (
|
can_safely_be_suppressed = (
|
||||||
|
@ -56,7 +56,7 @@ from app.scodoc.htmlutils import histogram_notes
|
|||||||
|
|
||||||
def do_evaluation_listenotes(
|
def do_evaluation_listenotes(
|
||||||
evaluation_id=None, moduleimpl_id=None, format="html"
|
evaluation_id=None, moduleimpl_id=None, format="html"
|
||||||
) -> str:
|
) -> tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
Affichage des notes d'une évaluation (si evaluation_id)
|
Affichage des notes d'une évaluation (si evaluation_id)
|
||||||
ou de toutes les évaluations d'un module (si moduleimpl_id)
|
ou de toutes les évaluations d'un module (si moduleimpl_id)
|
||||||
@ -71,7 +71,7 @@ def do_evaluation_listenotes(
|
|||||||
else:
|
else:
|
||||||
raise ValueError("missing argument: evaluation or module")
|
raise ValueError("missing argument: evaluation or module")
|
||||||
if not evals:
|
if not evals:
|
||||||
return "<p>Aucune évaluation !</p>"
|
return "<p>Aucune évaluation !</p>", f"ScoDoc"
|
||||||
|
|
||||||
E = evals[0] # il y a au moins une evaluation
|
E = evals[0] # il y a au moins une evaluation
|
||||||
modimpl = ModuleImpl.query.get(E["moduleimpl_id"])
|
modimpl = ModuleImpl.query.get(E["moduleimpl_id"])
|
||||||
@ -189,9 +189,12 @@ def do_evaluation_listenotes(
|
|||||||
if tf[0] == 0:
|
if tf[0] == 0:
|
||||||
return "\n".join(H) + "\n" + tf[1], page_title
|
return "\n".join(H) + "\n" + tf[1], page_title
|
||||||
elif tf[0] == -1:
|
elif tf[0] == -1:
|
||||||
return flask.redirect(
|
return (
|
||||||
|
flask.redirect(
|
||||||
"%s/Notes/moduleimpl_status?moduleimpl_id=%s"
|
"%s/Notes/moduleimpl_status?moduleimpl_id=%s"
|
||||||
% (scu.ScoURL(), E["moduleimpl_id"])
|
% (scu.ScoURL(), E["moduleimpl_id"])
|
||||||
|
),
|
||||||
|
"",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
anonymous_listing = tf[2]["anonymous_listing"]
|
anonymous_listing = tf[2]["anonymous_listing"]
|
||||||
|
@ -42,9 +42,6 @@ Les images sont servies par ScoDoc, via la méthode getphotofile?etudid=xxx
|
|||||||
- support for legacy ZODB removed in v1909.
|
- support for legacy ZODB removed in v1909.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask.helpers import make_response, url_for
|
|
||||||
from app.scodoc.sco_exceptions import ScoGenError
|
|
||||||
import datetime
|
import datetime
|
||||||
import glob
|
import glob
|
||||||
import io
|
import io
|
||||||
@ -52,24 +49,26 @@ import os
|
|||||||
import random
|
import random
|
||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
import traceback
|
|
||||||
|
|
||||||
import PIL
|
import PIL
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
|
|
||||||
from flask import request, g
|
from flask import request, g
|
||||||
|
from flask.helpers import make_response, url_for
|
||||||
|
|
||||||
from config import Config
|
from app import log
|
||||||
|
from app import db
|
||||||
|
from app.models import Identite
|
||||||
from app.scodoc import sco_etud
|
from app.scodoc import sco_etud
|
||||||
from app.scodoc import sco_portal_apogee
|
from app.scodoc import sco_portal_apogee
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app import log
|
from app.scodoc.sco_exceptions import ScoGenError
|
||||||
from app.scodoc.scolog import logdb
|
from app.scodoc.scolog import logdb
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
from config import Config
|
||||||
|
|
||||||
# Full paths on server's filesystem. Something like "/opt/scodoc/var/scodoc/photos"
|
# Full paths on server's filesystem. Something like "/opt/scodoc-data/photos"
|
||||||
PHOTO_DIR = os.path.join(Config.SCODOC_VAR_DIR, "photos")
|
PHOTO_DIR = os.path.join(Config.SCODOC_VAR_DIR, "photos")
|
||||||
ICONS_DIR = os.path.join(Config.SCODOC_DIR, "app", "static", "icons")
|
ICONS_DIR = os.path.join(Config.SCODOC_DIR, "app", "static", "icons")
|
||||||
UNKNOWN_IMAGE_PATH = os.path.join(ICONS_DIR, "unknown.jpg")
|
UNKNOWN_IMAGE_PATH = os.path.join(ICONS_DIR, "unknown.jpg")
|
||||||
@ -97,14 +96,15 @@ def get_etud_photo_url(etudid, size="small"):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def etud_photo_url(etud, size="small", fast=False):
|
def etud_photo_url(etud: dict, size="small", fast=False) -> str:
|
||||||
"""url to the image of the student, in "small" size or "orig" size.
|
"""url to the image of the student, in "small" size or "orig" size.
|
||||||
If ScoDoc doesn't have an image and a portal is configured, link to it.
|
If ScoDoc doesn't have an image and a portal is configured, link to it.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
photo_url = get_etud_photo_url(etud["etudid"], size=size)
|
photo_url = get_etud_photo_url(etud["etudid"], size=size)
|
||||||
if fast:
|
if fast:
|
||||||
return photo_url
|
return photo_url
|
||||||
path = photo_pathname(etud, size=size)
|
path = photo_pathname(etud["photo_filename"], size=size)
|
||||||
if not path:
|
if not path:
|
||||||
# Portail ?
|
# Portail ?
|
||||||
ext_url = photo_portal_url(etud)
|
ext_url = photo_portal_url(etud)
|
||||||
@ -131,8 +131,8 @@ def get_photo_image(etudid=None, size="small"):
|
|||||||
if not etudid:
|
if not etudid:
|
||||||
filename = UNKNOWN_IMAGE_PATH
|
filename = UNKNOWN_IMAGE_PATH
|
||||||
else:
|
else:
|
||||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
etud = Identite.query.get_or_404(etudid)
|
||||||
filename = photo_pathname(etud, size=size)
|
filename = photo_pathname(etud.photo_filename, size=size)
|
||||||
if not filename:
|
if not filename:
|
||||||
filename = UNKNOWN_IMAGE_PATH
|
filename = UNKNOWN_IMAGE_PATH
|
||||||
return _http_jpeg_file(filename)
|
return _http_jpeg_file(filename)
|
||||||
@ -171,8 +171,8 @@ def _http_jpeg_file(filename):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def etud_photo_is_local(etud, size="small"):
|
def etud_photo_is_local(etud: dict, size="small"):
|
||||||
return photo_pathname(etud, size=size)
|
return photo_pathname(etud["photo_filename"], size=size)
|
||||||
|
|
||||||
|
|
||||||
def etud_photo_html(etud=None, etudid=None, title=None, size="small"):
|
def etud_photo_html(etud=None, etudid=None, title=None, size="small"):
|
||||||
@ -215,9 +215,12 @@ def etud_photo_orig_html(etud=None, etudid=None, title=None):
|
|||||||
return etud_photo_html(etud=etud, etudid=etudid, title=title, size="orig")
|
return etud_photo_html(etud=etud, etudid=etudid, title=title, size="orig")
|
||||||
|
|
||||||
|
|
||||||
def photo_pathname(etud, size="orig"):
|
def photo_pathname(photo_filename: str, size="orig"):
|
||||||
"""Returns full path of image file if etud has a photo (in the filesystem), or False.
|
"""Returns full path of image file if etud has a photo (in the filesystem),
|
||||||
|
or False.
|
||||||
Do not distinguish the cases: no photo, or file missing.
|
Do not distinguish the cases: no photo, or file missing.
|
||||||
|
Argument: photo_filename (Identite attribute)
|
||||||
|
Resultat: False or str
|
||||||
"""
|
"""
|
||||||
if size == "small":
|
if size == "small":
|
||||||
version = H90
|
version = H90
|
||||||
@ -225,9 +228,9 @@ def photo_pathname(etud, size="orig"):
|
|||||||
version = ""
|
version = ""
|
||||||
else:
|
else:
|
||||||
raise ValueError("invalid size parameter for photo")
|
raise ValueError("invalid size parameter for photo")
|
||||||
if not etud["photo_filename"]:
|
if not photo_filename:
|
||||||
return False
|
return False
|
||||||
path = os.path.join(PHOTO_DIR, etud["photo_filename"]) + version + IMAGE_EXT
|
path = os.path.join(PHOTO_DIR, photo_filename) + version + IMAGE_EXT
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
return path
|
return path
|
||||||
else:
|
else:
|
||||||
@ -264,15 +267,14 @@ def store_photo(etud, data):
|
|||||||
return 1, "ok"
|
return 1, "ok"
|
||||||
|
|
||||||
|
|
||||||
def suppress_photo(etud):
|
def suppress_photo(etud: Identite) -> None:
|
||||||
"""Suppress a photo"""
|
"""Suppress a photo"""
|
||||||
log("suppress_photo etudid=%s" % etud["etudid"])
|
log("suppress_photo etudid=%s" % etud.id)
|
||||||
rel_path = photo_pathname(etud)
|
rel_path = photo_pathname(etud.photo_filename)
|
||||||
# 1- remove ref. from database
|
# 1- remove ref. from database
|
||||||
etud["photo_filename"] = None
|
etud.photo_filename = None
|
||||||
cnx = ndb.GetDBConnexion()
|
db.session.add(etud)
|
||||||
sco_etud.identite_edit_nocheck(cnx, etud)
|
|
||||||
cnx.commit()
|
|
||||||
# 2- erase images files
|
# 2- erase images files
|
||||||
if rel_path:
|
if rel_path:
|
||||||
# remove extension and glob
|
# remove extension and glob
|
||||||
@ -281,8 +283,10 @@ def suppress_photo(etud):
|
|||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
log("removing file %s" % filename)
|
log("removing file %s" % filename)
|
||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
|
db.session.commit()
|
||||||
# 3- log
|
# 3- log
|
||||||
logdb(cnx, method="changePhoto", msg="suppression", etudid=etud["etudid"])
|
cnx = ndb.GetDBConnexion()
|
||||||
|
logdb(cnx, method="changePhoto", msg="suppression", etudid=etud.id)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@ -373,6 +377,9 @@ def copy_portal_photo_to_fs(etud):
|
|||||||
log("copy_portal_photo_to_fs: failure (exception in store_photo)!")
|
log("copy_portal_photo_to_fs: failure (exception in store_photo)!")
|
||||||
if status == 1:
|
if status == 1:
|
||||||
log("copy_portal_photo_to_fs: copied %s" % url)
|
log("copy_portal_photo_to_fs: copied %s" % url)
|
||||||
return photo_pathname(etud), "%s: photo chargée" % etud["nomprenom"]
|
return (
|
||||||
|
photo_pathname(etud["photo_filename"]),
|
||||||
|
f"{etud['nomprenom']}: photo chargée",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return None, "%s: <b>%s</b>" % (etud["nomprenom"], diag)
|
return None, "%s: <b>%s</b>" % (etud["nomprenom"], diag)
|
||||||
|
@ -183,10 +183,11 @@ def trombino_html(groups_infos):
|
|||||||
|
|
||||||
|
|
||||||
def check_local_photos_availability(groups_infos, format=""):
|
def check_local_photos_availability(groups_infos, format=""):
|
||||||
"""Verifie que toutes les photos (des gropupes indiqués) sont copiées localement
|
"""Vérifie que toutes les photos (des groupes indiqués) sont copiées
|
||||||
dans ScoDoc (seules les photos dont nous disposons localement peuvent être exportées
|
localement dans ScoDoc (seules les photos dont nous disposons localement
|
||||||
en pdf ou en zip).
|
peuvent être exportées en pdf ou en zip).
|
||||||
Si toutes ne sont pas dispo, retourne un dialogue d'avertissement pour l'utilisateur.
|
Si toutes ne sont pas dispo, retourne un dialogue d'avertissement
|
||||||
|
pour l'utilisateur.
|
||||||
"""
|
"""
|
||||||
nb_missing = 0
|
nb_missing = 0
|
||||||
for t in groups_infos.members:
|
for t in groups_infos.members:
|
||||||
@ -221,7 +222,7 @@ def _trombino_zip(groups_infos):
|
|||||||
# assume we have the photos (or the user acknowledged the fact)
|
# assume we have the photos (or the user acknowledged the fact)
|
||||||
# Archive originals (not reduced) images, in JPEG
|
# Archive originals (not reduced) images, in JPEG
|
||||||
for t in groups_infos.members:
|
for t in groups_infos.members:
|
||||||
im_path = sco_photos.photo_pathname(t, size="orig")
|
im_path = sco_photos.photo_pathname(t["photo_filename"], size="orig")
|
||||||
if not im_path:
|
if not im_path:
|
||||||
continue
|
continue
|
||||||
img = open(im_path, "rb").read()
|
img = open(im_path, "rb").read()
|
||||||
@ -292,9 +293,9 @@ def trombino_copy_photos(group_ids=[], dialog_confirmed=False):
|
|||||||
|
|
||||||
|
|
||||||
def _get_etud_platypus_image(t, image_width=2 * cm):
|
def _get_etud_platypus_image(t, image_width=2 * cm):
|
||||||
"""Returns aplatypus object for the photo of student t"""
|
"""Returns a platypus object for the photo of student t"""
|
||||||
try:
|
try:
|
||||||
path = sco_photos.photo_pathname(t, size="small")
|
path = sco_photos.photo_pathname(t["photo_filename"], size="small")
|
||||||
if not path:
|
if not path:
|
||||||
# log('> unknown')
|
# log('> unknown')
|
||||||
path = sco_photos.UNKNOWN_IMAGE_PATH
|
path = sco_photos.UNKNOWN_IMAGE_PATH
|
||||||
|
@ -72,7 +72,7 @@ NOTES_SUPPRESS = -1001.0 # note a supprimer
|
|||||||
NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutralisee)
|
NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutralisee)
|
||||||
|
|
||||||
# ---- CODES INSCRIPTION AUX SEMESTRES
|
# ---- CODES INSCRIPTION AUX SEMESTRES
|
||||||
# (champ etat de FormsemestreInscription)
|
# (champ etat de FormSemestreInscription)
|
||||||
INSCRIT = "I"
|
INSCRIT = "I"
|
||||||
DEMISSION = "D"
|
DEMISSION = "D"
|
||||||
DEF = "DEF"
|
DEF = "DEF"
|
||||||
|
@ -22,10 +22,9 @@
|
|||||||
.dateInscription,
|
.dateInscription,
|
||||||
.numerosEtudiant,
|
.numerosEtudiant,
|
||||||
.dateNaissance{
|
.dateNaissance{
|
||||||
/*display: none;*/
|
display: none;
|
||||||
}`;
|
}`;
|
||||||
releve.shadowRoot.appendChild(style);
|
releve.shadowRoot.appendChild(style);
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -1739,7 +1739,7 @@ def evaluation_listenotes():
|
|||||||
mode = "module"
|
mode = "module"
|
||||||
|
|
||||||
format = vals.get("format", "html")
|
format = vals.get("format", "html")
|
||||||
B, page_title = sco_liste_notes.do_evaluation_listenotes(
|
html_content, page_title = sco_liste_notes.do_evaluation_listenotes(
|
||||||
evaluation_id=evaluation_id, moduleimpl_id=moduleimpl_id, format=format
|
evaluation_id=evaluation_id, moduleimpl_id=moduleimpl_id, format=format
|
||||||
)
|
)
|
||||||
if format == "html":
|
if format == "html":
|
||||||
@ -1750,9 +1750,9 @@ def evaluation_listenotes():
|
|||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
)
|
)
|
||||||
F = html_sco_header.sco_footer()
|
F = html_sco_header.sco_footer()
|
||||||
return H + B + F
|
return H + html_content + F
|
||||||
else:
|
else:
|
||||||
return B
|
return html_content
|
||||||
|
|
||||||
|
|
||||||
sco_publish(
|
sco_publish(
|
||||||
|
@ -54,7 +54,7 @@ from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
|
|||||||
|
|
||||||
import app
|
import app
|
||||||
from app.models import Departement, Identite
|
from app.models import Departement, Identite
|
||||||
from app.models import FormSemestre, FormsemestreInscription
|
from app.models import FormSemestre, FormSemestreInscription
|
||||||
from app.models import ScoDocSiteConfig
|
from app.models import ScoDocSiteConfig
|
||||||
import sco_version
|
import sco_version
|
||||||
from app.scodoc import sco_logos, sco_config_form
|
from app.scodoc import sco_logos, sco_config_form
|
||||||
|
@ -49,6 +49,7 @@ from app.decorators import (
|
|||||||
admin_required,
|
admin_required,
|
||||||
login_required,
|
login_required,
|
||||||
)
|
)
|
||||||
|
from app.models.etudiants import Identite
|
||||||
|
|
||||||
from app.views import scolar_bp as bp
|
from app.views import scolar_bp as bp
|
||||||
|
|
||||||
@ -944,21 +945,21 @@ def formChangePhoto(etudid=None):
|
|||||||
@scodoc7func
|
@scodoc7func
|
||||||
def formSuppressPhoto(etudid=None, dialog_confirmed=False):
|
def formSuppressPhoto(etudid=None, dialog_confirmed=False):
|
||||||
"""Formulaire suppression photo étudiant"""
|
"""Formulaire suppression photo étudiant"""
|
||||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
etud = Identite.query.get_or_404(etudid)
|
||||||
if not dialog_confirmed:
|
if not dialog_confirmed:
|
||||||
return scu.confirm_dialog(
|
return scu.confirm_dialog(
|
||||||
"<p>Confirmer la suppression de la photo de %(nomprenom)s ?</p>" % etud,
|
f"<p>Confirmer la suppression de la photo de {etud.nom_disp()} ?</p>",
|
||||||
dest_url="",
|
dest_url="",
|
||||||
cancel_url=url_for(
|
cancel_url=url_for(
|
||||||
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid
|
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id
|
||||||
),
|
),
|
||||||
parameters={"etudid": etudid},
|
parameters={"etudid": etud.id},
|
||||||
)
|
)
|
||||||
|
|
||||||
sco_photos.suppress_photo(etud)
|
sco_photos.suppress_photo(etud)
|
||||||
|
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""index in FormsemestreInscription
|
"""index in FormSemestreInscription
|
||||||
|
|
||||||
Revision ID: 4f98a8b02c89
|
Revision ID: 4f98a8b02c89
|
||||||
Revises: a57a6ee2e3cb
|
Revises: a57a6ee2e3cb
|
||||||
@ -10,23 +10,47 @@ import sqlalchemy as sa
|
|||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = '4f98a8b02c89'
|
revision = "4f98a8b02c89"
|
||||||
down_revision = 'a57a6ee2e3cb'
|
down_revision = "a57a6ee2e3cb"
|
||||||
branch_labels = None
|
branch_labels = None
|
||||||
depends_on = None
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.create_index(op.f('ix_notes_formsemestre_inscription_etat'), 'notes_formsemestre_inscription', ['etat'], unique=False)
|
op.create_index(
|
||||||
op.create_index(op.f('ix_notes_formsemestre_inscription_etudid'), 'notes_formsemestre_inscription', ['etudid'], unique=False)
|
op.f("ix_notes_formsemestre_inscription_etat"),
|
||||||
op.create_index(op.f('ix_notes_formsemestre_inscription_formsemestre_id'), 'notes_formsemestre_inscription', ['formsemestre_id'], unique=False)
|
"notes_formsemestre_inscription",
|
||||||
|
["etat"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_notes_formsemestre_inscription_etudid"),
|
||||||
|
"notes_formsemestre_inscription",
|
||||||
|
["etudid"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_notes_formsemestre_inscription_formsemestre_id"),
|
||||||
|
"notes_formsemestre_inscription",
|
||||||
|
["formsemestre_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
def downgrade():
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.drop_index(op.f('ix_notes_formsemestre_inscription_formsemestre_id'), table_name='notes_formsemestre_inscription')
|
op.drop_index(
|
||||||
op.drop_index(op.f('ix_notes_formsemestre_inscription_etudid'), table_name='notes_formsemestre_inscription')
|
op.f("ix_notes_formsemestre_inscription_formsemestre_id"),
|
||||||
op.drop_index(op.f('ix_notes_formsemestre_inscription_etat'), table_name='notes_formsemestre_inscription')
|
table_name="notes_formsemestre_inscription",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_notes_formsemestre_inscription_etudid"),
|
||||||
|
table_name="notes_formsemestre_inscription",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_notes_formsemestre_inscription_etat"),
|
||||||
|
table_name="notes_formsemestre_inscription",
|
||||||
|
)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
40
migrations/versions/91be8a06d423_user_token_size_limit.py
Normal file
40
migrations/versions/91be8a06d423_user_token_size_limit.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"""user token size limit
|
||||||
|
|
||||||
|
Revision ID: 91be8a06d423
|
||||||
|
Revises: 4f98a8b02c89
|
||||||
|
Create Date: 2021-12-20 22:48:42.390743
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "91be8a06d423"
|
||||||
|
down_revision = "4f98a8b02c89"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column(
|
||||||
|
"user",
|
||||||
|
"token",
|
||||||
|
existing_type=sa.VARCHAR(length=32),
|
||||||
|
type_=sa.Text(),
|
||||||
|
existing_nullable=True,
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column(
|
||||||
|
"user",
|
||||||
|
"token",
|
||||||
|
existing_type=sa.Text(),
|
||||||
|
type_=sa.VARCHAR(length=32),
|
||||||
|
existing_nullable=True,
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.1.11"
|
SCOVERSION = "9.1.12"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
33
scodoc.py
33
scodoc.py
@ -24,7 +24,7 @@ from app.auth.models import User, Role, UserRole
|
|||||||
from app.models import ScoPreference
|
from app.models import ScoPreference
|
||||||
from app.scodoc.sco_logos import make_logo_local
|
from app.scodoc.sco_logos import make_logo_local
|
||||||
from app.models import Formation, UniteEns, Module
|
from app.models import Formation, UniteEns, Module
|
||||||
from app.models import FormSemestre, FormsemestreInscription
|
from app.models import FormSemestre, FormSemestreInscription
|
||||||
from app.models import ModuleImpl, ModuleImplInscription
|
from app.models import ModuleImpl, ModuleImplInscription
|
||||||
from app.models import Identite
|
from app.models import Identite
|
||||||
from app.models.evaluations import Evaluation
|
from app.models.evaluations import Evaluation
|
||||||
@ -57,7 +57,7 @@ def make_shell_context():
|
|||||||
"flask": flask,
|
"flask": flask,
|
||||||
"Formation": Formation,
|
"Formation": Formation,
|
||||||
"FormSemestre": FormSemestre,
|
"FormSemestre": FormSemestre,
|
||||||
"FormsemestreInscription": FormsemestreInscription,
|
"FormSemestreInscription": FormSemestreInscription,
|
||||||
"Identite": Identite,
|
"Identite": Identite,
|
||||||
"login_user": login_user,
|
"login_user": login_user,
|
||||||
"logout_user": logout_user,
|
"logout_user": logout_user,
|
||||||
@ -248,6 +248,35 @@ def edit_role(rolename, addpermissionname=None, removepermissionname=None): # e
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
@app.cli.command()
|
||||||
|
@click.argument("username")
|
||||||
|
@click.option("-d", "--dept", "dept_acronym")
|
||||||
|
@click.option("-a", "--add", "add_role_name")
|
||||||
|
@click.option("-r", "--remove", "remove_role_name")
|
||||||
|
def user_role(username, dept_acronym=None, add_role_name=None, remove_role_name=None):
|
||||||
|
"""Add or remove a role to the given user in the given dept"""
|
||||||
|
user = User.query.filter_by(user_name=username).first()
|
||||||
|
if not user:
|
||||||
|
sys.stderr.write(f"user_role: user {username} does not exists\n")
|
||||||
|
return 1
|
||||||
|
if dept_acronym:
|
||||||
|
dept = models.Departement.query.filter_by(acronym=dept_acronym).first()
|
||||||
|
if dept is None:
|
||||||
|
sys.stderr.write(f"Erreur: le departement {dept} n'existe pas !\n")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
if add_role_name:
|
||||||
|
role = Role.query.filter_by(name=add_role_name).first()
|
||||||
|
user.add_role(role, dept_acronym)
|
||||||
|
if remove_role_name:
|
||||||
|
role = Role.query.filter_by(name=remove_role_name).first()
|
||||||
|
user_role = UserRole.query.filter(
|
||||||
|
UserRole.role == role, UserRole.user == user, UserRole.dept == dept_acronym
|
||||||
|
).first()
|
||||||
|
db.session.delete(user_role)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@app.cli.command()
|
@app.cli.command()
|
||||||
@click.argument("dept")
|
@click.argument("dept")
|
||||||
def delete_dept(dept): # delete-dept
|
def delete_dept(dept): # delete-dept
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""Exemple utilisation API ScoDoc 9 avec jeton obtenu par basic athentication
|
"""Exemple utilisation API ScoDoc 9 avec jeton obtenu par basic authentication
|
||||||
|
|
||||||
|
|
||||||
Utilisation: créer les variables d'environnement: (indiquer les valeurs
|
Utilisation: créer les variables d'environnement: (indiquer les valeurs
|
||||||
@ -80,6 +80,15 @@ if r.status_code != 200:
|
|||||||
|
|
||||||
pp(r.json())
|
pp(r.json())
|
||||||
|
|
||||||
|
# Liste des tous les étudiants en cours (de tous les depts)
|
||||||
|
r = requests.get(
|
||||||
|
SCODOC_URL + "/ScoDoc/api/etudiants/courant",
|
||||||
|
headers=HEADERS,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
if r.status_code != 200:
|
||||||
|
raise ScoError("erreur de connexion: vérifier adresse et identifiants")
|
||||||
|
|
||||||
|
|
||||||
# # --- Recupere la liste de tous les semestres:
|
# # --- Recupere la liste de tous les semestres:
|
||||||
# sems = GET(s, "Notes/formsemestre_list?format=json", "Aucun semestre !")
|
# sems = GET(s, "Notes/formsemestre_list?format=json", "Aucun semestre !")
|
||||||
|
Loading…
Reference in New Issue
Block a user