forked from ScoDoc/ScoDoc
Merge branch 'master' of https://scodoc.org/git/ScoDoc/ScoDoc into entreprises
This commit is contained in:
commit
f0edcb28f7
@ -22,8 +22,6 @@ def requested_format(default_format="json", allowed_formats=None):
|
|||||||
|
|
||||||
|
|
||||||
from app.api import tokens
|
from app.api import tokens
|
||||||
from app.api import sco_api
|
|
||||||
from app.api import test_api
|
|
||||||
from app.api import departements
|
from app.api import departements
|
||||||
from app.api import etudiants
|
from app.api import etudiants
|
||||||
from app.api import formations
|
from app.api import formations
|
||||||
|
@ -4,73 +4,63 @@ from flask import jsonify
|
|||||||
|
|
||||||
from app.api import bp
|
from app.api import bp
|
||||||
from app.api.errors import error_response
|
from app.api.errors import error_response
|
||||||
from app.api.auth import token_permission_required
|
from app.api.auth import token_auth, token_permission_required
|
||||||
from app.api.tools import get_etu_from_etudid_or_nip_or_ine
|
from app.models import Identite
|
||||||
from app.scodoc import notesdb as ndb
|
|
||||||
|
|
||||||
|
from app.scodoc import notesdb as ndb
|
||||||
from app.scodoc import sco_abs
|
from app.scodoc import sco_abs
|
||||||
from app.scodoc.sco_groups import get_group_members
|
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/absences/etudid/<int:etudid>", methods=["GET"])
|
@bp.route("/absences/etudid/<int:etudid>", methods=["GET"])
|
||||||
@bp.route("/absences/nip/<int:nip>", methods=["GET"])
|
@token_auth.login_required
|
||||||
@bp.route("/absences/ine/<int:ine>", methods=["GET"])
|
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def absences(etudid: int = None, nip: int = None, ine: int = None):
|
def absences(etudid: int = None):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des absences d'un étudiant donné
|
Retourne la liste des absences d'un étudiant donné
|
||||||
|
|
||||||
etudid : l'etudid d'un étudiant
|
etudid : l'etudid d'un étudiant
|
||||||
nip: le code nip d'un étudiant
|
|
||||||
ine : le code ine d'un étudiant
|
|
||||||
|
|
||||||
Exemple de résultat:
|
Exemple de résultat:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"jour": "2022-04-15",
|
"jour": "2022-04-15",
|
||||||
"matin": true,
|
"matin": true,
|
||||||
"estabs": true,
|
"estabs": true,
|
||||||
"estjust": true,
|
"estjust": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"begin": "2022-04-15 08:00:00",
|
"begin": "2022-04-15 08:00:00",
|
||||||
"end": "2022-04-15 11:59:59"
|
"end": "2022-04-15 11:59:59"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"jour": "2022-04-15",
|
"jour": "2022-04-15",
|
||||||
"matin": false,
|
"matin": false,
|
||||||
"estabs": true,
|
"estabs": true,
|
||||||
"estjust": false,
|
"estjust": false,
|
||||||
"description": "",
|
"description": "",
|
||||||
"begin": "2022-04-15 12:00:00",
|
"begin": "2022-04-15 12:00:00",
|
||||||
"end": "2022-04-15 17:59:59"
|
"end": "2022-04-15 17:59:59"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
if etudid is None:
|
etud = Identite.query.get(etudid)
|
||||||
# Récupération de l'étudiant
|
if etud is None:
|
||||||
etud = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
return error_response(
|
||||||
if etud is None:
|
404,
|
||||||
return error_response(
|
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
||||||
409,
|
)
|
||||||
message="La requête ne peut être traitée en l’état actuel.\n "
|
# Absences de l'étudiant
|
||||||
"Veuillez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide",
|
|
||||||
)
|
|
||||||
etudid = etud.etudid
|
|
||||||
|
|
||||||
# Récupération des absences de l'étudiant
|
|
||||||
ndb.open_db_connection()
|
ndb.open_db_connection()
|
||||||
absences = sco_abs.list_abs_date(etudid)
|
absences = sco_abs.list_abs_date(etud.id)
|
||||||
for absence in absences:
|
for absence in absences:
|
||||||
absence["jour"] = absence["jour"].isoformat()
|
absence["jour"] = absence["jour"].isoformat()
|
||||||
return jsonify(absences)
|
return jsonify(absences)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"])
|
@bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"])
|
||||||
@bp.route("/absences/nip/<int:nip>/just", methods=["GET"])
|
@token_auth.login_required
|
||||||
@bp.route("/absences/ine/<int:ine>/just", methods=["GET"])
|
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def absences_just(etudid: int = None, nip: int = None, ine: int = None):
|
def absences_just(etudid: int = None):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des absences justifiées d'un étudiant donné
|
Retourne la liste des absences justifiées d'un étudiant donné
|
||||||
|
|
||||||
@ -79,76 +69,77 @@ def absences_just(etudid: int = None, nip: int = None, ine: int = None):
|
|||||||
ine : le code ine d'un étudiant
|
ine : le code ine d'un étudiant
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"jour": "2022-04-15",
|
"jour": "2022-04-15",
|
||||||
"matin": true,
|
"matin": true,
|
||||||
"estabs": true,
|
"estabs": true,
|
||||||
"estjust": true,
|
"estjust": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"begin": "2022-04-15 08:00:00",
|
"begin": "2022-04-15 08:00:00",
|
||||||
"end": "2022-04-15 11:59:59"
|
"end": "2022-04-15 11:59:59"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"jour": "Fri, 15 Apr 2022 00:00:00 GMT",
|
"jour": "Fri, 15 Apr 2022 00:00:00 GMT",
|
||||||
"matin": false,
|
"matin": false,
|
||||||
"estabs": true,
|
"estabs": true,
|
||||||
"estjust": true,
|
"estjust": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"begin": "2022-04-15 12:00:00",
|
"begin": "2022-04-15 12:00:00",
|
||||||
"end": "2022-04-15 17:59:59"
|
"end": "2022-04-15 17:59:59"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
if etudid is None:
|
etud = Identite.query.get(etudid)
|
||||||
# Récupération de l'étudiant
|
if etud is None:
|
||||||
try:
|
|
||||||
etu = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
|
||||||
etudid = etu.etudid
|
|
||||||
except AttributeError:
|
|
||||||
return error_response(
|
|
||||||
409,
|
|
||||||
message="La requête ne peut être traitée en l’état actuel.\n "
|
|
||||||
"Veuillez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Récupération des absences justifiées de l'étudiant
|
|
||||||
absences = sco_abs.list_abs_date(etudid)
|
|
||||||
for absence in [absence for absence in absences if absence["estjust"]]:
|
|
||||||
absence["jour"] = absence["jour"].isoformat()
|
|
||||||
return jsonify(absences)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route(
|
|
||||||
"/absences/abs_group_etat/<int:group_id>",
|
|
||||||
methods=["GET"],
|
|
||||||
)
|
|
||||||
@bp.route(
|
|
||||||
"/absences/abs_group_etat/group_id/<int:group_id>/date_debut/<string:date_debut>/date_fin/<string:date_fin>",
|
|
||||||
methods=["GET"],
|
|
||||||
)
|
|
||||||
@token_permission_required(Permission.APIView)
|
|
||||||
def abs_groupe_etat( # XXX A REVOIR XXX
|
|
||||||
group_id: int, date_debut, date_fin, with_boursier=True, format="html"
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Retoune la liste des absences d'un ou plusieurs groupes entre deux dates
|
|
||||||
"""
|
|
||||||
# Fonction utilisée : app.scodoc.sco_groups.get_group_members() et app.scodoc.sco_abs.list_abs_date()
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Utilisation de la fonction get_group_members
|
|
||||||
members = get_group_members(group_id)
|
|
||||||
except ValueError:
|
|
||||||
return error_response(
|
return error_response(
|
||||||
409, message="La requête ne peut être traitée en l’état actuel"
|
404,
|
||||||
|
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
||||||
)
|
)
|
||||||
|
|
||||||
data = []
|
# Absences justifiées de l'étudiant
|
||||||
# Filtre entre les deux dates renseignées
|
abs_just = [
|
||||||
for member in members:
|
absence for absence in sco_abs.list_abs_date(etud.id) if absence["estjust"]
|
||||||
abs = sco_abs.list_abs_date(member.id, date_debut, date_fin)
|
]
|
||||||
data.append(abs)
|
for absence in abs_just:
|
||||||
|
absence["jour"] = absence["jour"].isoformat()
|
||||||
|
return jsonify(abs_just)
|
||||||
|
|
||||||
# return jsonify(data) # XXX TODO faire en sorte de pouvoir renvoyer sa (ex to_dict() dans absences)
|
|
||||||
return error_response(501, message="Not implemented")
|
# XXX TODO INACHEVEE
|
||||||
|
# @bp.route(
|
||||||
|
# "/absences/abs_group_etat/<int:group_id>",
|
||||||
|
# methods=["GET"],
|
||||||
|
# )
|
||||||
|
# @bp.route(
|
||||||
|
# "/absences/abs_group_etat/group_id/<int:group_id>/date_debut/<string:date_debut>/date_fin/<string:date_fin>",
|
||||||
|
# methods=["GET"],
|
||||||
|
# )
|
||||||
|
# @token_auth.login_required
|
||||||
|
# @token_permission_required(Permission.APIView)
|
||||||
|
# def abs_groupe_etat( # XXX A REVOIR XXX
|
||||||
|
# group_id: int, date_debut, date_fin, with_boursier=True, format="html"
|
||||||
|
# ):
|
||||||
|
# """
|
||||||
|
# Liste des absences d'un ou plusieurs groupes entre deux dates
|
||||||
|
# """
|
||||||
|
# return error_response(501, message="Not implemented")
|
||||||
|
|
||||||
|
# # Fonction utilisée : app.scodoc.sco_groups.get_group_members() et app.scodoc.sco_abs.list_abs_date()
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# # Utilisation de la fonction get_group_members
|
||||||
|
# members = get_group_members(group_id)
|
||||||
|
# except ValueError:
|
||||||
|
# return error_response(
|
||||||
|
# 404, message="La requête ne peut être traitée en l’état actuel"
|
||||||
|
# )
|
||||||
|
|
||||||
|
# data = []
|
||||||
|
# # Filtre entre les deux dates renseignées
|
||||||
|
# for member in members:
|
||||||
|
# abs = sco_abs.list_abs_date(member.id, date_debut, date_fin)
|
||||||
|
# data.append(abs)
|
||||||
|
|
||||||
|
# # return jsonify(data) # XXX TODO faire en sorte de pouvoir renvoyer sa (ex to_dict() dans absences)
|
||||||
|
# return error_response(501, message="Not implemented")
|
||||||
|
@ -30,6 +30,8 @@ from functools import wraps
|
|||||||
from flask import abort
|
from flask import abort
|
||||||
from flask import g
|
from flask import g
|
||||||
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
|
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
|
||||||
|
|
||||||
|
from app import log
|
||||||
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
|
||||||
|
|
||||||
@ -39,19 +41,23 @@ token_auth = HTTPTokenAuth()
|
|||||||
|
|
||||||
@basic_auth.verify_password
|
@basic_auth.verify_password
|
||||||
def verify_password(username, password):
|
def verify_password(username, password):
|
||||||
|
"Verify password for this user"
|
||||||
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
|
g.current_user = user
|
||||||
|
# note: est aussi basic_auth.current_user()
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
@basic_auth.error_handler
|
@basic_auth.error_handler
|
||||||
def basic_auth_error(status):
|
def basic_auth_error(status):
|
||||||
|
"error response (401 for invalid auth.)"
|
||||||
return error_response(status)
|
return error_response(status)
|
||||||
|
|
||||||
|
|
||||||
@token_auth.verify_token
|
@token_auth.verify_token
|
||||||
def verify_token(token):
|
def verify_token(token) -> User:
|
||||||
|
"Retrouve l'utilisateur à partir du jeton"
|
||||||
user = User.check_token(token) if token else None
|
user = User.check_token(token) if token else None
|
||||||
g.current_user = user
|
g.current_user = user
|
||||||
return user
|
return user
|
||||||
@ -59,6 +65,7 @@ def verify_token(token):
|
|||||||
|
|
||||||
@token_auth.error_handler
|
@token_auth.error_handler
|
||||||
def token_auth_error(status):
|
def token_auth_error(status):
|
||||||
|
"rréponse en cas d'erreur d'auth."
|
||||||
return error_response(status)
|
return error_response(status)
|
||||||
|
|
||||||
|
|
||||||
@ -68,16 +75,22 @@ def get_user_roles(user):
|
|||||||
|
|
||||||
|
|
||||||
def token_permission_required(permission):
|
def token_permission_required(permission):
|
||||||
|
"Décorateur pour les fontions de l'API ScoDoc"
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
scodoc_dept = getattr(g, "scodoc_dept", None)
|
# token_auth.login_required()
|
||||||
if hasattr(g, "current_user") and not g.current_user.has_permission(
|
current_user = basic_auth.current_user()
|
||||||
permission, scodoc_dept
|
if not current_user or not current_user.has_permission(permission, None):
|
||||||
):
|
if current_user:
|
||||||
|
log(f"API permission denied (user {current_user})")
|
||||||
|
else:
|
||||||
|
log("API permission denied (no user supplied)")
|
||||||
abort(403)
|
abort(403)
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
return decorated_function # login_required(decorated_function)
|
# return decorated_function(token_auth.login_required())
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
@ -1,55 +1,66 @@
|
|||||||
############################################### Departements ##########################################################
|
############################################### Departements ##########################################################
|
||||||
import app
|
|
||||||
|
|
||||||
from app import models
|
|
||||||
from app.api import bp
|
|
||||||
from app.api.auth import token_permission_required
|
|
||||||
from app.api.errors import error_response
|
|
||||||
from app.scodoc.sco_permissions import Permission
|
|
||||||
|
|
||||||
from flask import jsonify
|
from flask import jsonify
|
||||||
|
|
||||||
|
import app
|
||||||
|
from app import models
|
||||||
|
from app.api import bp
|
||||||
|
from app.api.auth import token_auth, token_permission_required
|
||||||
|
from app.models import Departement, FormSemestre
|
||||||
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
|
||||||
@bp.route("/departements", methods=["GET"])
|
|
||||||
|
def get_departement(dept_ident: str) -> Departement:
|
||||||
|
"Le departement, par id ou acronyme. Erreur 404 si pas trouvé."
|
||||||
|
try:
|
||||||
|
dept_id = int(dept_ident)
|
||||||
|
except ValueError:
|
||||||
|
dept_id = None
|
||||||
|
if dept_id is None:
|
||||||
|
return Departement.query.filter_by(acronym=dept_ident).first_or_404()
|
||||||
|
return Departement.query.get_or_404(dept_id)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/departements_ids", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def departements():
|
def departements_ids():
|
||||||
|
"""Liste des ids de départements"""
|
||||||
|
return jsonify([dept.id for dept in Departement.query])
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/departement/<string:dept_ident>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
|
@token_permission_required(Permission.APIView)
|
||||||
|
def departement(dept_ident: str):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des ids de départements visibles
|
Info sur un département. Accès par id ou acronyme.
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
[
|
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"acronym": "TAPI",
|
"acronym": "TAPI",
|
||||||
"description": null,
|
"description": null,
|
||||||
"visible": true,
|
"visible": true,
|
||||||
"date_creation": "Fri, 15 Apr 2022 12:19:28 GMT"
|
"date_creation": "Fri, 15 Apr 2022 12:19:28 GMT"
|
||||||
},
|
}
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"acronym": "MMI",
|
|
||||||
"description": null,
|
|
||||||
"visible": false,
|
|
||||||
"date_creation": "Fri, 18 Apr 2022 11:20:8 GMT"
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
# Récupération de tous les départements
|
dept = get_departement(dept_ident)
|
||||||
depts = models.Departement.query.all()
|
return jsonify(dept.to_dict())
|
||||||
|
|
||||||
# Mise en place de la liste avec tous les départements
|
|
||||||
data = [d.to_dict() for d in depts]
|
|
||||||
|
|
||||||
return jsonify(data)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/departements/<string:dept>/etudiants/liste", methods=["GET"])
|
@bp.route("/departements", methods=["GET"])
|
||||||
@bp.route(
|
@token_auth.login_required
|
||||||
"/departements/<string:dept>/etudiants/liste/<int:formsemestre_id>", methods=["GET"]
|
|
||||||
)
|
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def liste_etudiants(dept: str, formsemestre_id=None):
|
def departements():
|
||||||
|
"""Liste les départements"""
|
||||||
|
return jsonify([dept.to_dict() for dept in Departement.query])
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/departement/<string:dept_ident>/etudiants", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
|
@token_permission_required(Permission.APIView)
|
||||||
|
def list_etudiants(dept_ident: str):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des étudiants d'un département
|
Retourne la liste des étudiants d'un département
|
||||||
|
|
||||||
@ -59,56 +70,39 @@ def liste_etudiants(dept: str, formsemestre_id=None):
|
|||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"civilite": "X",
|
"civilite": "M",
|
||||||
"code_ine": null,
|
"ine": "7899X61616",
|
||||||
"code_nip": null,
|
"nip": "F6777H88",
|
||||||
"date_naissance": null,
|
"date_naissance": null,
|
||||||
"email": null,
|
"email": "toto@toto.fr",
|
||||||
"emailperso": null,
|
"emailperso": null,
|
||||||
"etudid": 18,
|
"etudid": 18,
|
||||||
"nom": "MOREL",
|
"nom": "MOREL",
|
||||||
"prenom": "JACQUES"
|
"prenom": "JACQUES"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"civilite": "X",
|
|
||||||
"code_ine": null,
|
|
||||||
"code_nip": null,
|
|
||||||
"date_naissance": null,
|
|
||||||
"email": null,
|
|
||||||
"emailperso": null,
|
|
||||||
"etudid": 19,
|
|
||||||
"nom": "FOURNIER",
|
|
||||||
"prenom": "ANNE"
|
|
||||||
},
|
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
# Si le formsemestre_id a été renseigné
|
# Le département, spécifié par un id ou un acronyme
|
||||||
if formsemestre_id is not None:
|
dept = get_departement(dept_ident)
|
||||||
# Récupération du formsemestre
|
|
||||||
formsemestre = models.FormSemestre.query.filter_by(
|
|
||||||
id=formsemestre_id
|
|
||||||
).first_or_404()
|
|
||||||
# Récupération du département
|
|
||||||
departement = formsemestre.departement
|
|
||||||
|
|
||||||
# Si le formsemestre_id n'a pas été renseigné
|
return jsonify([etud.to_dict_short() for etud in dept.etudiants])
|
||||||
else:
|
|
||||||
# Récupération du formsemestre
|
|
||||||
departement = models.Departement.query.filter_by(acronym=dept).first_or_404()
|
|
||||||
|
|
||||||
# Récupération des étudiants
|
|
||||||
etudiants = departement.etudiants.all()
|
|
||||||
|
|
||||||
# Mise en forme des données
|
|
||||||
list_etu = [etu.to_dict_bul(include_urls=False) for etu in etudiants]
|
|
||||||
|
|
||||||
return jsonify(list_etu)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/departements/<string:dept>/semestres_courants", methods=["GET"])
|
@bp.route("/departement/<string:dept_ident>/formsemestres_ids", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def liste_semestres_courant(dept: str):
|
def formsemestres_ids(dept_ident: str):
|
||||||
|
"""liste des ids formsemestre du département"""
|
||||||
|
# Le département, spécifié par un id ou un acronyme
|
||||||
|
dept = get_departement(dept_ident)
|
||||||
|
return jsonify([formsemestre.id for formsemestre in dept.formsemestres])
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/departement/<string:dept_ident>/formsemestres_courants", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
|
@token_permission_required(Permission.APIView)
|
||||||
|
def liste_semestres_courant(dept_ident: str):
|
||||||
"""
|
"""
|
||||||
Liste des semestres actifs d'un départements donné
|
Liste des semestres actifs d'un départements donné
|
||||||
|
|
||||||
@ -149,41 +143,14 @@ def liste_semestres_courant(dept: str):
|
|||||||
...
|
...
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
# Récupération des départements comportant l'acronym mit en paramètre
|
# Le département, spécifié par un id ou un acronyme
|
||||||
dept = models.Departement.query.filter_by(acronym=dept).first_or_404()
|
dept = get_departement(dept_ident)
|
||||||
|
|
||||||
# Récupération des semestres suivant id_dept
|
# Les semestres en cours de ce département
|
||||||
semestres = models.FormSemestre.query.filter_by(dept_id=dept.id, etat=True)
|
formsemestres = models.FormSemestre.query.filter(
|
||||||
|
FormSemestre.dept_id == dept.id,
|
||||||
|
FormSemestre.date_debut <= app.db.func.now(),
|
||||||
|
FormSemestre.date_fin >= app.db.func.now(),
|
||||||
|
)
|
||||||
|
|
||||||
# Mise en forme des données
|
return jsonify([d.to_dict() for d in formsemestres])
|
||||||
data = [d.to_dict() for d in semestres]
|
|
||||||
|
|
||||||
return jsonify(data)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route(
|
|
||||||
"/departements/<string:dept>/formations/<int:formation_id>/referentiel_competences",
|
|
||||||
methods=["GET"],
|
|
||||||
)
|
|
||||||
@token_permission_required(Permission.APIView)
|
|
||||||
def referenciel_competences(dept: str, formation_id: int):
|
|
||||||
"""
|
|
||||||
Retourne le référentiel de compétences
|
|
||||||
|
|
||||||
dept : l'acronym d'un département
|
|
||||||
formation_id : l'id d'une formation
|
|
||||||
"""
|
|
||||||
dept = models.Departement.query.filter_by(acronym=dept).first_or_404()
|
|
||||||
|
|
||||||
formation = models.Formation.query.filter_by(
|
|
||||||
id=formation_id, dept_id=dept.id
|
|
||||||
).first_or_404()
|
|
||||||
|
|
||||||
ref_comp = formation.referentiel_competence_id
|
|
||||||
|
|
||||||
if ref_comp is None:
|
|
||||||
return error_response(
|
|
||||||
204, message="Pas de référenciel de compétences pour cette formation"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return jsonify(ref_comp)
|
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
#################################################### Etudiants ########################################################
|
##############################################################################
|
||||||
|
# ScoDoc
|
||||||
|
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
||||||
|
# See LICENSE
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
API : accès aux étudiants
|
||||||
|
"""
|
||||||
|
|
||||||
from flask import jsonify
|
from flask import jsonify
|
||||||
|
|
||||||
import app
|
import app
|
||||||
from app import models
|
|
||||||
from app.api import bp
|
from app.api import bp
|
||||||
from app.api.errors import error_response
|
from app.api.errors import error_response
|
||||||
from app.api.auth import token_permission_required
|
from app.api.auth import token_auth, token_permission_required
|
||||||
from app.api.tools import get_etu_from_etudid_or_nip_or_ine
|
from app.models import Departement, FormSemestreInscription, FormSemestre, Identite
|
||||||
from app.models import FormSemestreInscription, FormSemestre, Identite
|
|
||||||
from app.scodoc import sco_bulletins
|
from app.scodoc import sco_bulletins
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
@ -16,10 +22,11 @@ from app.scodoc.sco_permissions import Permission
|
|||||||
|
|
||||||
@bp.route("/etudiants/courant", defaults={"long": False})
|
@bp.route("/etudiants/courant", defaults={"long": False})
|
||||||
@bp.route("/etudiants/courant/long", defaults={"long": True})
|
@bp.route("/etudiants/courant/long", defaults={"long": True})
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def etudiants_courant(long=False):
|
def etudiants_courant(long=False):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des étudiants courant
|
Liste des étudiants inscrits dans un formsemestre actuellement en cours.
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
[
|
[
|
||||||
@ -40,7 +47,6 @@ def etudiants_courant(long=False):
|
|||||||
...
|
...
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
# Récupération de tous les étudiants
|
|
||||||
etuds = Identite.query.filter(
|
etuds = Identite.query.filter(
|
||||||
Identite.id == FormSemestreInscription.etudid,
|
Identite.id == FormSemestreInscription.etudid,
|
||||||
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
||||||
@ -51,110 +57,178 @@ def etudiants_courant(long=False):
|
|||||||
data = [etud.to_dict_bul(include_urls=False) for etud in etuds]
|
data = [etud.to_dict_bul(include_urls=False) for etud in etuds]
|
||||||
else:
|
else:
|
||||||
data = [etud.to_dict_short() for etud in etuds]
|
data = [etud.to_dict_short() for etud in etuds]
|
||||||
print(jsonify(data))
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"])
|
@bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"])
|
||||||
@bp.route("/etudiant/nip/<int:nip>", methods=["GET"])
|
@bp.route("/etudiant/nip/<string:nip>", methods=["GET"])
|
||||||
@bp.route("/etudiant/ine/<int:ine>", methods=["GET"])
|
@bp.route("/etudiant/ine/<string:ine>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def etudiant(etudid: int = None, nip: int = None, ine: int = None):
|
def etudiant(etudid: int = None, nip: str = None, ine: str = None):
|
||||||
"""
|
"""
|
||||||
Retourne les informations de l'étudiant correspondant à l'id passé en paramètres.
|
Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé.
|
||||||
|
|
||||||
etudid : l'etudid d'un étudiant
|
etudid : l'etudid de l'étudiant
|
||||||
nip : le code nip d'un étudiant
|
nip : le code nip de l'étudiant
|
||||||
ine : le code ine d'un étudiant
|
ine : le code ine de l'étudiant
|
||||||
|
|
||||||
|
Les codes INE et NIP sont uniques au sein d'un département.
|
||||||
|
Si plusieurs objets ont le même code, on ramène le plus récemment inscrit.
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
{
|
{
|
||||||
"civilite": "X",
|
"civilite": "X",
|
||||||
"code_ine": "1",
|
"code_ine": "1",
|
||||||
"code_nip": "1",
|
"code_nip": "1",
|
||||||
"date_naissance": "",
|
"date_naissance": "",
|
||||||
"email": "SACHA.COSTA@example.com",
|
"email": "SACHA.COSTA@example.com",
|
||||||
"emailperso": "",
|
"emailperso": "",
|
||||||
"etudid": 1,
|
"etudid": 1,
|
||||||
"nom": "COSTA",
|
"nom": "COSTA",
|
||||||
"prenom": "SACHA",
|
"prenom": "SACHA",
|
||||||
"nomprenom": "Sacha COSTA",
|
"nomprenom": "Sacha COSTA",
|
||||||
"lieu_naissance": "",
|
"lieu_naissance": "",
|
||||||
"dept_naissance": "",
|
"dept_naissance": "",
|
||||||
"nationalite": "",
|
"nationalite": "",
|
||||||
"boursier": "",
|
"boursier": "",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"codepostaldomicile": "",
|
"codepostaldomicile": "",
|
||||||
"paysdomicile": "",
|
"paysdomicile": "",
|
||||||
"telephonemobile": "",
|
"telephonemobile": "",
|
||||||
"typeadresse": "domicile",
|
"typeadresse": "domicile",
|
||||||
"domicile": "",
|
"domicile": "",
|
||||||
"villedomicile": "",
|
"villedomicile": "",
|
||||||
"telephone": "",
|
"telephone": "",
|
||||||
"fax": "",
|
"fax": "",
|
||||||
"description": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# Récupération de l'étudiant
|
if etudid is not None:
|
||||||
etud = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
etud = Identite.query.get(etudid)
|
||||||
|
else:
|
||||||
|
if nip is not None:
|
||||||
|
query = Identite.query.filter_by(code_nip=nip)
|
||||||
|
elif ine is not None:
|
||||||
|
query = Identite.query.filter_by(code_ine=ine)
|
||||||
|
else:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="parametre manquant",
|
||||||
|
)
|
||||||
|
if query.count() > 1: # cas rare d'un étudiant présent dans plusieurs depts
|
||||||
|
etuds = []
|
||||||
|
for e in query:
|
||||||
|
admission = e.admission.first()
|
||||||
|
etuds.append((((admission.annee or 0) if admission else 0), e))
|
||||||
|
etuds.sort()
|
||||||
|
etud = etuds[-1][1]
|
||||||
|
else:
|
||||||
|
etud = query.first()
|
||||||
|
|
||||||
# Mise en forme des données
|
if etud is None:
|
||||||
data = etud.to_dict_bul(include_urls=False)
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="étudiant inconnu",
|
||||||
|
)
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(etud.to_dict_bul(include_urls=False))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"])
|
||||||
|
@bp.route("/etudiants/nip/<string:nip>", methods=["GET"])
|
||||||
|
@bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
|
@token_permission_required(Permission.APIView)
|
||||||
|
def etudiants(etudid: int = None, nip: str = None, ine: str = None):
|
||||||
|
"""
|
||||||
|
Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie
|
||||||
|
toujours une liste.
|
||||||
|
Si non trouvé, liste vide, pas d'erreur.
|
||||||
|
Dans 99% des cas, la liste contient un seul étudiant, mais si l'étudiant a
|
||||||
|
été inscrit dans plusieurs départements, on a plusieurs objets (1 par dept.).
|
||||||
|
"""
|
||||||
|
if etudid is not None:
|
||||||
|
query = Identite.query.filter_by(id=etudid)
|
||||||
|
elif nip is not None:
|
||||||
|
query = Identite.query.filter_by(code_nip=nip)
|
||||||
|
elif ine is not None:
|
||||||
|
query = Identite.query.filter_by(code_ine=ine)
|
||||||
|
else:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="parametre manquant",
|
||||||
|
)
|
||||||
|
|
||||||
|
return jsonify([etud.to_dict_bul(include_urls=False) for etud in query])
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
|
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
|
||||||
@bp.route("/etudiant/nip/<int:nip>/formsemestres")
|
@bp.route("/etudiant/nip/<string:nip>/formsemestres")
|
||||||
@bp.route("/etudiant/ine/<int:ine>/formsemestres")
|
@bp.route("/etudiant/ine/<string:ine>/formsemestres")
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
|
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des semestres qu'un étudiant a suivis, triés par ordre chronologique.
|
Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique.
|
||||||
|
Accès par etudid, nip ou ine
|
||||||
etudid : l'etudid d'un étudiant
|
|
||||||
nip : le code nip d'un étudiant
|
|
||||||
ine : le code ine d'un étudiant
|
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"date_fin": "31/08/2022",
|
"date_fin": "31/08/2022",
|
||||||
"resp_can_edit": false,
|
"resp_can_edit": false,
|
||||||
"dept_id": 1,
|
"dept_id": 1,
|
||||||
"etat": true,
|
"etat": true,
|
||||||
"resp_can_change_ens": true,
|
"resp_can_change_ens": true,
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"modalite": "FI",
|
"modalite": "FI",
|
||||||
"ens_can_edit_eval": false,
|
"ens_can_edit_eval": false,
|
||||||
"formation_id": 1,
|
"formation_id": 1,
|
||||||
"gestion_compensation": false,
|
"gestion_compensation": false,
|
||||||
"elt_sem_apo": null,
|
"elt_sem_apo": null,
|
||||||
"semestre_id": 1,
|
"semestre_id": 1,
|
||||||
"bul_hide_xml": false,
|
"bul_hide_xml": false,
|
||||||
"elt_annee_apo": null,
|
"elt_annee_apo": null,
|
||||||
"titre": "Semestre test",
|
"titre": "Semestre test",
|
||||||
"block_moyennes": false,
|
"block_moyennes": false,
|
||||||
"scodoc7_id": null,
|
"scodoc7_id": null,
|
||||||
"date_debut": "01/09/2021",
|
"date_debut": "01/09/2021",
|
||||||
"gestion_semestrielle": false,
|
"gestion_semestrielle": false,
|
||||||
"bul_bgcolor": "white",
|
"bul_bgcolor": "white",
|
||||||
"formsemestre_id": 1,
|
"formsemestre_id": 1,
|
||||||
"titre_num": "Semestre test semestre 1",
|
"titre_num": "Semestre test semestre 1",
|
||||||
"date_debut_iso": "2021-09-01",
|
"date_debut_iso": "2021-09-01",
|
||||||
"date_fin_iso": "2022-08-31",
|
"date_fin_iso": "2022-08-31",
|
||||||
"responsables": []
|
"responsables": []
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
# Récupération de l'étudiant
|
if etudid is not None:
|
||||||
etud = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
query = FormSemestre.query.filter(
|
||||||
|
FormSemestreInscription.etudid == etudid,
|
||||||
|
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
||||||
|
)
|
||||||
|
elif nip is not None:
|
||||||
|
query = FormSemestre.query.filter(
|
||||||
|
Identite.code_nip == nip,
|
||||||
|
FormSemestreInscription.etudid == Identite.id,
|
||||||
|
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
||||||
|
)
|
||||||
|
elif ine is not None:
|
||||||
|
query = FormSemestre.query.filter(
|
||||||
|
Identite.code_ine == ine,
|
||||||
|
FormSemestreInscription.etudid == Identite.id,
|
||||||
|
FormSemestreInscription.formsemestre_id == FormSemestre.id,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="parametre manquant",
|
||||||
|
)
|
||||||
|
|
||||||
formsemestres = models.FormSemestre.query.filter(
|
formsemestres = query.order_by(FormSemestre.date_debut)
|
||||||
models.FormSemestreInscription.etudid == etud.id,
|
|
||||||
models.FormSemestreInscription.formsemestre_id == models.FormSemestre.id,
|
|
||||||
).order_by(models.FormSemestre.date_debut)
|
|
||||||
|
|
||||||
return jsonify([formsemestre.to_dict() for formsemestre in formsemestres])
|
return jsonify([formsemestre.to_dict() for formsemestre in formsemestres])
|
||||||
|
|
||||||
@ -162,18 +236,41 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
|
|||||||
@bp.route(
|
@bp.route(
|
||||||
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin",
|
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin",
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
|
defaults={"version": "long"},
|
||||||
)
|
)
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/etudiant/nip/<int:nip>/formsemestre/<int:formsemestre_id>/bulletin",
|
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin",
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
|
defaults={"version": "long"},
|
||||||
)
|
)
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/etudiant/ine/<int:ine>/formsemestre/<int:formsemestre_id>/bulletin",
|
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin",
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
|
defaults={"version": "long"},
|
||||||
)
|
)
|
||||||
|
@bp.route(
|
||||||
|
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short",
|
||||||
|
methods=["GET"],
|
||||||
|
defaults={"version": "short"},
|
||||||
|
)
|
||||||
|
@bp.route(
|
||||||
|
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short",
|
||||||
|
methods=["GET"],
|
||||||
|
defaults={"version": "short"},
|
||||||
|
)
|
||||||
|
@bp.route(
|
||||||
|
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short",
|
||||||
|
methods=["GET"],
|
||||||
|
defaults={"version": "short"},
|
||||||
|
)
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def etudiant_bulletin_semestre(
|
def etudiant_bulletin_semestre(
|
||||||
formsemestre_id, etudid: int = None, nip: int = None, ine: int = None
|
formsemestre_id,
|
||||||
|
etudid: int = None,
|
||||||
|
nip: str = None,
|
||||||
|
ine: str = None,
|
||||||
|
version="long",
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Retourne le bulletin d'un étudiant en fonction de son id et d'un semestre donné
|
Retourne le bulletin d'un étudiant en fonction de son id et d'un semestre donné
|
||||||
@ -183,12 +280,12 @@ def etudiant_bulletin_semestre(
|
|||||||
nip : le code nip d'un étudiant
|
nip : le code nip d'un étudiant
|
||||||
ine : le code ine d'un étudiant
|
ine : le code ine d'un étudiant
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
{
|
{
|
||||||
"version": "0",
|
"version": "0",
|
||||||
"type": "BUT",
|
"type": "BUT",
|
||||||
"date": "2022-04-27T07:18:16.450634Z",
|
"date": "2022-04-27T07:18:16.450634Z",
|
||||||
"publie": true,
|
"publie": true,
|
||||||
"etudiant": {
|
"etudiant": {
|
||||||
"civilite": "X",
|
"civilite": "X",
|
||||||
"code_ine": "1",
|
"code_ine": "1",
|
||||||
"code_nip": "1",
|
"code_nip": "1",
|
||||||
@ -214,17 +311,17 @@ def etudiant_bulletin_semestre(
|
|||||||
"villedomicile": "",
|
"villedomicile": "",
|
||||||
"telephone": "",
|
"telephone": "",
|
||||||
"fax": "",
|
"fax": "",
|
||||||
"description": ""
|
"description": "",
|
||||||
},
|
},
|
||||||
"formation": {
|
"formation": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"acronyme": "BUT R&T",
|
"acronyme": "BUT R&T",
|
||||||
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
|
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
|
||||||
"titre": "BUT R&T"
|
"titre": "BUT R&T",
|
||||||
},
|
},
|
||||||
"formsemestre_id": 1,
|
"formsemestre_id": 1,
|
||||||
"etat_inscription": "I",
|
"etat_inscription": "I",
|
||||||
"options": {
|
"options": {
|
||||||
"show_abs": true,
|
"show_abs": true,
|
||||||
"show_abs_modules": false,
|
"show_abs_modules": false,
|
||||||
"show_ects": true,
|
"show_ects": true,
|
||||||
@ -243,151 +340,137 @@ def etudiant_bulletin_semestre(
|
|||||||
"show_temporary": true,
|
"show_temporary": true,
|
||||||
"temporary_txt": "Provisoire",
|
"temporary_txt": "Provisoire",
|
||||||
"show_uevalid": true,
|
"show_uevalid": true,
|
||||||
"show_date_inscr": true
|
"show_date_inscr": true,
|
||||||
},
|
},
|
||||||
"ressources": {
|
"ressources": {
|
||||||
"R101": {
|
"R101": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"titre": "Initiation aux r\u00e9seaux informatiques",
|
"titre": "Initiation aux r\u00e9seaux informatiques",
|
||||||
"code_apogee": null,
|
"code_apogee": null,
|
||||||
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=1",
|
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=1",
|
||||||
"moyenne": {},
|
"moyenne": {},
|
||||||
"evaluations": [
|
"evaluations": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"description": "eval1",
|
"description": "eval1",
|
||||||
"date": "2022-04-20",
|
"date": "2022-04-20",
|
||||||
"heure_debut": "08:00",
|
"heure_debut": "08:00",
|
||||||
"heure_fin": "09:00",
|
"heure_fin": "09:00",
|
||||||
"coef": "01.00",
|
"coef": "01.00",
|
||||||
"poids": {
|
"poids": {
|
||||||
"RT1.1": 1.0,
|
"RT1.1": 1.0,
|
||||||
},
|
},
|
||||||
"note": {
|
"note": {
|
||||||
"value": "12.00",
|
"value": "12.00",
|
||||||
"min": "00.00",
|
"min": "00.00",
|
||||||
"max": "18.00",
|
"max": "18.00",
|
||||||
"moy": "10.88"
|
"moy": "10.88",
|
||||||
},
|
},
|
||||||
"url": "/ScoDoc/TAPI/Scolarite/Notes/evaluation_listenotes?evaluation_id=1"
|
"url": "/ScoDoc/TAPI/Scolarite/Notes/evaluation_listenotes?evaluation_id=1",
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"saes": {
|
"saes": {
|
||||||
"SAE11": {
|
"SAE11": {
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"titre": "Se sensibiliser \u00e0 l'hygi\u00e8ne informatique et \u00e0 la cybers\u00e9curit\u00e9",
|
"titre": "Se sensibiliser \u00e0 l'hygi\u00e8ne informatique et \u00e0 la cybers\u00e9curit\u00e9",
|
||||||
"code_apogee": null,
|
"code_apogee": null,
|
||||||
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=2",
|
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=2",
|
||||||
"moyenne": {},
|
"moyenne": {},
|
||||||
"evaluations": []
|
"evaluations": [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ues": {
|
"ues": {
|
||||||
"RT1.1": {
|
"RT1.1": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
|
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
|
||||||
"numero": 1,
|
"numero": 1,
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"color": "#B80004",
|
"color": "#B80004",
|
||||||
"competence": null,
|
"competence": null,
|
||||||
"moyenne": {
|
"moyenne": {
|
||||||
"value": "08.50",
|
"value": "08.50",
|
||||||
"min": "06.00",
|
"min": "06.00",
|
||||||
"max": "16.50",
|
"max": "16.50",
|
||||||
"moy": "11.31",
|
"moy": "11.31",
|
||||||
"rang": "12",
|
"rang": "12",
|
||||||
"total": 16
|
"total": 16,
|
||||||
},
|
|
||||||
"bonus": "00.00",
|
|
||||||
"malus": "00.00",
|
|
||||||
"capitalise": null,
|
|
||||||
"ressources": {
|
|
||||||
"R101": {
|
|
||||||
"id": 1,
|
|
||||||
"coef": 12.0,
|
|
||||||
"moyenne": "12.00"
|
|
||||||
},
|
},
|
||||||
},
|
"bonus": "00.00",
|
||||||
"saes": {
|
"malus": "00.00",
|
||||||
"SAE11": {
|
"capitalise": null,
|
||||||
"id": 2,
|
"ressources": {
|
||||||
"coef": 16.0,
|
"R101": {"id": 1, "coef": 12.0, "moyenne": "12.00"},
|
||||||
"moyenne": "~"
|
|
||||||
},
|
},
|
||||||
},
|
"saes": {
|
||||||
"ECTS": {
|
"SAE11": {"id": 2, "coef": 16.0, "moyenne": "~"},
|
||||||
"acquis": 0.0,
|
},
|
||||||
"total": 12.0
|
"ECTS": {"acquis": 0.0, "total": 12.0},
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"semestre": {
|
"semestre": {
|
||||||
"etapes": [],
|
"etapes": [],
|
||||||
"date_debut": "2021-09-01",
|
"date_debut": "2021-09-01",
|
||||||
"date_fin": "2022-08-31",
|
"date_fin": "2022-08-31",
|
||||||
"annee_universitaire": "2021 - 2022",
|
"annee_universitaire": "2021 - 2022",
|
||||||
"numero": 1,
|
"numero": 1,
|
||||||
"inscription": "",
|
"inscription": "",
|
||||||
"groupes": [],
|
"groupes": [],
|
||||||
"absences": {
|
"absences": {"injustifie": 1, "total": 2},
|
||||||
"injustifie": 1,
|
"ECTS": {"acquis": 0, "total": 30.0},
|
||||||
"total": 2
|
"notes": {"value": "10.60", "min": "02.40", "moy": "11.05", "max": "17.40"},
|
||||||
|
"rang": {"value": "10", "total": 16},
|
||||||
},
|
},
|
||||||
"ECTS": {
|
},
|
||||||
"acquis": 0,
|
}
|
||||||
"total": 30.0
|
|
||||||
},
|
|
||||||
"notes": {
|
|
||||||
"value": "10.60",
|
|
||||||
"min": "02.40",
|
|
||||||
"moy": "11.05",
|
|
||||||
"max": "17.40"
|
|
||||||
},
|
|
||||||
"rang": {
|
|
||||||
"value": "10",
|
|
||||||
"total": 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
formsemestre = models.FormSemestre.query.filter_by(
|
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
|
||||||
id=formsemestre_id
|
dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
||||||
).first_or_404()
|
|
||||||
|
|
||||||
dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
if etudid is not None:
|
||||||
|
query = Identite.query.filter_by(id=etudid)
|
||||||
app.set_sco_dept(dept.acronym)
|
elif nip is not None:
|
||||||
|
query = Identite.query.filter_by(code_nip=nip, dept_id=dept.id)
|
||||||
# Récupération de l'étudiant
|
elif ine is not None:
|
||||||
try:
|
query = Identite.query.filter_by(code_ine=ine, dept_id=dept.id)
|
||||||
etu = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
else:
|
||||||
except AttributeError:
|
|
||||||
return error_response(
|
return error_response(
|
||||||
409,
|
404,
|
||||||
message="La requête ne peut être traitée en l’état actuel.\n "
|
message="parametre manquant",
|
||||||
"Veuillez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return sco_bulletins.get_formsemestre_bulletin_etud_json(formsemestre, etu)
|
etud = query.first()
|
||||||
|
if etud is None:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="id de l'étudiant (etudid, nip, ine) inconnu",
|
||||||
|
)
|
||||||
|
|
||||||
|
app.set_sco_dept(dept.acronym)
|
||||||
|
return sco_bulletins.get_formsemestre_bulletin_etud_json(
|
||||||
|
formsemestre, etud, version
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/etudiant/etudid/<int:etudid>/semestre/<int:formsemestre_id>/groups",
|
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/groups",
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
)
|
)
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/etudiant/nip/<int:nip>/semestre/<int:formsemestre_id>/groups", methods=["GET"]
|
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/groups",
|
||||||
|
methods=["GET"],
|
||||||
)
|
)
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/etudiant/ine/<int:ine>/semestre/<int:formsemestre_id>/groups", methods=["GET"]
|
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/groups",
|
||||||
|
methods=["GET"],
|
||||||
)
|
)
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def etudiant_groups(
|
def etudiant_groups(
|
||||||
formsemestre_id: int, etudid: int = None, nip: int = None, ine: int = None
|
formsemestre_id: int, etudid: int = None, nip: int = None, ine: int = None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des groupes auxquels appartient l'étudiant dans le semestre indiqué
|
Retourne la liste des groupes auxquels appartient l'étudiant dans le formsemestre indiqué
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formsemestre
|
formsemestre_id : l'id d'un formsemestre
|
||||||
etudid : l'etudid d'un étudiant
|
etudid : l'etudid d'un étudiant
|
||||||
@ -395,45 +478,57 @@ def etudiant_groups(
|
|||||||
ine : le code ine d'un étudiant
|
ine : le code ine d'un étudiant
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"partition_id": 1,
|
"partition_id": 1,
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"formsemestre_id": 1,
|
"formsemestre_id": 1,
|
||||||
"partition_name": null,
|
"partition_name": null,
|
||||||
"numero": 0,
|
"numero": 0,
|
||||||
"bul_show_rank": false,
|
"bul_show_rank": false,
|
||||||
"show_in_lists": true,
|
"show_in_lists": true,
|
||||||
"group_id": 1,
|
"group_id": 1,
|
||||||
"group_name": null
|
"group_name": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"partition_id": 2,
|
"partition_id": 2,
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"formsemestre_id": 1,
|
"formsemestre_id": 1,
|
||||||
"partition_name": "TD",
|
"partition_name": "TD",
|
||||||
"numero": 1,
|
"numero": 1,
|
||||||
"bul_show_rank": false,
|
"bul_show_rank": false,
|
||||||
"show_in_lists": true,
|
"show_in_lists": true,
|
||||||
"group_id": 2,
|
"group_id": 2,
|
||||||
"group_name": "A"
|
"group_name": "A"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
if etudid is None:
|
|
||||||
etud = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
|
||||||
if etud is None:
|
|
||||||
return error_response(
|
|
||||||
409,
|
|
||||||
message="La requête ne peut être traitée en l’état actuel.\n "
|
|
||||||
"Veuillez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide",
|
|
||||||
)
|
|
||||||
etudid = etud.etudid
|
|
||||||
|
|
||||||
# Récupération du formsemestre
|
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
|
||||||
sem = models.FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
|
if formsemestre is None:
|
||||||
dept = models.Departement.query.get(sem.dept_id)
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="formsemestre inconnu",
|
||||||
|
)
|
||||||
|
dept = Departement.query.get(formsemestre.dept_id)
|
||||||
|
if etudid is not None:
|
||||||
|
query = Identite.query.filter_by(id=etudid)
|
||||||
|
elif nip is not None:
|
||||||
|
query = Identite.query.filter_by(code_nip=nip, dept_id=dept.id)
|
||||||
|
elif ine is not None:
|
||||||
|
query = Identite.query.filter_by(code_ine=ine, dept_id=dept.id)
|
||||||
|
else:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="parametre manquant",
|
||||||
|
)
|
||||||
|
etud = query.first()
|
||||||
|
if etud is None:
|
||||||
|
return error_response(
|
||||||
|
404,
|
||||||
|
message="etudiant inconnu",
|
||||||
|
)
|
||||||
app.set_sco_dept(dept.acronym)
|
app.set_sco_dept(dept.acronym)
|
||||||
data = sco_groups.get_etud_groups(etudid, sem.id)
|
data = sco_groups.get_etud_groups(etud.id, formsemestre.id)
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
@ -4,14 +4,16 @@ from flask import jsonify
|
|||||||
import app
|
import app
|
||||||
|
|
||||||
from app import models
|
from app import models
|
||||||
|
from app.models import Evaluation
|
||||||
from app.api import bp
|
from app.api import bp
|
||||||
from app.api.auth import token_permission_required
|
from app.api.auth import token_auth, token_permission_required
|
||||||
from app.api.errors import error_response
|
from app.api.errors import error_response
|
||||||
from app.scodoc.sco_evaluation_db import do_evaluation_get_all_notes
|
from app.scodoc.sco_evaluation_db import do_evaluation_get_all_notes
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/evaluations/<int:moduleimpl_id>", methods=["GET"])
|
@bp.route("/evaluations/<int:moduleimpl_id>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def evaluations(moduleimpl_id: int):
|
def evaluations(moduleimpl_id: int):
|
||||||
"""
|
"""
|
||||||
@ -45,7 +47,7 @@ def evaluations(moduleimpl_id: int):
|
|||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
# Récupération de toutes les évaluations
|
# Récupération de toutes les évaluations
|
||||||
evals = models.Evaluation.query.filter_by(id=moduleimpl_id)
|
evals = Evaluation.query.filter_by(id=moduleimpl_id)
|
||||||
|
|
||||||
# Mise en forme des données
|
# Mise en forme des données
|
||||||
data = [d.to_dict() for d in evals]
|
data = [d.to_dict() for d in evals]
|
||||||
@ -53,7 +55,8 @@ def evaluations(moduleimpl_id: int):
|
|||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/evaluations/eval_notes/<int:evaluation_id>", methods=["GET"])
|
@bp.route("/evaluation/eval_notes/<int:evaluation_id>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def evaluation_notes(evaluation_id: int):
|
def evaluation_notes(evaluation_id: int):
|
||||||
"""
|
"""
|
||||||
@ -86,26 +89,19 @@ def evaluation_notes(evaluation_id: int):
|
|||||||
"""
|
"""
|
||||||
# Fonction utilisée : app.scodoc.sco_evaluation_db.do_evaluation_get_all_notes()
|
# Fonction utilisée : app.scodoc.sco_evaluation_db.do_evaluation_get_all_notes()
|
||||||
|
|
||||||
eval = models.Evaluation.query.filter_by(id=evaluation_id).first_or_404()
|
evaluation = models.Evaluation.query.filter_by(id=evaluation_id).first_or_404()
|
||||||
|
dept = models.Departement.query.filter_by(
|
||||||
moduleimpl = models.ModuleImpl.query.filter_by(id=eval.moduleimpl_id).first_or_404()
|
id=evaluation.moduleimpl.formsemestre.dept_id
|
||||||
|
).first()
|
||||||
formsemestre = models.FormSemestre.query.filter_by(
|
|
||||||
id=moduleimpl.formsemestre_id
|
|
||||||
).first_or_404()
|
|
||||||
|
|
||||||
dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
|
||||||
|
|
||||||
app.set_sco_dept(dept.acronym)
|
app.set_sco_dept(dept.acronym)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Utilisation de la fonction do_evaluation_get_all_notes
|
# Utilisation de la fonction do_evaluation_get_all_notes
|
||||||
data = do_evaluation_get_all_notes(evaluation_id)
|
data = do_evaluation_get_all_notes(evaluation_id)
|
||||||
except AttributeError:
|
except AttributeError: # ???
|
||||||
return error_response(
|
return error_response(
|
||||||
409,
|
404,
|
||||||
message="La requête ne peut être traitée en l’état actuel. \n"
|
message="La requête ne peut être traitée en l’état actuel.",
|
||||||
"Veillez vérifier la conformité du 'evaluation_id'",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
@ -1,50 +1,38 @@
|
|||||||
##############################################" Formations ############################################################
|
##############################################" Formations ############################################################
|
||||||
from flask import jsonify
|
from flask import jsonify
|
||||||
|
|
||||||
|
import app
|
||||||
from app import models
|
from app import models
|
||||||
from app.api import bp
|
from app.api import bp
|
||||||
from app.api.errors import error_response
|
from app.api.errors import error_response
|
||||||
from app.api.auth import token_permission_required
|
from app.api.auth import token_auth, token_permission_required
|
||||||
from app.scodoc.sco_formations import formation_export
|
from app.models.formations import Formation
|
||||||
|
from app.scodoc import sco_formations
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formations", methods=["GET"])
|
@bp.route("/formations_ids", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def formations():
|
def formations_ids():
|
||||||
"""
|
"""
|
||||||
Retourne la liste des formations
|
Retourne la liste de toutes les formations (tous départements)
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat : [ 17, 99, 32 ]
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"acronyme": "BUT R&T",
|
|
||||||
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
|
|
||||||
"formation_code": "V1RET",
|
|
||||||
"code_specialite": null,
|
|
||||||
"dept_id": 1,
|
|
||||||
"titre": "BUT R&T",
|
|
||||||
"version": 1,
|
|
||||||
"type_parcours": 700,
|
|
||||||
"referentiel_competence_id": null,
|
|
||||||
"formation_id": 1
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
# Récupération de toutes les formations
|
# Récupération de toutes les formations
|
||||||
list_formations = models.Formation.query.all()
|
list_formations = models.Formation.query.all()
|
||||||
|
|
||||||
# Mise en forme des données
|
# Mise en forme des données
|
||||||
data = [d.to_dict() for d in list_formations]
|
data = [d.id for d in list_formations]
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formations/<int:formation_id>", methods=["GET"])
|
@bp.route("/formation/<int:formation_id>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def formations_by_id(formation_id: int):
|
def formation_by_id(formation_id: int):
|
||||||
"""
|
"""
|
||||||
Retourne une formation en fonction d'un id donné
|
Retourne une formation en fonction d'un id donné
|
||||||
|
|
||||||
@ -66,15 +54,25 @@ def formations_by_id(formation_id: int):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# Récupération de la formation
|
# Récupération de la formation
|
||||||
forma = models.Formation.query.filter_by(id=formation_id).first_or_404()
|
formation = models.Formation.query.filter_by(id=formation_id).first_or_404()
|
||||||
|
|
||||||
# Mise en forme des données
|
# Mise en forme des données
|
||||||
data = forma.to_dict()
|
data = formation.to_dict()
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formations/formation_export/<int:formation_id>", methods=["GET"])
|
@bp.route(
|
||||||
|
"/formation/formation_export/<int:formation_id>",
|
||||||
|
methods=["GET"],
|
||||||
|
defaults={"export_ids": False},
|
||||||
|
)
|
||||||
|
@bp.route(
|
||||||
|
"/formation/formation_export/<int:formation_id>/with_ids",
|
||||||
|
methods=["GET"],
|
||||||
|
defaults={"export_ids": True},
|
||||||
|
)
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def formation_export_by_formation_id(formation_id: int, export_ids=False):
|
def formation_export_by_formation_id(formation_id: int, export_ids=False):
|
||||||
"""
|
"""
|
||||||
@ -171,22 +169,20 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# Fonction utilité : app.scodoc.sco_formations.formation_export()
|
formation = Formation.query.get_or_404(formation_id)
|
||||||
|
dept = models.Departement.query.filter_by(id=formation.dept_id).first()
|
||||||
|
app.set_sco_dept(dept.acronym)
|
||||||
try:
|
try:
|
||||||
# Utilisation de la fonction formation_export
|
# Utilisation de la fonction formation_export
|
||||||
data = formation_export(formation_id, export_ids)
|
data = sco_formations.formation_export(formation_id, export_ids)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return error_response(
|
return error_response(500, message="Erreur inconnue")
|
||||||
409,
|
|
||||||
message="La requête ne peut être traitée en l’état actuel. \n"
|
|
||||||
"Veillez vérifier la conformité du 'formation_id'",
|
|
||||||
)
|
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formations/moduleimpl/<int:moduleimpl_id>", methods=["GET"])
|
@bp.route("/formation/moduleimpl/<int:moduleimpl_id>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def moduleimpl(moduleimpl_id: int):
|
def moduleimpl(moduleimpl_id: int):
|
||||||
"""
|
"""
|
||||||
@ -198,7 +194,6 @@ def moduleimpl(moduleimpl_id: int):
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"formsemestre_id": 1,
|
"formsemestre_id": 1,
|
||||||
"computation_expr": null,
|
|
||||||
"module_id": 1,
|
"module_id": 1,
|
||||||
"responsable_id": 2,
|
"responsable_id": 2,
|
||||||
"moduleimpl_id": 1,
|
"moduleimpl_id": 1,
|
||||||
@ -224,65 +219,26 @@ def moduleimpl(moduleimpl_id: int):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# Récupération des tous les moduleimpl
|
modimpl = models.ModuleImpl.query.filter_by(id=moduleimpl_id).first_or_404()
|
||||||
moduleimpl = models.ModuleImpl.query.filter_by(id=moduleimpl_id).first_or_404()
|
data = modimpl.to_dict()
|
||||||
|
|
||||||
# Mise en forme des données
|
|
||||||
data = moduleimpl.to_dict()
|
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/formations/moduleimpl/formsemestre/<int:formsemestre_id>/liste",
|
"/formation/<int:formation_id>/referentiel_competences",
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
)
|
)
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def moduleimpls_sem(formsemestre_id: int):
|
def referentiel_competences(formation_id: int):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des moduleimpl d'un semestre
|
Retourne le référentiel de compétences
|
||||||
|
formation_id : l'id d'une formation
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formsemestre
|
return json, ou null si pas de référentiel associé.
|
||||||
|
|
||||||
Exemple d'utilisation :
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"formsemestre_id": 1,
|
|
||||||
"computation_expr": null,
|
|
||||||
"module_id": 1,
|
|
||||||
"responsable_id": 2,
|
|
||||||
"module": {
|
|
||||||
"heures_tp": 0.0,
|
|
||||||
"code_apogee": "",
|
|
||||||
"titre": "Initiation aux r\u00e9seaux informatiques",
|
|
||||||
"coefficient": 1.0,
|
|
||||||
"module_type": 2,
|
|
||||||
"id": 1,
|
|
||||||
"ects": null,
|
|
||||||
"abbrev": "Init aux r\u00e9seaux informatiques",
|
|
||||||
"ue_id": 1,
|
|
||||||
"code": "R101",
|
|
||||||
"formation_id": 1,
|
|
||||||
"heures_cours": 0.0,
|
|
||||||
"matiere_id": 1,
|
|
||||||
"heures_td": 0.0,
|
|
||||||
"semestre_id": 1,
|
|
||||||
"numero": 10,
|
|
||||||
"module_id": 1
|
|
||||||
},
|
|
||||||
"moduleimpl_id": 1,
|
|
||||||
"ens": []
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
"""
|
||||||
formsemestre = models.FormSemestre.query.filter_by(
|
formation = models.Formation.query.filter_by(id=formation_id).first_or_404()
|
||||||
id=formsemestre_id
|
|
||||||
).first_or_404()
|
|
||||||
|
|
||||||
moduleimpls = formsemestre.modimpls_sorted
|
if formation.referentiel_competence is None:
|
||||||
|
return jsonify(None)
|
||||||
data = [moduleimpl.to_dict() for moduleimpl in moduleimpls]
|
return jsonify(formation.referentiel_competence.to_dict())
|
||||||
|
|
||||||
return jsonify(data)
|
|
||||||
|
@ -4,99 +4,80 @@ from flask import jsonify
|
|||||||
import app
|
import app
|
||||||
from app import models
|
from app import models
|
||||||
from app.api import bp
|
from app.api import bp
|
||||||
from app.api.errors import error_response
|
from app.api.auth import token_auth, token_permission_required
|
||||||
from app.api.auth import token_permission_required
|
from app.models import Departement, FormSemestre, FormSemestreEtape
|
||||||
from app.api.tools import get_etu_from_etudid_or_nip_or_ine
|
|
||||||
from app.models import FormSemestre, FormSemestreEtape
|
|
||||||
from app.scodoc.sco_bulletins import get_formsemestre_bulletin_etud_json
|
from app.scodoc.sco_bulletins import get_formsemestre_bulletin_etud_json
|
||||||
from app.scodoc.sco_bulletins_json import make_json_formsemestre_bulletinetud
|
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_pvjury import formsemestre_pvjury
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formsemestre/<int:formsemestre_id>", methods=["GET"])
|
@bp.route("/formsemestre/<int:formsemestre_id>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def formsemestre(formsemestre_id: int):
|
def formsemestre(formsemestre_id: int):
|
||||||
"""
|
"""
|
||||||
Retourne l'information sur le formsemestre correspondant au formsemestre_id
|
Information sur le formsemestre indiqué.
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formsemestre
|
formsemestre_id : l'id du formsemestre
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
{
|
{
|
||||||
|
"block_moyennes": false,
|
||||||
|
"bul_bgcolor": "white",
|
||||||
|
"bul_hide_xml": false,
|
||||||
|
"date_debut_iso": "2021-09-01",
|
||||||
|
"date_debut": "01/09/2021",
|
||||||
|
"date_fin_iso": "2022-08-31",
|
||||||
"date_fin": "31/08/2022",
|
"date_fin": "31/08/2022",
|
||||||
"resp_can_edit": false,
|
|
||||||
"dept_id": 1,
|
"dept_id": 1,
|
||||||
|
"elt_annee_apo": null,
|
||||||
|
"elt_sem_apo": null,
|
||||||
|
"ens_can_edit_eval": false,
|
||||||
"etat": true,
|
"etat": true,
|
||||||
"resp_can_change_ens": true,
|
"formation_id": 1,
|
||||||
|
"formsemestre_id": 1,
|
||||||
|
"gestion_compensation": false,
|
||||||
|
"gestion_semestrielle": false,
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"modalite": "FI",
|
"modalite": "FI",
|
||||||
"ens_can_edit_eval": false,
|
"resp_can_change_ens": true,
|
||||||
"formation_id": 1,
|
"resp_can_edit": false,
|
||||||
"gestion_compensation": false,
|
"responsables": [1, 99], // uids
|
||||||
"elt_sem_apo": null,
|
|
||||||
"semestre_id": 1,
|
|
||||||
"bul_hide_xml": false,
|
|
||||||
"elt_annee_apo": null,
|
|
||||||
"titre": "Semestre test",
|
|
||||||
"block_moyennes": false,
|
|
||||||
"scodoc7_id": null,
|
"scodoc7_id": null,
|
||||||
"date_debut": "01/09/2021",
|
"semestre_id": 1,
|
||||||
"gestion_semestrielle": false,
|
"titre_formation" : "BUT GEA",
|
||||||
"bul_bgcolor": "white",
|
"titre_num": "BUT GEA semestre 1",
|
||||||
"formsemestre_id": 1,
|
"titre": "BUT GEA",
|
||||||
"titre_num": "Semestre test semestre 1",
|
|
||||||
"date_debut_iso": "2021-09-01",
|
|
||||||
"date_fin_iso": "2022-08-31",
|
|
||||||
"responsables": []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Récupération de tous les formsemestres
|
formsemestre: FormSemestre = models.FormSemestre.query.filter_by(
|
||||||
formsemetre = models.FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
|
id=formsemestre_id
|
||||||
|
).first_or_404()
|
||||||
# Mise en forme des données
|
data = formsemestre.to_dict()
|
||||||
data = formsemetre.to_dict()
|
# Pour le moment on a besoin de fixer le departement
|
||||||
|
# pour accéder aux préferences
|
||||||
|
dept = Departement.query.get(formsemestre.dept_id)
|
||||||
|
app.set_sco_dept(dept.acronym)
|
||||||
|
data["annee_scolaire"] = formsemestre.annee_scolaire_str()
|
||||||
|
data["session_id"] = formsemestre.session_id()
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formsemestre/apo/<string:etape_apo>", methods=["GET"])
|
@bp.route("/formsemestre/apo/<string:etape_apo>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def formsemestre_apo(etape_apo: str):
|
def formsemestre_apo(etape_apo: str):
|
||||||
"""
|
"""
|
||||||
Retourne les informations sur les formsemestres
|
Retourne les informations sur les formsemestres ayant cette étape Apogée
|
||||||
|
|
||||||
etape_apo : l'id d'une étape apogée
|
etape_apo : un code étape apogée
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
{
|
[
|
||||||
"date_fin": "31/08/2022",
|
{ ...formsemestre...
|
||||||
"resp_can_edit": false,
|
}, ...
|
||||||
"dept_id": 1,
|
]
|
||||||
"etat": true,
|
|
||||||
"resp_can_change_ens": true,
|
|
||||||
"id": 1,
|
|
||||||
"modalite": "FI",
|
|
||||||
"ens_can_edit_eval": false,
|
|
||||||
"formation_id": 1,
|
|
||||||
"gestion_compensation": false,
|
|
||||||
"elt_sem_apo": null,
|
|
||||||
"semestre_id": 1,
|
|
||||||
"bul_hide_xml": false,
|
|
||||||
"elt_annee_apo": null,
|
|
||||||
"titre": "Semestre test",
|
|
||||||
"block_moyennes": false,
|
|
||||||
"scodoc7_id": null,
|
|
||||||
"date_debut": "01/09/2021",
|
|
||||||
"gestion_semestrielle": false,
|
|
||||||
"bul_bgcolor": "white",
|
|
||||||
"formsemestre_id": 1,
|
|
||||||
"titre_num": "Semestre test semestre 1",
|
|
||||||
"date_debut_iso": "2021-09-01",
|
|
||||||
"date_fin_iso": "2022-08-31",
|
|
||||||
"responsables": []
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
formsemestres = FormSemestre.query.filter(
|
formsemestres = FormSemestre.query.filter(
|
||||||
FormSemestreEtape.etape_apo == etape_apo,
|
FormSemestreEtape.etape_apo == etape_apo,
|
||||||
@ -106,173 +87,8 @@ def formsemestre_apo(etape_apo: str):
|
|||||||
return jsonify([formsemestre.to_dict() for formsemestre in formsemestres])
|
return jsonify([formsemestre.to_dict() for formsemestre in formsemestres])
|
||||||
|
|
||||||
|
|
||||||
@bp.route(
|
|
||||||
"/formsemestre/<int:formsemestre_id>/etudiant/etudid/<int:etudid>/bulletin",
|
|
||||||
methods=["GET"],
|
|
||||||
)
|
|
||||||
@bp.route(
|
|
||||||
"/formsemestre/<int:formsemestre_id>/etudiant/nip/<int:nip>/bulletin",
|
|
||||||
methods=["GET"],
|
|
||||||
)
|
|
||||||
@bp.route(
|
|
||||||
"/formsemestre/<int:formsemestre_id>/etudiant/ine/<int:ine>/bulletin",
|
|
||||||
methods=["GET"],
|
|
||||||
)
|
|
||||||
@token_permission_required(Permission.APIView)
|
|
||||||
def etudiant_bulletin(
|
|
||||||
formsemestre_id,
|
|
||||||
etudid: int = None,
|
|
||||||
nip: int = None,
|
|
||||||
ine: int = None,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Retourne le bulletin de note d'un étudiant
|
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formsemestre
|
|
||||||
etudid : l'etudid d'un étudiant
|
|
||||||
nip : le code nip d'un étudiant
|
|
||||||
ine : le code ine d'un étudiant
|
|
||||||
|
|
||||||
Exemple de résultat :
|
|
||||||
{
|
|
||||||
"etudid":1,
|
|
||||||
"formsemestre_id":1,
|
|
||||||
"date":"2022-04-27T10:44:47.448094",
|
|
||||||
"publie":true,
|
|
||||||
"etapes":[
|
|
||||||
|
|
||||||
],
|
|
||||||
"etudiant":{
|
|
||||||
"etudid":1,
|
|
||||||
"code_nip":"1",
|
|
||||||
"code_ine":"1",
|
|
||||||
"nom":"COSTA",
|
|
||||||
"prenom":"Sacha",
|
|
||||||
"civilite":"",
|
|
||||||
"photo_url":"/ScoDoc/TAPI/Scolarite/get_photo_image?etudid=1&size=small",
|
|
||||||
"email":"SACHA.COSTA@example.com",
|
|
||||||
"emailperso":"",
|
|
||||||
"sexe":""
|
|
||||||
},
|
|
||||||
"note":{
|
|
||||||
"value":"10.60",
|
|
||||||
"min":"-",
|
|
||||||
"max":"-",
|
|
||||||
"moy":"-"
|
|
||||||
},
|
|
||||||
"rang":{
|
|
||||||
"value":"10",
|
|
||||||
"ninscrits":16
|
|
||||||
},
|
|
||||||
"rang_group":[
|
|
||||||
{
|
|
||||||
"group_type":"TD",
|
|
||||||
"group_name":"",
|
|
||||||
"value":"",
|
|
||||||
"ninscrits":""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"note_max":{
|
|
||||||
"value":20
|
|
||||||
},
|
|
||||||
"bonus_sport_culture":{
|
|
||||||
"value":0.0
|
|
||||||
},
|
|
||||||
"ue":[
|
|
||||||
{
|
|
||||||
"id":1,
|
|
||||||
"numero":"1",
|
|
||||||
"acronyme":"RT1.1",
|
|
||||||
"titre":"Administrer les r\u00e9seaux et l\u2019Internet",
|
|
||||||
"note":{
|
|
||||||
"value":"08.50",
|
|
||||||
"min":"06.00",
|
|
||||||
"max":"16.50",
|
|
||||||
"moy":"11.31"
|
|
||||||
},
|
|
||||||
"rang":"12",
|
|
||||||
"effectif":16,
|
|
||||||
"ects":"12",
|
|
||||||
"code_apogee":"",
|
|
||||||
"module":[
|
|
||||||
{
|
|
||||||
"id":1,
|
|
||||||
"code":"R101",
|
|
||||||
"coefficient":1.0,
|
|
||||||
"numero":10,
|
|
||||||
"titre":"Initiation aux r\u00e9seaux informatiques",
|
|
||||||
"abbrev":"Init aux r\u00e9seaux informatiques",
|
|
||||||
"note":{
|
|
||||||
"value":"12.00",
|
|
||||||
"moy":"-",
|
|
||||||
"max":"-",
|
|
||||||
"min":"-",
|
|
||||||
"nb_notes":"-",
|
|
||||||
"nb_missing":"-",
|
|
||||||
"nb_valid_evals":"-"
|
|
||||||
},
|
|
||||||
"code_apogee":"",
|
|
||||||
"evaluation":[
|
|
||||||
{
|
|
||||||
"jour":"2022-04-20",
|
|
||||||
"heure_debut":"08:00:00",
|
|
||||||
"heure_fin":"09:00:00",
|
|
||||||
"coefficient":1.0,
|
|
||||||
"evaluation_type":0,
|
|
||||||
"evaluation_id":1,
|
|
||||||
"description":"eval1",
|
|
||||||
"note":"12.00"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ue_capitalisee":[],
|
|
||||||
"absences":{
|
|
||||||
"nbabs":2,
|
|
||||||
"nbabsjust":1
|
|
||||||
},
|
|
||||||
"appreciation":[]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
# Fonction utilisée : app.scodoc.sco_bulletins_json.make_json_formsemestre_bulletinetud()
|
|
||||||
|
|
||||||
try:
|
|
||||||
formsemestre = models.FormSemestre.query.filter_by(
|
|
||||||
id=formsemestre_id
|
|
||||||
).first_or_404()
|
|
||||||
|
|
||||||
dept = models.Departement.query.filter_by(
|
|
||||||
id=formsemestre.dept_id
|
|
||||||
).first_or_404()
|
|
||||||
|
|
||||||
app.set_sco_dept(dept.acronym)
|
|
||||||
except:
|
|
||||||
return error_response(
|
|
||||||
409,
|
|
||||||
message="La requête ne peut être traitée en l’état actuel.\n "
|
|
||||||
"Veilliez vérifier que le nom de département est valide",
|
|
||||||
)
|
|
||||||
if etudid is None:
|
|
||||||
# Récupération de l'étudiant
|
|
||||||
try:
|
|
||||||
etu = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
|
||||||
etudid = etu.etudid
|
|
||||||
except AttributeError:
|
|
||||||
return error_response(
|
|
||||||
409,
|
|
||||||
message="La requête ne peut être traitée en l’état actuel.\n "
|
|
||||||
"Veilliez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide",
|
|
||||||
)
|
|
||||||
|
|
||||||
data = make_json_formsemestre_bulletinetud(formsemestre_id, etudid)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"])
|
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def bulletins(formsemestre_id: int):
|
def bulletins(formsemestre_id: int):
|
||||||
"""
|
"""
|
||||||
@ -452,73 +268,69 @@ def bulletins(formsemestre_id: int):
|
|||||||
...
|
...
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
# Fonction utilisée : app.scodoc.sco_bulletins.get_formsemestre_bulletin_etud_json()
|
|
||||||
|
|
||||||
formsemestre = models.FormSemestre.query.filter_by(
|
formsemestre = models.FormSemestre.query.filter_by(
|
||||||
id=formsemestre_id
|
id=formsemestre_id
|
||||||
).first_or_404()
|
).first_or_404()
|
||||||
|
|
||||||
dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
||||||
|
|
||||||
app.set_sco_dept(dept.acronym)
|
app.set_sco_dept(dept.acronym)
|
||||||
|
|
||||||
etuds = formsemestre.etuds
|
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for etu in etuds:
|
for etu in formsemestre.etuds:
|
||||||
bul_etu = get_formsemestre_bulletin_etud_json(formsemestre, etu)
|
bul_etu = get_formsemestre_bulletin_etud_json(formsemestre, etu)
|
||||||
data.append(bul_etu.json)
|
data.append(bul_etu.json)
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formsemestre/<int:formsemestre_id>/jury", methods=["GET"])
|
# XXX Attendre ScoDoc 9.3
|
||||||
@token_permission_required(Permission.APIView)
|
# @bp.route("/formsemestre/<int:formsemestre_id>/jury", methods=["GET"])
|
||||||
def jury(formsemestre_id: int):
|
# @token_auth.login_required
|
||||||
"""
|
# @token_permission_required(Permission.APIView)
|
||||||
Retourne le récapitulatif des décisions jury
|
# def jury(formsemestre_id: int):
|
||||||
|
# """
|
||||||
|
# Retourne le récapitulatif des décisions jury
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formsemestre
|
# formsemestre_id : l'id d'un formsemestre
|
||||||
|
|
||||||
Exemple de résultat :
|
# Exemple de résultat :
|
||||||
|
|
||||||
"""
|
# """
|
||||||
# Fonction utilisée : app.scodoc.sco_pvjury.formsemestre_pvjury()
|
# # Fonction utilisée : app.scodoc.sco_pvjury.formsemestre_pvjury()
|
||||||
|
|
||||||
formsemestre = models.FormSemestre.query.filter_by(
|
# formsemestre = models.FormSemestre.query.filter_by(
|
||||||
id=formsemestre_id
|
# id=formsemestre_id
|
||||||
).first_or_404()
|
# ).first_or_404()
|
||||||
|
|
||||||
dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
# dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
|
||||||
|
|
||||||
app.set_sco_dept(dept.acronym)
|
# app.set_sco_dept(dept.acronym)
|
||||||
|
|
||||||
data = formsemestre_pvjury(formsemestre_id)
|
# data = formsemestre_pvjury(formsemestre_id)
|
||||||
|
|
||||||
# try:
|
# # try:
|
||||||
# # Utilisation de la fonction formsemestre_pvjury
|
# # # Utilisation de la fonction formsemestre_pvjury
|
||||||
# data = formsemestre_pvjury(formsemestre_id)
|
# # data = formsemestre_pvjury(formsemestre_id)
|
||||||
# except AttributeError:
|
# # except AttributeError:
|
||||||
# return error_response(
|
# # return error_response(
|
||||||
# 409,
|
# # 409,
|
||||||
# message="La requête ne peut être traitée en l’état actuel. \n"
|
# # message="La requête ne peut être traitée en l’état actuel. \n"
|
||||||
# "Veillez vérifier la conformité du 'formation_id'",
|
# # "Veillez vérifier la conformité du 'formation_id'",
|
||||||
# )
|
# # )
|
||||||
|
|
||||||
return jsonify(data)
|
# return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/formsemestre/<int:formsemestre_id>/programme",
|
"/formsemestre/<int:formsemestre_id>/programme",
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
)
|
)
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def semestre_index(formsemestre_id: int):
|
def formsemestre_programme(formsemestre_id: int):
|
||||||
"""
|
"""
|
||||||
Retourne la liste des Ues, ressources et SAE d'un semestre
|
Retourne la liste des Ues, ressources et SAE d'un semestre
|
||||||
|
|
||||||
dept : l'acronym d'un département
|
formsemestre_id : l'id d'un formsemestre
|
||||||
formsemestre_id : l'id d'un formesemestre
|
|
||||||
|
|
||||||
Exemple de résultat :
|
Exemple de résultat :
|
||||||
{
|
{
|
||||||
@ -543,78 +355,61 @@ def semestre_index(formsemestre_id: int):
|
|||||||
],
|
],
|
||||||
"ressources": [
|
"ressources": [
|
||||||
{
|
{
|
||||||
"titre": "Fondamentaux de la programmation",
|
"ens": [ 10, 18 ],
|
||||||
"coefficient": 1.0,
|
"formsemestre_id": 1,
|
||||||
"module_type": 2,
|
"id": 15,
|
||||||
"id": 17,
|
"module": {
|
||||||
"ects": null,
|
"abbrev": "Programmer",
|
||||||
"abbrev": null,
|
"code": "SAE15",
|
||||||
"ue_id": 3,
|
"code_apogee": "V7GOP",
|
||||||
"code": "R107",
|
"coefficient": 1.0,
|
||||||
"formation_id": 1,
|
"formation_id": 1,
|
||||||
"heures_cours": 0.0,
|
"heures_cours": 0.0,
|
||||||
"matiere_id": 3,
|
"heures_td": 0.0,
|
||||||
"heures_td": 0.0,
|
"heures_tp": 0.0,
|
||||||
"semestre_id": 1,
|
"id": 15,
|
||||||
"heures_tp": 0.0,
|
"matiere_id": 3,
|
||||||
"numero": 70,
|
"module_id": 15,
|
||||||
"code_apogee": "",
|
"module_type": 3,
|
||||||
"module_id": 17
|
"numero": 50,
|
||||||
|
"semestre_id": 1,
|
||||||
|
"titre": "Programmer en Python",
|
||||||
|
"ue_id": 3
|
||||||
},
|
},
|
||||||
|
"module_id": 15,
|
||||||
|
"moduleimpl_id": 15,
|
||||||
|
"responsable_id": 2
|
||||||
|
},
|
||||||
...
|
...
|
||||||
],
|
],
|
||||||
"saes": [
|
"saes": [
|
||||||
{
|
{
|
||||||
"titre": "Se pr\u00e9senter sur Internet",
|
...
|
||||||
"coefficient": 1.0,
|
|
||||||
"module_type": 3,
|
|
||||||
"id": 14,
|
|
||||||
"ects": null,
|
|
||||||
"abbrev": null,
|
|
||||||
"ue_id": 3,
|
|
||||||
"code": "SAE14",
|
|
||||||
"formation_id": 1,
|
|
||||||
"heures_cours": 0.0,
|
|
||||||
"matiere_id": 3,
|
|
||||||
"heures_td": 0.0,
|
|
||||||
"semestre_id": 1,
|
|
||||||
"heures_tp": 0.0,
|
|
||||||
"numero": 40,
|
|
||||||
"code_apogee": "",
|
|
||||||
"module_id": 14
|
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
]
|
],
|
||||||
|
"modules" : [ ... les modules qui ne sont ni des SAEs ni des ressources ... ]
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
formsemestre: FormSemestre = models.FormSemestre.query.filter_by(
|
||||||
formsemestre = models.FormSemestre.query.filter_by(
|
|
||||||
id=formsemestre_id
|
id=formsemestre_id
|
||||||
).first_or_404()
|
).first_or_404()
|
||||||
|
|
||||||
ues = formsemestre.query_ues()
|
ues = formsemestre.query_ues()
|
||||||
|
m_list = {
|
||||||
ues_dict = []
|
ModuleType.RESSOURCE: [],
|
||||||
ressources = []
|
ModuleType.SAE: [],
|
||||||
saes = []
|
ModuleType.STANDARD: [],
|
||||||
|
|
||||||
for ue in ues:
|
|
||||||
ues_dict.append(ue.to_dict())
|
|
||||||
ressources = ue.get_ressources()
|
|
||||||
saes = ue.get_saes()
|
|
||||||
|
|
||||||
data_ressources = []
|
|
||||||
for ressource in ressources:
|
|
||||||
data_ressources.append(ressource.to_dict())
|
|
||||||
|
|
||||||
data_saes = []
|
|
||||||
for sae in saes:
|
|
||||||
data_saes.append(sae.to_dict())
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"ues": ues_dict,
|
|
||||||
"ressources": data_ressources,
|
|
||||||
"saes": data_saes,
|
|
||||||
}
|
}
|
||||||
|
for modimpl in formsemestre.modimpls_sorted:
|
||||||
|
d = modimpl.to_dict()
|
||||||
|
m_list[modimpl.module.module_type].append(d)
|
||||||
|
|
||||||
return data
|
return jsonify(
|
||||||
|
{
|
||||||
|
"ues": [ue.to_dict() for ue in ues],
|
||||||
|
"ressources": m_list[ModuleType.RESSOURCE],
|
||||||
|
"saes": m_list[ModuleType.SAE],
|
||||||
|
"modules": m_list[ModuleType.STANDARD],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
#################################################### Jury #############################################################
|
#################################################### Jury #############################################################
|
||||||
from flask import jsonify
|
# from flask import jsonify
|
||||||
|
|
||||||
from app import models
|
# from app import models
|
||||||
from app.api import bp
|
# from app.api import bp
|
||||||
from app.api.errors import error_response
|
# from app.api.errors import error_response
|
||||||
from app.api.auth import token_permission_required
|
# from app.api.auth import token_auth, token_permission_required
|
||||||
from app.scodoc.sco_prepajury import feuille_preparation_jury
|
# from app.scodoc.sco_prepajury import feuille_preparation_jury
|
||||||
from app.scodoc.sco_pvjury import formsemestre_pvjury
|
# from app.scodoc.sco_pvjury import formsemestre_pvjury
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/jury/formsemestre/<int:formsemestre_id>/preparation_jury", methods=["GET"])
|
# # @bp.route("/jury/formsemestre/<int:formsemestre_id>/preparation_jury", methods=["GET"])
|
||||||
# @token_permission_required(Permission.?)
|
# # @token_permission_required(Permission.?)
|
||||||
def jury_preparation(formsemestre_id: int):
|
# def jury_preparation(formsemestre_id: int):
|
||||||
"""
|
# """
|
||||||
Retourne la feuille de préparation du jury
|
# Retourne la feuille de préparation du jury
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formsemestre
|
# formsemestre_id : l'id d'un formsemestre
|
||||||
"""
|
# """
|
||||||
# Fonction utilisée : app.scodoc.sco_prepajury.feuille_preparation_jury()
|
# # Fonction utilisée : app.scodoc.sco_prepajury.feuille_preparation_jury()
|
||||||
|
|
||||||
# Utilisation de la fonction feuille_preparation_jury
|
# # Utilisation de la fonction feuille_preparation_jury
|
||||||
prepa_jury = feuille_preparation_jury(formsemestre_id)
|
# prepa_jury = feuille_preparation_jury(formsemestre_id)
|
||||||
|
|
||||||
return error_response(501, message="Not implemented")
|
# return error_response(501, message="Not implemented")
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/jury/formsemestre/<int:formsemestre_id>/decisions_jury", methods=["GET"])
|
# # @bp.route("/jury/formsemestre/<int:formsemestre_id>/decisions_jury", methods=["GET"])
|
||||||
# @token_permission_required(Permission.?)
|
# # @token_permission_required(Permission.?)
|
||||||
def jury_decisions(formsemestre_id: int):
|
# def jury_decisions(formsemestre_id: int):
|
||||||
"""
|
# """
|
||||||
Retourne les décisions du jury suivant un formsemestre donné
|
# Retourne les décisions du jury suivant un formsemestre donné
|
||||||
|
|
||||||
formsemestre_id : l'id d'un formsemestre
|
# formsemestre_id : l'id d'un formsemestre
|
||||||
"""
|
# """
|
||||||
# Fonction utilisée : app.scodoc.sco_pvjury.formsemestre_pvjury()
|
# # Fonction utilisée : app.scodoc.sco_pvjury.formsemestre_pvjury()
|
||||||
|
|
||||||
# Utilisation de la fonction formsemestre_pvjury
|
# # Utilisation de la fonction formsemestre_pvjury
|
||||||
decision_jury = formsemestre_pvjury(formsemestre_id)
|
# decision_jury = formsemestre_pvjury(formsemestre_id)
|
||||||
|
|
||||||
return error_response(501, message="Not implemented")
|
# return error_response(501, message="Not implemented")
|
||||||
|
@ -36,7 +36,6 @@ from app.api import bp
|
|||||||
from app.api import requested_format
|
from app.api import requested_format
|
||||||
from app.api.auth import token_auth
|
from app.api.auth import token_auth
|
||||||
from app.api.errors import error_response
|
from app.api.errors import error_response
|
||||||
from app.decorators import permission_required
|
|
||||||
from app.models import Departement
|
from app.models import Departement
|
||||||
from app.scodoc.sco_logos import list_logos, find_logo
|
from app.scodoc.sco_logos import list_logos, find_logo
|
||||||
from app.api.auth import token_auth, token_permission_required
|
from app.api.auth import token_auth, token_permission_required
|
||||||
@ -44,6 +43,7 @@ from app.scodoc.sco_permissions import Permission
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/logos", methods=["GET"])
|
@bp.route("/logos", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def api_get_glob_logos():
|
def api_get_glob_logos():
|
||||||
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
|
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
|
||||||
@ -56,6 +56,7 @@ def api_get_glob_logos():
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/logos/<string:logoname>", methods=["GET"])
|
@bp.route("/logos/<string:logoname>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def api_get_glob_logo(logoname):
|
def api_get_glob_logo(logoname):
|
||||||
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
|
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
|
||||||
@ -72,6 +73,7 @@ def api_get_glob_logo(logoname):
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/departements/<string:departement>/logos", methods=["GET"])
|
@bp.route("/departements/<string:departement>/logos", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def api_get_local_logos(departement):
|
def api_get_local_logos(departement):
|
||||||
dept_id = Departement.from_acronym(departement).id
|
dept_id = Departement.from_acronym(departement).id
|
||||||
@ -82,6 +84,7 @@ def api_get_local_logos(departement):
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/departements/<string:departement>/logos/<string:logoname>", methods=["GET"])
|
@bp.route("/departements/<string:departement>/logos/<string:logoname>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def api_get_local_logo(departement, logoname):
|
def api_get_local_logo(departement, logoname):
|
||||||
# format = requested_format("jpg", ['png', 'jpg']) XXX ?
|
# format = requested_format("jpg", ['png', 'jpg']) XXX ?
|
||||||
|
@ -5,12 +5,13 @@ from app import models
|
|||||||
from app.api import bp
|
from app.api import bp
|
||||||
|
|
||||||
from app.api.errors import error_response
|
from app.api.errors import error_response
|
||||||
from app.api.auth import token_permission_required
|
from app.api.auth import token_auth, token_permission_required
|
||||||
from app.scodoc.sco_groups import get_group_members, setGroups, get_partitions_list
|
from app.scodoc.sco_groups import get_group_members, setGroups, get_partitions_list
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/partitions/<int:formsemestre_id>", methods=["GET"])
|
@bp.route("/partitions/<int:formsemestre_id>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def partition(formsemestre_id: int):
|
def partition(formsemestre_id: int):
|
||||||
"""
|
"""
|
||||||
@ -53,6 +54,7 @@ def partition(formsemestre_id: int):
|
|||||||
|
|
||||||
@bp.route("/partitions/groups/<int:group_id>", methods=["GET"])
|
@bp.route("/partitions/groups/<int:group_id>", methods=["GET"])
|
||||||
@bp.route("/partitions/groups/<int:group_id>/etat/<string:etat>", methods=["GET"])
|
@bp.route("/partitions/groups/<int:group_id>/etat/<string:etat>", methods=["GET"])
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIView)
|
@token_permission_required(Permission.APIView)
|
||||||
def etud_in_group(group_id: int, etat=None):
|
def etud_in_group(group_id: int, etat=None):
|
||||||
"""
|
"""
|
||||||
@ -111,11 +113,7 @@ def etud_in_group(group_id: int, etat=None):
|
|||||||
data = get_group_members(group_id, etat)
|
data = get_group_members(group_id, etat)
|
||||||
|
|
||||||
if len(data) == 0:
|
if len(data) == 0:
|
||||||
return error_response(
|
return error_response(404, message="group_id inconnu")
|
||||||
409,
|
|
||||||
message="La requête ne peut être traitée en l’état actuel. \n"
|
|
||||||
"Aucun groupe ne correspond au 'group_id' renseigné",
|
|
||||||
)
|
|
||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
@ -125,6 +123,7 @@ def etud_in_group(group_id: int, etat=None):
|
|||||||
"/create/<string:groups_to_create>",
|
"/create/<string:groups_to_create>",
|
||||||
methods=["POST"],
|
methods=["POST"],
|
||||||
)
|
)
|
||||||
|
@token_auth.login_required
|
||||||
@token_permission_required(Permission.APIEtudChangeGroups)
|
@token_permission_required(Permission.APIEtudChangeGroups)
|
||||||
def set_groups(
|
def set_groups(
|
||||||
partition_id: int, groups_lists: str, groups_to_delete: str, groups_to_create: str
|
partition_id: int, groups_lists: str, groups_to_delete: str, groups_to_create: str
|
||||||
@ -143,8 +142,4 @@ def set_groups(
|
|||||||
setGroups(partition_id, groups_lists, groups_to_create, groups_to_delete)
|
setGroups(partition_id, groups_lists, groups_to_create, groups_to_delete)
|
||||||
return error_response(200, message="Groups set")
|
return error_response(200, message="Groups set")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return error_response(
|
return error_response(404, message="Erreur")
|
||||||
409,
|
|
||||||
message="La requête ne peut être traitée en l’état actuel. \n"
|
|
||||||
"Veillez vérifier la conformité des éléments passé en paramètres",
|
|
||||||
)
|
|
||||||
|
@ -1,253 +0,0 @@
|
|||||||
# @bp.route("/etudiants", methods=["GET"])
|
|
||||||
# @token_permission_required(Permission.APIView)
|
|
||||||
# def etudiants():
|
|
||||||
# """
|
|
||||||
# Retourne la liste de tous les étudiants
|
|
||||||
#
|
|
||||||
# Exemple de résultat :
|
|
||||||
# {
|
|
||||||
# "civilite": "X",
|
|
||||||
# "code_ine": null,
|
|
||||||
# "code_nip": null,
|
|
||||||
# "date_naissance": null,
|
|
||||||
# "email": null,
|
|
||||||
# "emailperso": null,
|
|
||||||
# "etudid": 18,
|
|
||||||
# "nom": "MOREL",
|
|
||||||
# "prenom": "JACQUES"
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "civilite": "X",
|
|
||||||
# "code_ine": null,
|
|
||||||
# "code_nip": null,
|
|
||||||
# "date_naissance": null,
|
|
||||||
# "email": null,
|
|
||||||
# "emailperso": null,
|
|
||||||
# "etudid": 19,
|
|
||||||
# "nom": "FOURNIER",
|
|
||||||
# "prenom": "ANNE"
|
|
||||||
# },
|
|
||||||
# ...
|
|
||||||
# """
|
|
||||||
# # Récupération de tous les étudiants
|
|
||||||
# etu = models.Identite.query.all()
|
|
||||||
#
|
|
||||||
# # Mise en forme des données
|
|
||||||
# data = [d.to_dict_bul(include_urls=False) for d in etu]
|
|
||||||
#
|
|
||||||
# return jsonify(data)
|
|
||||||
|
|
||||||
|
|
||||||
# @bp.route(
|
|
||||||
# "/evaluations/eval_set_notes?eval_id=<int:eval_id>&etudid=<int:etudid>¬e=<float:note>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/evaluations/eval_set_notes?eval_id=<int:eval_id>&nip=<int:nip>¬e=<float:note>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/evaluations/eval_set_notes?eval_id=<int:eval_id>&ine=<int:ine>¬e=<float:note>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @token_permission_required(Permission.APIEditAllNotes)
|
|
||||||
# def evaluation_set_notes(
|
|
||||||
# eval_id: int, note: float, etudid: int = None, nip: int = None, ine: int = None
|
|
||||||
# ):
|
|
||||||
# """
|
|
||||||
# Set les notes d'une évaluation pour un étudiant donnée
|
|
||||||
#
|
|
||||||
# eval_id : l'id d'une évaluation
|
|
||||||
# note : la note à attribuer
|
|
||||||
# etudid : l'etudid d'un étudiant
|
|
||||||
# nip : le code nip d'un étudiant
|
|
||||||
# ine : le code ine d'un étudiant
|
|
||||||
# """
|
|
||||||
# # Fonction utilisée : app.scodoc.sco_saisie_notes.notes_add()
|
|
||||||
#
|
|
||||||
# # Qu'est ce qu'un user ???
|
|
||||||
# # notes_add()
|
|
||||||
# return error_response(501, message="Not implemented")
|
|
||||||
|
|
||||||
|
|
||||||
# ### Inutil en définitif ###
|
|
||||||
# @bp.route(
|
|
||||||
# "/absences/abs_signale?etudid=<int:etudid>&date=<string:date>&matin=<string:matin>&justif=<string:justif>"
|
|
||||||
# "&description=<string:description>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/absences/abs_signale?nip=<int:nip>&date=<string:date>&matin=<string:matin>&justif=<string:justif>"
|
|
||||||
# "&description=<string:description>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/absences/abs_signale?ine=<int:ine>&date=<string:date>&matin=<string:matin>&justif=<string:justif>"
|
|
||||||
# "&description=<string:description>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/absences/abs_signale?ine=<int:ine>&date=<string:date>&matin=<string:matin>&justif=<string:justif>"
|
|
||||||
# "&description=<string:description>&moduleimpl_id=<int:moduleimpl_id>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @token_permission_required(Permission.APIAbsChange)
|
|
||||||
# def abs_signale(
|
|
||||||
# date: datetime,
|
|
||||||
# matin: bool,
|
|
||||||
# justif: bool,
|
|
||||||
# etudid: int = None,
|
|
||||||
# nip: int = None,
|
|
||||||
# ine: int = None, ### Inutil en définitif
|
|
||||||
# description: str = None,
|
|
||||||
# moduleimpl_id: int = None,
|
|
||||||
# ):
|
|
||||||
# """
|
|
||||||
# Permet d'ajouter une absence en base
|
|
||||||
#
|
|
||||||
# date : la date de l'absence
|
|
||||||
# matin : True ou False
|
|
||||||
# justif : True ou False
|
|
||||||
# etudid : l'etudid d'un étudiant
|
|
||||||
# nip: le code nip d'un étudiant
|
|
||||||
# ine : le code ine d'un étudiant
|
|
||||||
# description : description possible à ajouter sur l'absence
|
|
||||||
# moduleimpl_id : l'id d'un moduleimpl
|
|
||||||
# """
|
|
||||||
# # Fonctions utilisées : app.scodoc.sco_abs.add_absence() et app.scodoc.sco_abs.add_justif()
|
|
||||||
#
|
|
||||||
# if etudid is None:
|
|
||||||
# # Récupération de l'étudiant
|
|
||||||
# try:
|
|
||||||
# etu = get_etu_from_request(etudid, nip, ine)
|
|
||||||
# etudid = etu.etudid
|
|
||||||
# except AttributeError:
|
|
||||||
# return error_response(
|
|
||||||
# 409,
|
|
||||||
# message="La requête ne peut être traitée en l’état actuel.\n "
|
|
||||||
# "Veilliez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide",
|
|
||||||
# )
|
|
||||||
# try:
|
|
||||||
# # Utilisation de la fonction add_absence
|
|
||||||
# add_absence(etudid, date, matin, justif, description, moduleimpl_id)
|
|
||||||
# if justif == True:
|
|
||||||
# # Utilisation de la fonction add_justif
|
|
||||||
# add_justif(etudid, date, matin, description)
|
|
||||||
# except ValueError:
|
|
||||||
# return error_response(
|
|
||||||
# 409, message="La requête ne peut être traitée en l’état actuel"
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/absences/abs_annule_justif?etudid=<int:etudid>&jour=<string:jour>&matin=<string:matin>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/absences/abs_annule_justif?nip=<int:nip>&jour=<string:jour>&matin=<string:matin>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/absences/abs_annule_justif?ine=<int:ine>&jour=<string:jour>&matin=<string:matin>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @token_permission_required(Permission.APIAbsChange)
|
|
||||||
# def abs_annule_justif(
|
|
||||||
# jour: datetime, matin: str, etudid: int = None, nip: int = None, ine: int = None
|
|
||||||
# ):
|
|
||||||
# """
|
|
||||||
# Retourne un html
|
|
||||||
|
|
||||||
# jour : la date de l'absence a annulé
|
|
||||||
# matin : True ou False
|
|
||||||
# etudid : l'etudid d'un étudiant
|
|
||||||
# nip: le code nip d'un étudiant
|
|
||||||
# ine : le code ine d'un étudiant
|
|
||||||
# """
|
|
||||||
# # Fonction utilisée : app.scodoc.sco_abs.annule_justif()
|
|
||||||
|
|
||||||
# if etudid is None:
|
|
||||||
# # Récupération de l'étudiant
|
|
||||||
# try:
|
|
||||||
# etu = get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine)
|
|
||||||
# etudid = etu.etudid
|
|
||||||
# except AttributeError:
|
|
||||||
# return error_response(
|
|
||||||
# 409,
|
|
||||||
# message="La requête ne peut être traitée en l’état actuel.\n "
|
|
||||||
# "Veilliez vérifier que l'id de l'étudiant (etudid, nip, ine) est valide",
|
|
||||||
# )
|
|
||||||
# try:
|
|
||||||
# # Utilisation de la fonction annule_justif
|
|
||||||
# annule_justif(etudid, jour, matin)
|
|
||||||
# except ValueError:
|
|
||||||
# return error_response(
|
|
||||||
# 409,
|
|
||||||
# message="La requête ne peut être traitée en l’état actuel.\n "
|
|
||||||
# "Veilliez vérifier que le 'jour' et le 'matin' sont valides",
|
|
||||||
# )
|
|
||||||
|
|
||||||
# return error_response(200, message="OK")
|
|
||||||
|
|
||||||
# @bp.route(
|
|
||||||
# "/jury/set_decision/etudid?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
|
|
||||||
# "&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/jury/set_decision/nip?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
|
|
||||||
# "&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/jury/set_decision/ine?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
|
|
||||||
# "&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
|
|
||||||
# methods=["POST"],
|
|
||||||
# )
|
|
||||||
# # @token_permission_required(Permission.)
|
|
||||||
# def set_decision_jury(
|
|
||||||
# formsemestre_id: int,
|
|
||||||
# decision_jury: str,
|
|
||||||
# devenir_jury: str,
|
|
||||||
# assiduite: bool,
|
|
||||||
# etudid: int = None,
|
|
||||||
# nip: int = None,
|
|
||||||
# ine: int = None,
|
|
||||||
# ):
|
|
||||||
# """
|
|
||||||
# Attribuer la décision du jury et le devenir à un etudiant
|
|
||||||
#
|
|
||||||
# formsemestre_id : l'id d'un formsemestre
|
|
||||||
# decision_jury : la décision du jury
|
|
||||||
# devenir_jury : le devenir du jury
|
|
||||||
# assiduite : True ou False
|
|
||||||
# etudid : l'etudid d'un étudiant
|
|
||||||
# nip: le code nip d'un étudiant
|
|
||||||
# ine : le code ine d'un étudiant
|
|
||||||
# """
|
|
||||||
# return error_response(501, message="Not implemented")
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# @bp.route(
|
|
||||||
# "/jury/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/annule_decision",
|
|
||||||
# methods=["DELETE"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/jury/nip/<int:nip>/formsemestre/<int:formsemestre_id>/annule_decision",
|
|
||||||
# methods=["DELETE"],
|
|
||||||
# )
|
|
||||||
# @bp.route(
|
|
||||||
# "/jury/ine/<int:ine>/formsemestre/<int:formsemestre_id>/annule_decision",
|
|
||||||
# methods=["DELETE"],
|
|
||||||
# )
|
|
||||||
# # @token_permission_required(Permission.)
|
|
||||||
# def annule_decision_jury(
|
|
||||||
# formsemestre_id: int, etudid: int = None, nip: int = None, ine: int = None
|
|
||||||
# ):
|
|
||||||
# """
|
|
||||||
# Supprime la déciosion du jury pour un étudiant donné
|
|
||||||
#
|
|
||||||
# formsemestre_id : l'id d'un formsemestre
|
|
||||||
# etudid : l'etudid d'un étudiant
|
|
||||||
# nip: le code nip d'un étudiant
|
|
||||||
# ine : le code ine d'un étudiant
|
|
||||||
# """
|
|
||||||
# return error_response(501, message="Not implemented")
|
|
@ -1,152 +0,0 @@
|
|||||||
# -*- mode: python -*-
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# Gestion scolarite IUT
|
|
||||||
#
|
|
||||||
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
"""API ScoDoc 9
|
|
||||||
"""
|
|
||||||
# PAS ENCORE IMPLEMENTEE, juste un essai
|
|
||||||
# Pour P. Bouron, il faudrait en priorité l'équivalent de
|
|
||||||
# Scolarite/Notes/moduleimpl_withmodule_list (alias scodoc7 do_moduleimpl_withmodule_list)
|
|
||||||
# Scolarite/Notes/evaluation_create
|
|
||||||
# Scolarite/Notes/evaluation_delete
|
|
||||||
# Scolarite/Notes/formation_list
|
|
||||||
# Scolarite/Notes/formsemestre_list
|
|
||||||
# Scolarite/Notes/formsemestre_partition_list
|
|
||||||
# Scolarite/Notes/groups_view
|
|
||||||
# Scolarite/Notes/moduleimpl_status
|
|
||||||
# Scolarite/setGroups
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from flask import jsonify, request, g, send_file
|
|
||||||
from sqlalchemy.sql import func
|
|
||||||
|
|
||||||
from app import db, log
|
|
||||||
from app.api import bp, requested_format
|
|
||||||
from app.api.auth import token_auth
|
|
||||||
from app.api.errors import error_response
|
|
||||||
from app import models
|
|
||||||
from app.models import FormSemestre, FormSemestreInscription, Identite
|
|
||||||
from app.models import ApcReferentielCompetences
|
|
||||||
from app.scodoc.sco_abs import (
|
|
||||||
annule_absence,
|
|
||||||
annule_justif,
|
|
||||||
add_absence,
|
|
||||||
add_justif,
|
|
||||||
list_abs_date,
|
|
||||||
)
|
|
||||||
from app.scodoc.sco_bulletins import formsemestre_bulletinetud_dict
|
|
||||||
from app.scodoc.sco_bulletins_json import make_json_formsemestre_bulletinetud
|
|
||||||
from app.scodoc.sco_evaluation_db import do_evaluation_get_all_notes
|
|
||||||
from app.scodoc.sco_formations import formation_export
|
|
||||||
from app.scodoc.sco_formsemestre_inscriptions import (
|
|
||||||
do_formsemestre_inscription_listinscrits,
|
|
||||||
)
|
|
||||||
from app.scodoc.sco_groups import setGroups, get_etud_groups, get_group_members
|
|
||||||
from app.scodoc.sco_logos import list_logos, find_logo, _list_dept_logos
|
|
||||||
from app.scodoc.sco_moduleimpl import moduleimpl_list
|
|
||||||
from app.scodoc.sco_permissions import Permission
|
|
||||||
|
|
||||||
|
|
||||||
# ###################################################### Logos ##########################################################
|
|
||||||
#
|
|
||||||
# # XXX TODO voir get_logo déjà existant dans app/views/scodoc.py
|
|
||||||
#
|
|
||||||
# @bp.route("/logos", methods=["GET"])
|
|
||||||
# def liste_logos(format="json"):
|
|
||||||
# """
|
|
||||||
# Liste des logos définis pour le site scodoc.
|
|
||||||
# """
|
|
||||||
# # fonction to use : list_logos()
|
|
||||||
# # try:
|
|
||||||
# # res = list_logos()
|
|
||||||
# # except ValueError:
|
|
||||||
# # return error_response(409, message="La requête ne peut être traitée en l’état actuel")
|
|
||||||
# #
|
|
||||||
# # if res is None:
|
|
||||||
# # return error_response(200, message="Aucun logo trouvé correspondant aux informations renseignés")
|
|
||||||
# #
|
|
||||||
# # return res
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# @bp.route("/logos/<string:logo_name>", methods=["GET"])
|
|
||||||
# def recup_logo_global(logo_name: str):
|
|
||||||
# """
|
|
||||||
# Retourne l'image au format png ou jpg
|
|
||||||
#
|
|
||||||
# logo_name : le nom du logo rechercher
|
|
||||||
# """
|
|
||||||
# # fonction to use find_logo
|
|
||||||
# # try:
|
|
||||||
# # res = find_logo(logo_name)
|
|
||||||
# # except ValueError:
|
|
||||||
# # return error_response(409, message="La requête ne peut être traitée en l’état actuel")
|
|
||||||
# #
|
|
||||||
# # if res is None:
|
|
||||||
# # return error_response(200, message="Aucun logo trouvé correspondant aux informations renseignés")
|
|
||||||
# #
|
|
||||||
# # return res
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# @bp.route("/departements/<string:dept>/logos", methods=["GET"])
|
|
||||||
# def logo_dept(dept: str):
|
|
||||||
# """
|
|
||||||
# Liste des logos définis pour le département visé.
|
|
||||||
#
|
|
||||||
# dept : l'id d'un département
|
|
||||||
# """
|
|
||||||
# # fonction to use: _list_dept_logos
|
|
||||||
# # dept_id = models.Departement.query.filter_by(acronym=dept).first()
|
|
||||||
# # try:
|
|
||||||
# # res = _list_dept_logos(dept_id.id)
|
|
||||||
# # except ValueError:
|
|
||||||
# # return error_response(409, message="La requête ne peut être traitée en l’état actuel")
|
|
||||||
# #
|
|
||||||
# # if res is None:
|
|
||||||
# # return error_response(200, message="Aucun logo trouvé correspondant aux informations renseignés")
|
|
||||||
# #
|
|
||||||
# # return res
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# @bp.route("/departement/<string:dept>/logos/<string:logo_name>", methods=["GET"])
|
|
||||||
# def recup_logo_dept_global(dept: str, logo_name: str):
|
|
||||||
# """
|
|
||||||
# L'image format png ou jpg
|
|
||||||
#
|
|
||||||
# dept : l'id d'un département
|
|
||||||
# logo_name : le nom du logo rechercher
|
|
||||||
# """
|
|
||||||
# # fonction to use find_logo
|
|
||||||
# # dept_id = models.Departement.query.filter_by(acronym=dept).first()
|
|
||||||
# # try:
|
|
||||||
# # res = find_logo(logo_name, dept_id.id)
|
|
||||||
# # except ValueError:
|
|
||||||
# # return error_response(409, message="La requête ne peut être traitée en l’état actuel")
|
|
||||||
# #
|
|
||||||
# # if res is None:
|
|
||||||
# # return error_response(200, message="Aucun logo trouvé correspondant aux informations renseignés")
|
|
||||||
# #
|
|
||||||
# # return res
|
|
@ -1,444 +0,0 @@
|
|||||||
################################################## Tests ##############################################################
|
|
||||||
|
|
||||||
|
|
||||||
# XXX OBSOLETE ??? XXX
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import os
|
|
||||||
|
|
||||||
from app import models
|
|
||||||
from app.api import bp, requested_format
|
|
||||||
from app.api.auth import token_auth
|
|
||||||
from app.api.errors import error_response
|
|
||||||
|
|
||||||
SCODOC_USER = "test"
|
|
||||||
SCODOC_PASSWORD = "test"
|
|
||||||
SCODOC_URL = "http://192.168.1.12:5000"
|
|
||||||
CHECK_CERTIFICATE = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
|
|
||||||
|
|
||||||
HEADERS = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_token():
|
|
||||||
"""
|
|
||||||
Permet de set le token dans le header
|
|
||||||
"""
|
|
||||||
global HEADERS
|
|
||||||
global SCODOC_USER
|
|
||||||
global SCODOC_PASSWORD
|
|
||||||
|
|
||||||
r0 = requests.post(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/tokens", auth=(SCODOC_USER, SCODOC_PASSWORD)
|
|
||||||
)
|
|
||||||
token = r0.json()["token"]
|
|
||||||
HEADERS = {"Authorization": f"Bearer {token}"}
|
|
||||||
|
|
||||||
|
|
||||||
DEPT = None
|
|
||||||
FORMSEMESTRE = None
|
|
||||||
ETU = None
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/test_dept", methods=["GET"])
|
|
||||||
def get_departement():
|
|
||||||
"""
|
|
||||||
Permet de tester departements() mais également de set un département dans DEPT pour la suite des tests
|
|
||||||
"""
|
|
||||||
|
|
||||||
get_token()
|
|
||||||
|
|
||||||
global HEADERS
|
|
||||||
global CHECK_CERTIFICATE
|
|
||||||
global SCODOC_USER
|
|
||||||
global SCODOC_PASSWORD
|
|
||||||
|
|
||||||
# print(HEADERS)
|
|
||||||
# departements
|
|
||||||
r = requests.get(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/departements",
|
|
||||||
headers=HEADERS,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
|
|
||||||
if r.status_code == 200:
|
|
||||||
dept_id = r.json()[0]
|
|
||||||
# print(dept_id)
|
|
||||||
|
|
||||||
dept = models.Departement.query.filter_by(id=dept_id).first()
|
|
||||||
dept = dept.to_dict()
|
|
||||||
|
|
||||||
fields = ["id", "acronym", "description", "visible", "date_creation"]
|
|
||||||
|
|
||||||
for field in dept:
|
|
||||||
if field not in fields:
|
|
||||||
return error_response(501, field + " field missing")
|
|
||||||
|
|
||||||
global DEPT
|
|
||||||
DEPT = dept
|
|
||||||
|
|
||||||
return error_response(200, "OK")
|
|
||||||
|
|
||||||
return error_response(409, "La requête ne peut être traitée en l’état actuel")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/test_formsemestre", methods=["GET"])
|
|
||||||
def get_formsemestre():
|
|
||||||
"""
|
|
||||||
Permet de tester liste_semestres_courant() mais également de set un formsemestre dans FORMSEMESTRE
|
|
||||||
pour la suite des tests
|
|
||||||
"""
|
|
||||||
get_departement()
|
|
||||||
|
|
||||||
global DEPT
|
|
||||||
dept_acronym = DEPT["acronym"]
|
|
||||||
|
|
||||||
# liste_semestres_courant
|
|
||||||
r = requests.get(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/departements/" + dept_acronym + "/semestres_courants",
|
|
||||||
auth=(SCODOC_USER, SCODOC_PASSWORD),
|
|
||||||
)
|
|
||||||
|
|
||||||
if r.status_code == 200:
|
|
||||||
formsemestre = r.json()[0]
|
|
||||||
print(r.json()[0])
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
"gestion_semestrielle",
|
|
||||||
"titre",
|
|
||||||
"scodoc7_id",
|
|
||||||
"date_debut",
|
|
||||||
"bul_bgcolor",
|
|
||||||
"date_fin",
|
|
||||||
"resp_can_edit",
|
|
||||||
"dept_id",
|
|
||||||
"etat",
|
|
||||||
"resp_can_change_ens",
|
|
||||||
"id",
|
|
||||||
"modalite",
|
|
||||||
"ens_can_edit_eval",
|
|
||||||
"formation_id",
|
|
||||||
"gestion_compensation",
|
|
||||||
"elt_sem_apo",
|
|
||||||
"semestre_id",
|
|
||||||
"bul_hide_xml",
|
|
||||||
"elt_annee_apo",
|
|
||||||
"block_moyennes",
|
|
||||||
"formsemestre_id",
|
|
||||||
"titre_num",
|
|
||||||
"date_debut_iso",
|
|
||||||
"date_fin_iso",
|
|
||||||
"responsables",
|
|
||||||
]
|
|
||||||
|
|
||||||
for field in formsemestre:
|
|
||||||
if field not in fields:
|
|
||||||
return error_response(501, field + " field missing")
|
|
||||||
|
|
||||||
global FORMSEMESTRE
|
|
||||||
FORMSEMESTRE = formsemestre
|
|
||||||
|
|
||||||
return error_response(200, "OK")
|
|
||||||
|
|
||||||
return error_response(409, "La requête ne peut être traitée en l’état actuel")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/test_etu", methods=["GET"])
|
|
||||||
def get_etudiant():
|
|
||||||
"""
|
|
||||||
Permet de tester etudiants() mais également de set un etudiant dans ETU pour la suite des tests
|
|
||||||
"""
|
|
||||||
|
|
||||||
# etudiants
|
|
||||||
r = requests.get(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiants/courant",
|
|
||||||
auth=(SCODOC_USER, SCODOC_PASSWORD),
|
|
||||||
)
|
|
||||||
|
|
||||||
if r.status_code == 200:
|
|
||||||
etu = r.json()[0]
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
"civilite",
|
|
||||||
"code_ine",
|
|
||||||
"code_nip",
|
|
||||||
"date_naissance",
|
|
||||||
"email",
|
|
||||||
"emailperso",
|
|
||||||
"etudid",
|
|
||||||
"nom",
|
|
||||||
"prenom",
|
|
||||||
]
|
|
||||||
|
|
||||||
for field in etu:
|
|
||||||
if field not in fields:
|
|
||||||
return error_response(501, field + " field missing")
|
|
||||||
|
|
||||||
global ETU
|
|
||||||
ETU = etu
|
|
||||||
print(etu)
|
|
||||||
|
|
||||||
return error_response(200, "OK")
|
|
||||||
|
|
||||||
return error_response(409, "La requête ne peut être traitée en l’état actuel")
|
|
||||||
|
|
||||||
|
|
||||||
############################################### Departements ##########################################################
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/test_liste_etudiants")
|
|
||||||
def test_departements_liste_etudiants():
|
|
||||||
"""
|
|
||||||
Test la route liste_etudiants
|
|
||||||
"""
|
|
||||||
# Set un département et un formsemestre pour les tests
|
|
||||||
get_departement()
|
|
||||||
get_formsemestre()
|
|
||||||
|
|
||||||
global DEPT
|
|
||||||
global FORMSEMESTRE
|
|
||||||
|
|
||||||
# Set les fields à vérifier
|
|
||||||
fields = [
|
|
||||||
"civilite",
|
|
||||||
"code_ine",
|
|
||||||
"code_nip",
|
|
||||||
"date_naissance",
|
|
||||||
"email",
|
|
||||||
"emailperso",
|
|
||||||
"etudid",
|
|
||||||
"nom",
|
|
||||||
"prenom",
|
|
||||||
]
|
|
||||||
|
|
||||||
# liste_etudiants (sans formsemestre)
|
|
||||||
r1 = requests.get(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/departements/" + DEPT["acronym"] + "/etudiants/liste",
|
|
||||||
auth=(SCODOC_USER, SCODOC_PASSWORD),
|
|
||||||
)
|
|
||||||
|
|
||||||
if r1.status_code == 200: # Si la requête est "OK"
|
|
||||||
# On récupère la liste des étudiants
|
|
||||||
etudiants = r1.json()
|
|
||||||
|
|
||||||
# Vérification que tous les étudiants ont bien tous les bons champs
|
|
||||||
for etu in etudiants:
|
|
||||||
for field in etu:
|
|
||||||
if field not in fields:
|
|
||||||
return error_response(501, field + " field missing")
|
|
||||||
|
|
||||||
# liste_etudiants (avec formsemestre)
|
|
||||||
r2 = requests.get(
|
|
||||||
SCODOC_URL
|
|
||||||
+ "/ScoDoc/api/departements/"
|
|
||||||
+ DEPT["acronym"]
|
|
||||||
+ "/etudiants/liste/"
|
|
||||||
+ str(FORMSEMESTRE["formsemestre_id"]),
|
|
||||||
auth=(SCODOC_USER, SCODOC_PASSWORD),
|
|
||||||
)
|
|
||||||
|
|
||||||
if r2.status_code == 200: # Si la requête est "OK"
|
|
||||||
# On récupère la liste des étudiants
|
|
||||||
etudiants = r2.json()
|
|
||||||
|
|
||||||
# Vérification que tous les étudiants ont bien tous les bons champs
|
|
||||||
for etu in etudiants:
|
|
||||||
for field in etu:
|
|
||||||
if field not in fields:
|
|
||||||
return error_response(501, field + " field missing")
|
|
||||||
|
|
||||||
return error_response(200, "OK")
|
|
||||||
|
|
||||||
return error_response(409, "La requête ne peut être traitée en l’état actuel")
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/test_referenciel_competences")
|
|
||||||
def test_departements_referenciel_competences():
|
|
||||||
"""
|
|
||||||
Test la route referenciel_competences
|
|
||||||
"""
|
|
||||||
get_departement()
|
|
||||||
get_formsemestre()
|
|
||||||
|
|
||||||
global DEPT
|
|
||||||
global FORMSEMESTRE
|
|
||||||
|
|
||||||
# referenciel_competences
|
|
||||||
r = requests.post(
|
|
||||||
SCODOC_URL
|
|
||||||
+ "/ScoDoc/api/departements/"
|
|
||||||
+ DEPT["acronym"]
|
|
||||||
+ "/formations/"
|
|
||||||
+ FORMSEMESTRE["formation_id"]
|
|
||||||
+ "/referentiel_competences",
|
|
||||||
auth=(SCODOC_USER, SCODOC_PASSWORD),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/test_liste_semestre_index")
|
|
||||||
def test_departements_semestre_index():
|
|
||||||
"""
|
|
||||||
Test la route semestre_index
|
|
||||||
"""
|
|
||||||
# semestre_index
|
|
||||||
r5 = requests.post(
|
|
||||||
SCODOC_URL
|
|
||||||
+ "/ScoDoc/api/departements/"
|
|
||||||
+ DEPT["acronym"]
|
|
||||||
+ "/formsemestre/"
|
|
||||||
+ FORMSEMESTRE["formation_id"]
|
|
||||||
+ "/programme",
|
|
||||||
auth=(SCODOC_USER, SCODOC_PASSWORD),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#################################################### Etudiants ########################################################
|
|
||||||
|
|
||||||
|
|
||||||
def test_routes_etudiants():
|
|
||||||
"""
|
|
||||||
Test les routes de la partie Etudiants
|
|
||||||
"""
|
|
||||||
# etudiants
|
|
||||||
r1 = requests.get(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiants", auth=(SCODOC_USER, SCODOC_PASSWORD)
|
|
||||||
)
|
|
||||||
|
|
||||||
# etudiants_courant
|
|
||||||
r2 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# etudiant
|
|
||||||
r3 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# etudiant_formsemestres
|
|
||||||
r4 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# etudiant_bulletin_semestre
|
|
||||||
r5 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# etudiant_groups
|
|
||||||
r6 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
|
|
||||||
def test_routes_formation():
|
|
||||||
"""
|
|
||||||
Test les routes de la partie Formation
|
|
||||||
"""
|
|
||||||
# formations
|
|
||||||
r1 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# formations_by_id
|
|
||||||
r2 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# formation_export_by_formation_id
|
|
||||||
r3 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# formsemestre_apo
|
|
||||||
r4 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# moduleimpls
|
|
||||||
r5 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# moduleimpls_sem
|
|
||||||
r6 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
|
|
||||||
def test_routes_formsemestres():
|
|
||||||
"""
|
|
||||||
Test les routes de la partie Formsemestres
|
|
||||||
"""
|
|
||||||
# formsemestre
|
|
||||||
r1 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# etudiant_bulletin
|
|
||||||
r2 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# bulletins
|
|
||||||
r3 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# jury
|
|
||||||
r4 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
|
|
||||||
def test_routes_partitions():
|
|
||||||
"""
|
|
||||||
Test les routes de la partie Partitions
|
|
||||||
"""
|
|
||||||
# partition
|
|
||||||
r1 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# etud_in_group
|
|
||||||
r2 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# set_groups
|
|
||||||
r3 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
|
|
||||||
def test_routes_evaluations():
|
|
||||||
"""
|
|
||||||
Test les routes de la partie Evaluations
|
|
||||||
"""
|
|
||||||
# evaluations
|
|
||||||
r1 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# evaluation_notes
|
|
||||||
r2 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# evaluation_set_notes
|
|
||||||
r3 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
|
|
||||||
def test_routes_jury():
|
|
||||||
"""
|
|
||||||
Test les routes de la partie Jury
|
|
||||||
"""
|
|
||||||
# jury_preparation
|
|
||||||
r1 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# jury_decisions
|
|
||||||
r2 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# set_decision_jury
|
|
||||||
r3 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# annule_decision_jury
|
|
||||||
r4 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
|
|
||||||
def test_routes_absences():
|
|
||||||
"""
|
|
||||||
Test les routes de la partie Absences
|
|
||||||
"""
|
|
||||||
# absences
|
|
||||||
r1 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# absences_justify
|
|
||||||
r2 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# abs_signale
|
|
||||||
r3 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# abs_annule
|
|
||||||
r4 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# abs_annule_justif
|
|
||||||
r5 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# abs_groupe_etat
|
|
||||||
r6 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
|
|
||||||
def test_routes_logos():
|
|
||||||
"""
|
|
||||||
Test les routes de la partie Logos
|
|
||||||
"""
|
|
||||||
# liste_logos
|
|
||||||
r1 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# recup_logo_global
|
|
||||||
r2 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# logo_dept
|
|
||||||
r3 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
||||||
|
|
||||||
# recup_logo_dept_global
|
|
||||||
r4 = requests.post(SCODOC_URL + "/ScoDoc/api", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
|
@ -1,5 +1,5 @@
|
|||||||
from flask import jsonify
|
from flask import jsonify
|
||||||
from app import db
|
from app import db, log
|
||||||
from app.api import bp
|
from app.api import bp
|
||||||
from app.api.auth import basic_auth, token_auth
|
from app.api.auth import basic_auth, token_auth
|
||||||
|
|
||||||
@ -7,7 +7,9 @@ from app.api.auth import basic_auth, token_auth
|
|||||||
@bp.route("/tokens", methods=["POST"])
|
@bp.route("/tokens", methods=["POST"])
|
||||||
@basic_auth.login_required
|
@basic_auth.login_required
|
||||||
def get_token():
|
def get_token():
|
||||||
|
"renvoie un jeton jwt pour l'utilisateur courant"
|
||||||
token = basic_auth.current_user().get_token()
|
token = basic_auth.current_user().get_token()
|
||||||
|
log(f"API: giving token to {basic_auth.current_user()}")
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return jsonify({"token": token})
|
return jsonify({"token": token})
|
||||||
|
|
||||||
@ -15,6 +17,7 @@ def get_token():
|
|||||||
@bp.route("/tokens", methods=["DELETE"])
|
@bp.route("/tokens", methods=["DELETE"])
|
||||||
@token_auth.login_required
|
@token_auth.login_required
|
||||||
def revoke_token():
|
def revoke_token():
|
||||||
|
"révoque le jeton de l'utilisateur courant"
|
||||||
token_auth.current_user().revoke_token()
|
token_auth.current_user().revoke_token()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return "", 204
|
return "", 204
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
from app import models
|
from app import models
|
||||||
|
|
||||||
|
|
||||||
def get_etu_from_etudid_or_nip_or_ine(etudid, nip, ine):
|
def get_etud_from_etudid_or_nip_or_ine(
|
||||||
|
etudid=None, nip=None, ine=None
|
||||||
|
) -> models.Identite:
|
||||||
"""
|
"""
|
||||||
Fonction qui retourne un etudiant en fonction de l'etudid, code nip et code ine rentré en paramètres
|
etudiant en fonction de l'etudid, code nip et code ine rentré en paramètres
|
||||||
|
|
||||||
etudid : None ou un int etudid
|
etudid : None ou un int etudid
|
||||||
nip : None ou un int code_nip
|
nip : None ou un int code_nip
|
||||||
ine : None ou un int code_ine
|
ine : None ou un int code_ine
|
||||||
|
|
||||||
Exemple de résultat: <Itendite>
|
Return None si étudiant inexistant.
|
||||||
"""
|
"""
|
||||||
if etudid is None:
|
if etudid is None:
|
||||||
if nip is None: # si ine
|
if nip is None: # si ine
|
||||||
|
@ -18,7 +18,7 @@ from werkzeug.security import generate_password_hash, check_password_hash
|
|||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
|
|
||||||
from app import db, login
|
from app import db, log, login
|
||||||
from app.models import Departement
|
from app.models import Departement
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
@ -150,11 +150,22 @@ class User(UserMixin, db.Model):
|
|||||||
def verify_reset_password_token(token):
|
def verify_reset_password_token(token):
|
||||||
"Vérification du token de reéinitialisation du mot de passe"
|
"Vérification du token de reéinitialisation du mot de passe"
|
||||||
try:
|
try:
|
||||||
user_id = jwt.decode(
|
token = jwt.decode(
|
||||||
token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
|
token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
|
||||||
)["reset_password"]
|
)
|
||||||
|
except jwt.exceptions.ExpiredSignatureError:
|
||||||
|
log(f"verify_reset_password_token: token expired")
|
||||||
except:
|
except:
|
||||||
return
|
return None
|
||||||
|
try:
|
||||||
|
user_id = token["reset_password"]
|
||||||
|
# double check en principe inutile car déjà fait dans decode()
|
||||||
|
expire = float(token["exp"])
|
||||||
|
if time() > expire:
|
||||||
|
log(f"verify_reset_password_token: token expired for uid={user_id}")
|
||||||
|
return None
|
||||||
|
except (TypeError, KeyError):
|
||||||
|
return None
|
||||||
return User.query.get(user_id)
|
return User.query.get(user_id)
|
||||||
|
|
||||||
def to_dict(self, include_email=True):
|
def to_dict(self, include_email=True):
|
||||||
@ -214,6 +225,7 @@ class User(UserMixin, db.Model):
|
|||||||
self.add_role(role, dept)
|
self.add_role(role, dept)
|
||||||
|
|
||||||
def get_token(self, expires_in=3600):
|
def get_token(self, expires_in=3600):
|
||||||
|
"Un jeton pour cet user. Stocké en base, non commité."
|
||||||
now = datetime.utcnow()
|
now = datetime.utcnow()
|
||||||
if self.token and self.token_expiration > now + timedelta(seconds=60):
|
if self.token and self.token_expiration > now + timedelta(seconds=60):
|
||||||
return self.token
|
return self.token
|
||||||
@ -223,6 +235,7 @@ class User(UserMixin, db.Model):
|
|||||||
return self.token
|
return self.token
|
||||||
|
|
||||||
def revoke_token(self):
|
def revoke_token(self):
|
||||||
|
"Révoque le jeton de cet utilisateur"
|
||||||
self.token_expiration = datetime.utcnow() - timedelta(seconds=1)
|
self.token_expiration = datetime.utcnow() - timedelta(seconds=1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -335,7 +348,7 @@ class User(UserMixin, db.Model):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_nom_fmt(self):
|
def get_nom_fmt(self):
|
||||||
"""Nom formatté: "Martin" """
|
"""Nom formaté: "Martin" """
|
||||||
if self.nom:
|
if self.nom:
|
||||||
return sco_etud.format_nom(self.nom, uppercase=False)
|
return sco_etud.format_nom(self.nom, uppercase=False)
|
||||||
else:
|
else:
|
||||||
|
@ -71,7 +71,7 @@ def create_user():
|
|||||||
flash("User {} created".format(user.user_name))
|
flash("User {} created".format(user.user_name))
|
||||||
return redirect(url_for("scodoc.index"))
|
return redirect(url_for("scodoc.index"))
|
||||||
return render_template(
|
return render_template(
|
||||||
"auth/register.html", title=u"Création utilisateur", form=form
|
"auth/register.html", title="Création utilisateur", form=form
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ def reset_password(token):
|
|||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
return redirect(url_for("scodoc.index"))
|
return redirect(url_for("scodoc.index"))
|
||||||
user = User.verify_reset_password_token(token)
|
user = User.verify_reset_password_token(token)
|
||||||
if not user:
|
if user is None:
|
||||||
return redirect(url_for("scodoc.index"))
|
return redirect(url_for("scodoc.index"))
|
||||||
form = ResetPasswordForm()
|
form = ResetPasswordForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
|
@ -14,10 +14,12 @@ from flask import url_for, g
|
|||||||
|
|
||||||
from app.comp.res_but import ResultatsSemestreBUT
|
from app.comp.res_but import ResultatsSemestreBUT
|
||||||
from app.models import FormSemestre, Identite
|
from app.models import FormSemestre, Identite
|
||||||
|
from app.models.groups import GroupDescr
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
from app.scodoc import sco_bulletins, sco_utils as scu
|
from app.scodoc import sco_bulletins, sco_utils as scu
|
||||||
from app.scodoc import sco_bulletins_json
|
from app.scodoc import sco_bulletins_json
|
||||||
from app.scodoc import sco_bulletins_pdf
|
from app.scodoc import sco_bulletins_pdf
|
||||||
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc.sco_codes_parcours import UE_SPORT, DEF
|
from app.scodoc.sco_codes_parcours import UE_SPORT, DEF
|
||||||
from app.scodoc.sco_utils import fmt_note
|
from app.scodoc.sco_utils import fmt_note
|
||||||
@ -64,8 +66,16 @@ class BulletinBUT:
|
|||||||
# }
|
# }
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def etud_ue_results(self, etud: Identite, ue: UniteEns, decision_ue: dict) -> dict:
|
def etud_ue_results(
|
||||||
"dict synthèse résultats UE"
|
self,
|
||||||
|
etud: Identite,
|
||||||
|
ue: UniteEns,
|
||||||
|
decision_ue: dict,
|
||||||
|
etud_groups: list[GroupDescr] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""dict synthèse résultats UE
|
||||||
|
etud_groups : liste des groupes, pour affichage du rang.
|
||||||
|
"""
|
||||||
res = self.res
|
res = self.res
|
||||||
|
|
||||||
d = {
|
d = {
|
||||||
@ -81,7 +91,7 @@ class BulletinBUT:
|
|||||||
if res.bonus_ues is not None and ue.id in res.bonus_ues
|
if res.bonus_ues is not None and ue.id in res.bonus_ues
|
||||||
else fmt_note(0.0),
|
else fmt_note(0.0),
|
||||||
"malus": fmt_note(res.malus[ue.id][etud.id]),
|
"malus": fmt_note(res.malus[ue.id][etud.id]),
|
||||||
"capitalise": None, # "AAAA-MM-JJ" TODO #sco92
|
"capitalise": None, # "AAAA-MM-JJ" TODO #sco93
|
||||||
"ressources": self.etud_ue_mod_results(etud, ue, res.ressources),
|
"ressources": self.etud_ue_mod_results(etud, ue, res.ressources),
|
||||||
"saes": self.etud_ue_mod_results(etud, ue, res.saes),
|
"saes": self.etud_ue_mod_results(etud, ue, res.saes),
|
||||||
}
|
}
|
||||||
@ -103,7 +113,18 @@ class BulletinBUT:
|
|||||||
"moy": fmt_note(res.etud_moy_ue[ue.id].mean()),
|
"moy": fmt_note(res.etud_moy_ue[ue.id].mean()),
|
||||||
"rang": rang,
|
"rang": rang,
|
||||||
"total": effectif, # nb etud avec note dans cette UE
|
"total": effectif, # nb etud avec note dans cette UE
|
||||||
|
"groupes": {},
|
||||||
}
|
}
|
||||||
|
if self.prefs["bul_show_ue_rangs"]:
|
||||||
|
for group in etud_groups:
|
||||||
|
if group.partition.bul_show_rank:
|
||||||
|
rang, effectif = self.res.get_etud_ue_rang(
|
||||||
|
ue.id, etud.id, group.id
|
||||||
|
)
|
||||||
|
d["moyenne"]["groupes"][group.id] = {
|
||||||
|
"value": rang,
|
||||||
|
"total": effectif,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
# ceci suppose que l'on a une seule UE bonus,
|
# ceci suppose que l'on a une seule UE bonus,
|
||||||
# en tous cas elles auront la même description
|
# en tous cas elles auront la même description
|
||||||
@ -275,6 +296,9 @@ class BulletinBUT:
|
|||||||
return d
|
return d
|
||||||
|
|
||||||
nbabs, nbabsjust = formsemestre.get_abs_count(etud.id)
|
nbabs, nbabsjust = formsemestre.get_abs_count(etud.id)
|
||||||
|
etud_groups = sco_groups.get_etud_formsemestre_groups(
|
||||||
|
etud, formsemestre, only_to_show=True
|
||||||
|
)
|
||||||
semestre_infos = {
|
semestre_infos = {
|
||||||
"etapes": [str(x.etape_apo) for x in formsemestre.etapes if x.etape_apo],
|
"etapes": [str(x.etape_apo) for x in formsemestre.etapes if x.etape_apo],
|
||||||
"date_debut": formsemestre.date_debut.isoformat(),
|
"date_debut": formsemestre.date_debut.isoformat(),
|
||||||
@ -282,7 +306,7 @@ class BulletinBUT:
|
|||||||
"annee_universitaire": formsemestre.annee_scolaire_str(),
|
"annee_universitaire": formsemestre.annee_scolaire_str(),
|
||||||
"numero": formsemestre.semestre_id,
|
"numero": formsemestre.semestre_id,
|
||||||
"inscription": "", # inutilisé mais nécessaire pour le js de Seb.
|
"inscription": "", # inutilisé mais nécessaire pour le js de Seb.
|
||||||
"groupes": [], # XXX TODO
|
"groupes": [group.to_dict() for group in etud_groups],
|
||||||
}
|
}
|
||||||
if self.prefs["bul_show_abs"]:
|
if self.prefs["bul_show_abs"]:
|
||||||
semestre_infos["absences"] = {
|
semestre_infos["absences"] = {
|
||||||
@ -306,15 +330,25 @@ class BulletinBUT:
|
|||||||
"max": fmt_note(res.etud_moy_gen.max()),
|
"max": fmt_note(res.etud_moy_gen.max()),
|
||||||
}
|
}
|
||||||
if self.prefs["bul_show_rangs"] and not np.isnan(res.etud_moy_gen[etud.id]):
|
if self.prefs["bul_show_rangs"] and not np.isnan(res.etud_moy_gen[etud.id]):
|
||||||
# classement wrt moyenne général, indicatif
|
# classement wrt moyenne générale, indicatif
|
||||||
semestre_infos["rang"] = {
|
semestre_infos["rang"] = {
|
||||||
"value": res.etud_moy_gen_ranks[etud.id],
|
"value": res.etud_moy_gen_ranks[etud.id],
|
||||||
"total": nb_inscrits,
|
"total": nb_inscrits,
|
||||||
|
"groupes": {},
|
||||||
}
|
}
|
||||||
|
# Rangs par groupes
|
||||||
|
for group in etud_groups:
|
||||||
|
if group.partition.bul_show_rank:
|
||||||
|
rang, effectif = self.res.get_etud_rang_group(etud.id, group.id)
|
||||||
|
semestre_infos["rang"]["groupes"][group.id] = {
|
||||||
|
"value": rang,
|
||||||
|
"total": effectif,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
semestre_infos["rang"] = {
|
semestre_infos["rang"] = {
|
||||||
"value": "-",
|
"value": "-",
|
||||||
"total": nb_inscrits,
|
"total": nb_inscrits,
|
||||||
|
"groupes": {},
|
||||||
}
|
}
|
||||||
d.update(
|
d.update(
|
||||||
{
|
{
|
||||||
@ -324,7 +358,10 @@ class BulletinBUT:
|
|||||||
"saes": self.etud_mods_results(etud, res.saes, version=version),
|
"saes": self.etud_mods_results(etud, res.saes, version=version),
|
||||||
"ues": {
|
"ues": {
|
||||||
ue.acronyme: self.etud_ue_results(
|
ue.acronyme: self.etud_ue_results(
|
||||||
etud, ue, decision_ue=decisions_ues.get(ue.id, {})
|
etud,
|
||||||
|
ue,
|
||||||
|
decision_ue=decisions_ues.get(ue.id, {}),
|
||||||
|
etud_groups=etud_groups,
|
||||||
)
|
)
|
||||||
for ue in res.ues
|
for ue in res.ues
|
||||||
# si l'UE comporte des modules auxquels on est inscrit:
|
# si l'UE comporte des modules auxquels on est inscrit:
|
||||||
|
@ -127,6 +127,9 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
|
|||||||
|
|
||||||
def ue_rows(self, rows: list, ue_acronym: str, ue: dict, title_bg: tuple):
|
def ue_rows(self, rows: list, ue_acronym: str, ue: dict, title_bg: tuple):
|
||||||
"Décrit une UE dans la table synthèse: titre, sous-titre et liste modules"
|
"Décrit une UE dans la table synthèse: titre, sous-titre et liste modules"
|
||||||
|
if (ue["type"] == UE_SPORT) and len(ue.get("modules", [])) == 0:
|
||||||
|
# ne mentionne l'UE que s'il y a des modules
|
||||||
|
return
|
||||||
# 1er ligne titre UE
|
# 1er ligne titre UE
|
||||||
moy_ue = ue.get("moyenne")
|
moy_ue = ue.get("moyenne")
|
||||||
t = {
|
t = {
|
||||||
@ -206,7 +209,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
|
|||||||
for mod_code, mod in ue["modules"].items():
|
for mod_code, mod in ue["modules"].items():
|
||||||
rows.append(
|
rows.append(
|
||||||
{
|
{
|
||||||
"titre": f"{mod_code} {mod['titre']}",
|
"titre": f"{mod_code or ''} {mod['titre'] or ''}",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.evaluations_rows(rows, mod["evaluations"])
|
self.evaluations_rows(rows, mod["evaluations"])
|
||||||
@ -313,7 +316,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
|
|||||||
"lignes des évaluations"
|
"lignes des évaluations"
|
||||||
for e in evaluations:
|
for e in evaluations:
|
||||||
t = {
|
t = {
|
||||||
"titre": f"{e['description']}",
|
"titre": f"{e['description'] or ''}",
|
||||||
"moyenne": e["note"]["value"],
|
"moyenne": e["note"]["value"],
|
||||||
"_moyenne_pdf": Paragraph(
|
"_moyenne_pdf": Paragraph(
|
||||||
f"""<para align=right>{e["note"]["value"]}</para>"""
|
f"""<para align=right>{e["note"]["value"]}</para>"""
|
||||||
|
@ -266,6 +266,8 @@ class BonusSportMultiplicatif(BonusSport):
|
|||||||
amplitude = 0.005 # multiplie les points au dessus du seuil
|
amplitude = 0.005 # multiplie les points au dessus du seuil
|
||||||
# En classique, les bonus multiplicatifs agissent par défaut sur les UE:
|
# En classique, les bonus multiplicatifs agissent par défaut sur les UE:
|
||||||
classic_use_bonus_ues = True
|
classic_use_bonus_ues = True
|
||||||
|
# Facteur multiplicatif max: (bonus = moy_ue*factor)
|
||||||
|
factor_max = 1000.0 # infini
|
||||||
|
|
||||||
# C'est un bonus "multiplicatif": on l'exprime en additif,
|
# C'est un bonus "multiplicatif": on l'exprime en additif,
|
||||||
# sur chaque moyenne d'UE m_0
|
# sur chaque moyenne d'UE m_0
|
||||||
@ -285,6 +287,8 @@ class BonusSportMultiplicatif(BonusSport):
|
|||||||
notes = np.nan_to_num(notes, copy=False)
|
notes = np.nan_to_num(notes, copy=False)
|
||||||
factor = (notes - self.seuil_moy_gen) * self.amplitude # 5% si note=20
|
factor = (notes - self.seuil_moy_gen) * self.amplitude # 5% si note=20
|
||||||
factor[factor <= 0] = 0.0 # note < seuil_moy_gen, pas de bonus
|
factor[factor <= 0] = 0.0 # note < seuil_moy_gen, pas de bonus
|
||||||
|
# note < seuil_moy_gen, pas de bonus: pas de facteur négatif, ni
|
||||||
|
factor.clip(0.0, self.factor_max, out=factor)
|
||||||
|
|
||||||
# Ne s'applique qu'aux moyennes d'UE
|
# Ne s'applique qu'aux moyennes d'UE
|
||||||
if len(factor.shape) == 1: # classic
|
if len(factor.shape) == 1: # classic
|
||||||
@ -481,6 +485,19 @@ class BonusBezier(BonusSportAdditif):
|
|||||||
proportion_point = 0.03
|
proportion_point = 0.03
|
||||||
|
|
||||||
|
|
||||||
|
class BonusBlagnac(BonusSportAdditif):
|
||||||
|
"""Calcul bonus modules optionnels (sport, culture), règle IUT de Blagnac.
|
||||||
|
|
||||||
|
Le bonus est égal à 5% des points au dessus de 10 à appliquer sur toutes
|
||||||
|
les UE du semestre, applicable dans toutes les formations (DUT, BUT, ...).
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "bonus_iutblagnac"
|
||||||
|
displayed_name = "IUT de Blagnac"
|
||||||
|
proportion_point = 0.05
|
||||||
|
classic_use_bonus_ues = True # toujours sur les UE
|
||||||
|
|
||||||
|
|
||||||
class BonusBordeaux1(BonusSportMultiplicatif):
|
class BonusBordeaux1(BonusSportMultiplicatif):
|
||||||
"""Calcul bonus modules optionnels (sport, culture), règle IUT Bordeaux 1,
|
"""Calcul bonus modules optionnels (sport, culture), règle IUT Bordeaux 1,
|
||||||
sur moyenne générale et UEs.
|
sur moyenne générale et UEs.
|
||||||
@ -690,22 +707,123 @@ class BonusGrenobleIUT1(BonusSportMultiplicatif):
|
|||||||
super().compute_bonus(sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan)
|
super().compute_bonus(sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan)
|
||||||
|
|
||||||
|
|
||||||
|
class BonusIUTRennes1(BonusSportAdditif):
|
||||||
|
"""Calcul bonus optionnels (sport, langue vivante, engagement étudiant),
|
||||||
|
règle IUT de l'Université de Rennes 1 (Lannion, Rennes, St Brieuc, St Malo).
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Les étudiants peuvent suivre un ou plusieurs activités optionnelles notées
|
||||||
|
dans les semestres pairs.<br>
|
||||||
|
La meilleure des notes obtenue est prise en compte, si elle est supérieure à 10/20.
|
||||||
|
</li>
|
||||||
|
<li>Le vingtième des points au dessus de 10 est ajouté à la moyenne de chaque UE
|
||||||
|
en BUT, ou à la moyenne générale pour les autres formations.
|
||||||
|
</li>
|
||||||
|
<li> Exemple: un étudiant ayant 16/20 bénéficiera d'un bonus de (16-10)/20 = 0,3 points
|
||||||
|
sur chaque UE.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "bonus_iut_rennes1"
|
||||||
|
displayed_name = "IUTs de Rennes 1 (Lannion, Rennes, St Brieuc, St Malo)"
|
||||||
|
seuil_moy_gen = 10.0
|
||||||
|
proportion_point = 1 / 20.0
|
||||||
|
classic_use_bonus_ues = False
|
||||||
|
# S'applique aussi en classic, sur la moy. gen.
|
||||||
|
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||||
|
"""calcul du bonus"""
|
||||||
|
# Prend la note de chaque modimpl, sans considération d'UE
|
||||||
|
if len(sem_modimpl_moys_inscrits.shape) > 2: # apc
|
||||||
|
sem_modimpl_moys_inscrits = sem_modimpl_moys_inscrits[:, :, 0]
|
||||||
|
# ici sem_modimpl_moys_inscrits est nb_etuds x nb_mods_bonus, en APC et en classic
|
||||||
|
note_bonus_max = np.max(sem_modimpl_moys_inscrits, axis=1) # 1d, nb_etuds
|
||||||
|
nb_ues = self.formsemestre.query_ues(with_sport=False).count()
|
||||||
|
|
||||||
|
bonus_moy_arr = np.where(
|
||||||
|
note_bonus_max > self.seuil_moy_gen,
|
||||||
|
(note_bonus_max - self.seuil_moy_gen) * self.proportion_point,
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
# Seuil: bonus dans [min, max] (défaut [0,20])
|
||||||
|
bonus_max = self.bonus_max or 20.0
|
||||||
|
np.clip(bonus_moy_arr, self.bonus_min, bonus_max, out=bonus_moy_arr)
|
||||||
|
if self.formsemestre.formation.is_apc():
|
||||||
|
bonus_moy_arr = np.stack([bonus_moy_arr] * nb_ues).T
|
||||||
|
|
||||||
|
self.bonus_additif(bonus_moy_arr)
|
||||||
|
|
||||||
|
|
||||||
|
# juste pour compatibilité (nom bonus en base):
|
||||||
|
class BonusStBrieuc(BonusIUTRennes1):
|
||||||
|
name = "bonus_iut_stbrieuc"
|
||||||
|
displayed_name = "IUTs de Rennes 1/St-Brieuc"
|
||||||
|
__doc__ = BonusIUTRennes1.__doc__
|
||||||
|
|
||||||
|
|
||||||
|
class BonusStMalo(BonusIUTRennes1):
|
||||||
|
name = "bonus_iut_stmalo"
|
||||||
|
displayed_name = "IUTs de Rennes 1/St-Malo"
|
||||||
|
__doc__ = BonusIUTRennes1.__doc__
|
||||||
|
|
||||||
|
|
||||||
class BonusLaRochelle(BonusSportAdditif):
|
class BonusLaRochelle(BonusSportAdditif):
|
||||||
"""Calcul bonus modules optionnels (sport, culture), règle IUT de La Rochelle.
|
"""Calcul bonus modules optionnels (sport, culture), règle IUT de La Rochelle.
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Si la note de sport est comprise entre 0 et 10 : pas d'ajout de point.</li>
|
<li>Si la note de sport est comprise entre 0 et 10 : pas d’ajout de point.</li>
|
||||||
<li>Si la note de sport est comprise entre 10 et 20 : ajout de 1% de cette
|
<li>Si la note de sport est comprise entre 10 et 20 :
|
||||||
note sur la moyenne générale du semestre (ou sur les UE en BUT).</li>
|
<ul>
|
||||||
|
<li>Pour le BUT, application pour chaque UE du semestre :
|
||||||
|
<ul>
|
||||||
|
<li>pour une note entre 18 et 20 => + 0,10 points</li>
|
||||||
|
<li>pour une note entre 16 et 17,99 => + 0,08 points</li>
|
||||||
|
<li>pour une note entre 14 et 15,99 => + 0,06 points</li>
|
||||||
|
<li>pour une note entre 12 et 13,99 => + 0,04 points</li>
|
||||||
|
<li>pour une note entre 10 et 11,99 => + 0,02 points</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Pour les DUT/LP :
|
||||||
|
ajout de 1% de la note sur la moyenne générale du semestre
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "bonus_iutlr"
|
name = "bonus_iutlr"
|
||||||
displayed_name = "IUT de La Rochelle"
|
displayed_name = "IUT de La Rochelle"
|
||||||
|
|
||||||
seuil_moy_gen = 10.0 # si bonus > 10,
|
seuil_moy_gen = 10.0 # si bonus > 10,
|
||||||
seuil_comptage = 0.0 # tous les points sont comptés
|
seuil_comptage = 0.0 # tous les points sont comptés
|
||||||
proportion_point = 0.01 # 1%
|
proportion_point = 0.01 # 1%
|
||||||
|
|
||||||
|
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||||
|
"""calcul du bonus"""
|
||||||
|
# La date du semestre ?
|
||||||
|
if self.formsemestre.formation.is_apc():
|
||||||
|
if 0 in sem_modimpl_moys_inscrits.shape:
|
||||||
|
# pas d'étudiants ou pas d'UE ou pas de module...
|
||||||
|
return
|
||||||
|
# Calcule moyenne pondérée des notes de sport:
|
||||||
|
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
|
||||||
|
bonus_moy_arr = np.sum(
|
||||||
|
sem_modimpl_moys_inscrits * modimpl_coefs_etuds_no_nan, axis=1
|
||||||
|
) / np.sum(modimpl_coefs_etuds_no_nan, axis=1)
|
||||||
|
np.nan_to_num(bonus_moy_arr, nan=0.0, copy=False)
|
||||||
|
bonus_moy_arr[bonus_moy_arr < 10.0] = 0.0
|
||||||
|
bonus_moy_arr[bonus_moy_arr >= 18.0] = 0.10
|
||||||
|
bonus_moy_arr[bonus_moy_arr >= 16.0] = 0.08
|
||||||
|
bonus_moy_arr[bonus_moy_arr >= 14.0] = 0.06
|
||||||
|
bonus_moy_arr[bonus_moy_arr >= 12.0] = 0.04
|
||||||
|
bonus_moy_arr[bonus_moy_arr >= 10.0] = 0.02
|
||||||
|
self.bonus_additif(bonus_moy_arr)
|
||||||
|
else:
|
||||||
|
# DUT et LP:
|
||||||
|
return super().compute_bonus(
|
||||||
|
sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BonusLeHavre(BonusSportAdditif):
|
class BonusLeHavre(BonusSportAdditif):
|
||||||
"""Bonus sport IUT du Havre sur les moyennes d'UE
|
"""Bonus sport IUT du Havre sur les moyennes d'UE
|
||||||
@ -908,7 +1026,7 @@ class BonusNantes(BonusSportAdditif):
|
|||||||
class BonusPoitiers(BonusSportAdditif):
|
class BonusPoitiers(BonusSportAdditif):
|
||||||
"""Calcul bonus optionnels (sport, culture), règle IUT de Poitiers.
|
"""Calcul bonus optionnels (sport, culture), règle IUT de Poitiers.
|
||||||
|
|
||||||
Les deux notes d'option supérieure à 10, bonifies les moyennes de chaque UE.
|
Les deux notes d'option supérieure à 10, bonifient les moyennes de chaque UE.
|
||||||
|
|
||||||
bonus = (option1 - 10)*5% + (option2 - 10)*5%
|
bonus = (option1 - 10)*5% + (option2 - 10)*5%
|
||||||
"""
|
"""
|
||||||
@ -933,27 +1051,6 @@ class BonusRoanne(BonusSportAdditif):
|
|||||||
proportion_point = 1
|
proportion_point = 1
|
||||||
|
|
||||||
|
|
||||||
class BonusStBrieuc(BonusSportAdditif):
|
|
||||||
"""IUT de Saint Brieuc
|
|
||||||
|
|
||||||
Ne s'applique qu'aux semestres pairs (S2, S4, S6), et bonifie les moyennes d'UE:
|
|
||||||
<ul>
|
|
||||||
<li>Bonus = (S - 10)/20</li>
|
|
||||||
</ul>
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Utilisé aussi par St Malo, voir plus bas
|
|
||||||
name = "bonus_iut_stbrieuc"
|
|
||||||
displayed_name = "IUT de Saint-Brieuc"
|
|
||||||
proportion_point = 1 / 20.0
|
|
||||||
classic_use_bonus_ues = False
|
|
||||||
|
|
||||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
|
||||||
"""calcul du bonus"""
|
|
||||||
if self.formsemestre.semestre_id % 2 == 0:
|
|
||||||
super().compute_bonus(sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan)
|
|
||||||
|
|
||||||
|
|
||||||
class BonusStEtienne(BonusSportAdditif):
|
class BonusStEtienne(BonusSportAdditif):
|
||||||
"""IUT de Saint-Etienne.
|
"""IUT de Saint-Etienne.
|
||||||
|
|
||||||
@ -984,27 +1081,42 @@ class BonusStDenis(BonusSportAdditif):
|
|||||||
bonus_max = 0.5
|
bonus_max = 0.5
|
||||||
|
|
||||||
|
|
||||||
class BonusStMalo(BonusStBrieuc):
|
class BonusStNazaire(BonusSportMultiplicatif):
|
||||||
# identique à St Brieux, sauf la doc
|
"""IUT de Saint-Nazaire
|
||||||
"""IUT de Saint Malo
|
|
||||||
|
|
||||||
Ne s'applique qu'aux semestres pairs (S2, S4, S6), et bonifie les moyennes d'UE:
|
Trois bonifications sont possibles : sport, culture et engagement citoyen
|
||||||
|
(qui seront déclarées comme des modules séparés de l'UE bonus).
|
||||||
<ul>
|
<ul>
|
||||||
<li>Bonus = (S - 10)/20</li>
|
<li>Chaque bonus est compris entre 0 et 20 points -> 4pt = 1%<br>
|
||||||
|
(note 4/20: 1%, 8/20: 2%, 12/20: 3%, 16/20: 4%, 20/20: 5%)
|
||||||
|
</li>
|
||||||
|
<li>Le total des 3 bonus ne peut excéder 10%</li>
|
||||||
|
<li>La somme des bonus s'applique à la moyenne de chaque UE</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<p>Exemple: une moyenne d'UE de 10/20 avec un total des bonus de 6% donne
|
||||||
|
une moyenne de 10,6.</p>
|
||||||
|
<p>Les bonifications s'appliquent aussi au classement général du semestre
|
||||||
|
et de l'année.
|
||||||
|
</p>
|
||||||
"""
|
"""
|
||||||
name = "bonus_iut_stmalo"
|
|
||||||
displayed_name = "IUT de Saint-Malo"
|
name = "bonus_iutSN"
|
||||||
|
displayed_name = "IUT de Saint-Nazaire"
|
||||||
|
classic_use_bonus_ues = True # s'applique aux UEs en DUT et LP
|
||||||
|
seuil_moy_gen = 0.0 # tous les points comptent
|
||||||
|
amplitude = 0.01 / 4 # 4pt => 1%
|
||||||
|
factor_max = 0.1 # 10% max
|
||||||
|
|
||||||
|
|
||||||
class BonusTarbes(BonusSportAdditif):
|
class BonusTarbes(BonusIUTRennes1):
|
||||||
"""Calcul bonus optionnels (sport, culture), règle IUT de Tarbes.
|
"""Calcul bonus optionnels (sport, culture), règle IUT de Tarbes.
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Les étudiants opeuvent suivre un ou plusieurs activités optionnelles notées.
|
<li>Les étudiants opeuvent suivre un ou plusieurs activités optionnelles notées.
|
||||||
La meilleure des notes obtenue est prise en compte, si elle est supérieure à 10/20.
|
La meilleure des notes obtenue est prise en compte, si elle est supérieure à 10/20.
|
||||||
</li>
|
</li>
|
||||||
<li>Le trentième des points au dessus de 10 est ajouté à la moyenne des UE.
|
<li>Le trentième des points au dessus de 10 est ajouté à la moyenne des UE en BUT,
|
||||||
|
ou à la moyenne générale en DUT et LP.
|
||||||
</li>
|
</li>
|
||||||
<li> Exemple: un étudiant ayant 16/20 bénéficiera d'un bonus de (16-10)/30 = 0,2 points
|
<li> Exemple: un étudiant ayant 16/20 bénéficiera d'un bonus de (16-10)/30 = 0,2 points
|
||||||
sur chaque UE.
|
sur chaque UE.
|
||||||
@ -1018,29 +1130,6 @@ class BonusTarbes(BonusSportAdditif):
|
|||||||
proportion_point = 1 / 30.0
|
proportion_point = 1 / 30.0
|
||||||
classic_use_bonus_ues = True
|
classic_use_bonus_ues = True
|
||||||
|
|
||||||
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
|
||||||
"""calcul du bonus"""
|
|
||||||
# Prend la note de chaque modimpl, sans considération d'UE
|
|
||||||
if len(sem_modimpl_moys_inscrits.shape) > 2: # apc
|
|
||||||
sem_modimpl_moys_inscrits = sem_modimpl_moys_inscrits[:, :, 0]
|
|
||||||
# ici sem_modimpl_moys_inscrits est nb_etuds x nb_mods_bonus, en APC et en classic
|
|
||||||
note_bonus_max = np.max(sem_modimpl_moys_inscrits, axis=1) # 1d, nb_etuds
|
|
||||||
ues = self.formsemestre.query_ues(with_sport=False).all()
|
|
||||||
ues_idx = [ue.id for ue in ues]
|
|
||||||
|
|
||||||
if self.formsemestre.formation.is_apc(): # --- BUT
|
|
||||||
bonus_moy_arr = np.where(
|
|
||||||
note_bonus_max > self.seuil_moy_gen,
|
|
||||||
(note_bonus_max - self.seuil_moy_gen) * self.proportion_point,
|
|
||||||
0.0,
|
|
||||||
)
|
|
||||||
self.bonus_ues = pd.DataFrame(
|
|
||||||
np.stack([bonus_moy_arr] * len(ues)).T,
|
|
||||||
index=self.etuds_idx,
|
|
||||||
columns=ues_idx,
|
|
||||||
dtype=float,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class BonusTours(BonusDirect):
|
class BonusTours(BonusDirect):
|
||||||
"""Calcul bonus sport & culture IUT Tours.
|
"""Calcul bonus sport & culture IUT Tours.
|
||||||
|
@ -41,7 +41,8 @@ from app import db
|
|||||||
from app.models import ModuleImpl, Evaluation, EvaluationUEPoids
|
from app.models import ModuleImpl, Evaluation, EvaluationUEPoids
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc.sco_codes_parcours import UE_SPORT
|
from app.scodoc.sco_codes_parcours import UE_SPORT
|
||||||
|
from app.scodoc import sco_cache
|
||||||
|
from app.scodoc.sco_exceptions import ScoBugCatcher
|
||||||
from app.scodoc.sco_utils import ModuleType
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
|
||||||
@ -423,7 +424,9 @@ def moduleimpl_is_conforme(
|
|||||||
if nb_ues == 0:
|
if nb_ues == 0:
|
||||||
return False # situation absurde (pas d'UE)
|
return False # situation absurde (pas d'UE)
|
||||||
if len(modules_coefficients) != nb_ues:
|
if len(modules_coefficients) != nb_ues:
|
||||||
raise ValueError("moduleimpl_is_conforme: nb ue incoherent")
|
# il arrive (#bug) que le cache ne soit pas à jour...
|
||||||
|
sco_cache.invalidate_formsemestre()
|
||||||
|
raise ScoBugCatcher("moduleimpl_is_conforme: nb ue incoherent")
|
||||||
module_evals_poids = evals_poids.transpose().sum(axis=1).to_numpy() != 0
|
module_evals_poids = evals_poids.transpose().sum(axis=1).to_numpy() != 0
|
||||||
check = all(
|
check = all(
|
||||||
(modules_coefficients[moduleimpl.module_id].to_numpy() != 0)
|
(modules_coefficients[moduleimpl.module_id].to_numpy() != 0)
|
||||||
|
@ -18,7 +18,7 @@ from app.auth.models import User
|
|||||||
from app.comp.res_cache import ResultatsCache
|
from app.comp.res_cache import ResultatsCache
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.moy_mod import ModuleImplResults
|
from app.comp.moy_mod import ModuleImplResults
|
||||||
from app.models import FormSemestre, FormSemestreUECoef, formsemestre
|
from app.models import FormSemestre, FormSemestreUECoef
|
||||||
from app.models import Identite
|
from app.models import Identite
|
||||||
from app.models import ModuleImpl, ModuleImplInscription
|
from app.models import ModuleImpl, ModuleImplInscription
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
@ -70,6 +70,7 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
self.etud_moy_gen: pd.Series = None
|
self.etud_moy_gen: pd.Series = None
|
||||||
self.etud_moy_gen_ranks = {}
|
self.etud_moy_gen_ranks = {}
|
||||||
self.etud_moy_gen_ranks_int = {}
|
self.etud_moy_gen_ranks_int = {}
|
||||||
|
self.moy_gen_rangs_by_group = None # virtual
|
||||||
self.modimpl_inscr_df: pd.DataFrame = None
|
self.modimpl_inscr_df: pd.DataFrame = None
|
||||||
"Inscriptions: row etudid, col modimlpl_id"
|
"Inscriptions: row etudid, col modimlpl_id"
|
||||||
self.modimpls_results: ModuleImplResults = None
|
self.modimpls_results: ModuleImplResults = None
|
||||||
@ -151,6 +152,7 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
if m.module.module_type == scu.ModuleType.SAE
|
if m.module.module_type == scu.ModuleType.SAE
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# --- JURY...
|
||||||
def get_etud_ue_validables(self, etudid: int) -> list[UniteEns]:
|
def get_etud_ue_validables(self, etudid: int) -> list[UniteEns]:
|
||||||
"""Liste des UEs du semestre qui doivent être validées
|
"""Liste des UEs du semestre qui doivent être validées
|
||||||
|
|
||||||
@ -396,7 +398,7 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
- titles: { column_id : title }
|
- titles: { column_id : title }
|
||||||
- columns_ids: (liste des id de colonnes)
|
- columns_ids: (liste des id de colonnes)
|
||||||
|
|
||||||
. Si convert_values, transforme les notes en chaines ("12.34").
|
Si convert_values, transforme les notes en chaines ("12.34").
|
||||||
Les colonnes générées sont:
|
Les colonnes générées sont:
|
||||||
etudid
|
etudid
|
||||||
rang : rang indicatif (basé sur moy gen)
|
rang : rang indicatif (basé sur moy gen)
|
||||||
@ -588,7 +590,9 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
f"moy_{modimpl.module.type_abbrv()}_{modimpl.id}_{ue.id}"
|
f"moy_{modimpl.module.type_abbrv()}_{modimpl.id}_{ue.id}"
|
||||||
)
|
)
|
||||||
val_fmt = val_fmt_html = fmt_note(val)
|
val_fmt = val_fmt_html = fmt_note(val)
|
||||||
if modimpl.module.module_type == scu.ModuleType.MALUS:
|
if convert_values and (
|
||||||
|
modimpl.module.module_type == scu.ModuleType.MALUS
|
||||||
|
):
|
||||||
val_fmt_html = (
|
val_fmt_html = (
|
||||||
(scu.EMO_RED_TRIANGLE_DOWN + val_fmt) if val else ""
|
(scu.EMO_RED_TRIANGLE_DOWN + val_fmt) if val else ""
|
||||||
)
|
)
|
||||||
@ -823,17 +827,25 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
self.formsemestre.id
|
self.formsemestre.id
|
||||||
)
|
)
|
||||||
first_partition = True
|
first_partition = True
|
||||||
|
col_order = 10
|
||||||
for partition in partitions:
|
for partition in partitions:
|
||||||
cid = f"part_{partition['partition_id']}"
|
cid = f"part_{partition['partition_id']}"
|
||||||
|
rg_cid = cid + "_rg" # rang dans la partition
|
||||||
titles[cid] = partition["partition_name"]
|
titles[cid] = partition["partition_name"]
|
||||||
if first_partition:
|
if first_partition:
|
||||||
klass = "partition"
|
klass = "partition"
|
||||||
else:
|
else:
|
||||||
klass = "partition partition_aux"
|
klass = "partition partition_aux"
|
||||||
titles[f"_{cid}_class"] = klass
|
titles[f"_{cid}_class"] = klass
|
||||||
titles[f"_{cid}_col_order"] = 10
|
titles[f"_{cid}_col_order"] = col_order
|
||||||
|
titles[f"_{rg_cid}_col_order"] = col_order + 1
|
||||||
|
col_order += 2
|
||||||
|
if partition["bul_show_rank"]:
|
||||||
|
titles[rg_cid] = f"Rg {partition['partition_name']}"
|
||||||
|
titles[f"_{rg_cid}_class"] = "partition_rangs"
|
||||||
partition_etud_groups = partitions_etud_groups[partition["partition_id"]]
|
partition_etud_groups = partitions_etud_groups[partition["partition_id"]]
|
||||||
for row in rows:
|
for row in rows:
|
||||||
|
group = None # group (dict) de l'étudiant dans cette partition
|
||||||
# dans NotesTableCompat, à revoir
|
# dans NotesTableCompat, à revoir
|
||||||
etud_etat = self.get_etud_etat(row["etudid"])
|
etud_etat = self.get_etud_etat(row["etudid"])
|
||||||
if etud_etat == "D":
|
if etud_etat == "D":
|
||||||
@ -846,8 +858,17 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
group = partition_etud_groups.get(row["etudid"])
|
group = partition_etud_groups.get(row["etudid"])
|
||||||
gr_name = group["group_name"] if group else ""
|
gr_name = group["group_name"] if group else ""
|
||||||
if gr_name:
|
if gr_name:
|
||||||
row[f"{cid}"] = gr_name
|
row[cid] = gr_name
|
||||||
row[f"_{cid}_class"] = klass
|
row[f"_{cid}_class"] = klass
|
||||||
|
# Rangs dans groupe
|
||||||
|
if (
|
||||||
|
partition["bul_show_rank"]
|
||||||
|
and (group is not None)
|
||||||
|
and (group["id"] in self.moy_gen_rangs_by_group)
|
||||||
|
):
|
||||||
|
rang = self.moy_gen_rangs_by_group[group["id"]][0]
|
||||||
|
row[rg_cid] = rang.get(row["etudid"], "")
|
||||||
|
|
||||||
first_partition = False
|
first_partition = False
|
||||||
|
|
||||||
def _recap_add_evaluations(
|
def _recap_add_evaluations(
|
||||||
|
@ -35,7 +35,9 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
"malus",
|
"malus",
|
||||||
"etud_moy_gen_ranks",
|
"etud_moy_gen_ranks",
|
||||||
"etud_moy_gen_ranks_int",
|
"etud_moy_gen_ranks_int",
|
||||||
|
"moy_gen_rangs_by_group",
|
||||||
"ue_rangs",
|
"ue_rangs",
|
||||||
|
"ue_rangs_by_group",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, formsemestre: FormSemestre):
|
def __init__(self, formsemestre: FormSemestre):
|
||||||
@ -48,6 +50,8 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
self.moy_min = "NA"
|
self.moy_min = "NA"
|
||||||
self.moy_max = "NA"
|
self.moy_max = "NA"
|
||||||
self.moy_moy = "NA"
|
self.moy_moy = "NA"
|
||||||
|
self.moy_gen_rangs_by_group = {} # { group_id : (Series, Series) }
|
||||||
|
self.ue_rangs_by_group = {} # { ue_id : {group_id : (Series, Series)}}
|
||||||
self.expr_diagnostics = ""
|
self.expr_diagnostics = ""
|
||||||
self.parcours = self.formsemestre.formation.get_parcours()
|
self.parcours = self.formsemestre.formation.get_parcours()
|
||||||
|
|
||||||
@ -153,31 +157,83 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
def compute_rangs(self):
|
def compute_rangs(self):
|
||||||
"""Calcule les classements
|
"""Calcule les classements
|
||||||
Moyenne générale: etud_moy_gen_ranks
|
Moyenne générale: etud_moy_gen_ranks
|
||||||
Par UE (sauf ue bonus)
|
Par UE (sauf ue bonus): ue_rangs[ue.id]
|
||||||
|
Par groupe: classements selon moy_gen et UE:
|
||||||
|
moy_gen_rangs_by_group[group_id]
|
||||||
|
ue_rangs_by_group[group_id]
|
||||||
"""
|
"""
|
||||||
(
|
(
|
||||||
self.etud_moy_gen_ranks,
|
self.etud_moy_gen_ranks,
|
||||||
self.etud_moy_gen_ranks_int,
|
self.etud_moy_gen_ranks_int,
|
||||||
) = moy_sem.comp_ranks_series(self.etud_moy_gen)
|
) = moy_sem.comp_ranks_series(self.etud_moy_gen)
|
||||||
for ue in self.formsemestre.query_ues():
|
ues = self.formsemestre.query_ues()
|
||||||
|
for ue in ues:
|
||||||
moy_ue = self.etud_moy_ue[ue.id]
|
moy_ue = self.etud_moy_ue[ue.id]
|
||||||
self.ue_rangs[ue.id] = (
|
self.ue_rangs[ue.id] = (
|
||||||
moy_sem.comp_ranks_series(moy_ue)[0], # juste en chaine
|
moy_sem.comp_ranks_series(moy_ue)[0], # juste en chaine
|
||||||
int(moy_ue.count()),
|
int(moy_ue.count()),
|
||||||
)
|
)
|
||||||
# .count() -> nb of non NaN values
|
# .count() -> nb of non NaN values
|
||||||
|
# Rangs dans les groupes (moy. gen et par UE)
|
||||||
|
self.moy_gen_rangs_by_group = {} # { group_id : (Series, Series) }
|
||||||
|
self.ue_rangs_by_group = {}
|
||||||
|
partitions_avec_rang = self.formsemestre.partitions.filter_by(
|
||||||
|
bul_show_rank=True
|
||||||
|
)
|
||||||
|
for partition in partitions_avec_rang:
|
||||||
|
for group in partition.groups:
|
||||||
|
# on prend l'intersection car les groupes peuvent inclure des étudiants désinscrits
|
||||||
|
group_members = list(
|
||||||
|
{etud.id for etud in group.etuds}.intersection(
|
||||||
|
self.etud_moy_gen.index
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# list() car pandas veut une sequence pour take()
|
||||||
|
# Rangs / moyenne générale:
|
||||||
|
group_moys_gen = self.etud_moy_gen[group_members]
|
||||||
|
self.moy_gen_rangs_by_group[group.id] = moy_sem.comp_ranks_series(
|
||||||
|
group_moys_gen
|
||||||
|
)
|
||||||
|
# Rangs / UEs:
|
||||||
|
for ue in ues:
|
||||||
|
group_moys_ue = self.etud_moy_ue[ue.id][group_members]
|
||||||
|
self.ue_rangs_by_group.setdefault(ue.id, {})[
|
||||||
|
group.id
|
||||||
|
] = moy_sem.comp_ranks_series(group_moys_ue)
|
||||||
|
|
||||||
def get_etud_ue_rang(self, ue_id, etudid) -> tuple[str, int]:
|
def get_etud_rang(self, etudid: int) -> str:
|
||||||
|
"""Le rang (classement) de l'étudiant dans le semestre.
|
||||||
|
Result: "13" ou "12 ex"
|
||||||
|
"""
|
||||||
|
return self.etud_moy_gen_ranks.get(etudid, 99999)
|
||||||
|
|
||||||
|
def get_etud_ue_rang(self, ue_id, etudid, group_id=None) -> tuple[str, int]:
|
||||||
"""Le rang de l'étudiant dans cette ue
|
"""Le rang de l'étudiant dans cette ue
|
||||||
|
Si le group_id est spécifié, rang au sein de ce groupe, sinon global.
|
||||||
Result: rang:str, effectif:str
|
Result: rang:str, effectif:str
|
||||||
"""
|
"""
|
||||||
rangs, effectif = self.ue_rangs[ue_id]
|
if group_id is None:
|
||||||
if rangs is not None:
|
rangs, effectif = self.ue_rangs[ue_id]
|
||||||
rang = rangs[etudid]
|
if rangs is not None:
|
||||||
|
rang = rangs[etudid]
|
||||||
|
else:
|
||||||
|
return "", ""
|
||||||
else:
|
else:
|
||||||
return "", ""
|
rangs = self.ue_rangs_by_group[ue_id][group_id][0]
|
||||||
|
rang = rangs[etudid]
|
||||||
|
effectif = len(rangs)
|
||||||
return rang, effectif
|
return rang, effectif
|
||||||
|
|
||||||
|
def get_etud_rang_group(self, etudid: int, group_id: int) -> tuple[str, int]:
|
||||||
|
"""Rang de l'étudiant (selon moy gen) et effectif dans ce groupe.
|
||||||
|
Si le groupe n'a pas de rang (partition avec bul_show_rank faux), ramène "", 0
|
||||||
|
"""
|
||||||
|
if group_id in self.moy_gen_rangs_by_group:
|
||||||
|
r = self.moy_gen_rangs_by_group[group_id][0] # version en str
|
||||||
|
return (r[etudid], len(r))
|
||||||
|
else:
|
||||||
|
return "", 0
|
||||||
|
|
||||||
def etud_check_conditions_ues(self, etudid):
|
def etud_check_conditions_ues(self, etudid):
|
||||||
"""Vrai si les conditions sur les UE sont remplies.
|
"""Vrai si les conditions sur les UE sont remplies.
|
||||||
Ne considère que les UE ayant des notes (moyenne calculée).
|
Ne considère que les UE ayant des notes (moyenne calculée).
|
||||||
@ -298,16 +354,6 @@ class NotesTableCompat(ResultatsSemestre):
|
|||||||
"ects_pot_fond": 0.0, # not implemented (anciennemment pour école ingé)
|
"ects_pot_fond": 0.0, # not implemented (anciennemment pour école ingé)
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_etud_rang(self, etudid: int) -> str:
|
|
||||||
"""Le rang (classement) de l'étudiant dans le semestre.
|
|
||||||
Result: "13" ou "12 ex"
|
|
||||||
"""
|
|
||||||
return self.etud_moy_gen_ranks.get(etudid, 99999)
|
|
||||||
|
|
||||||
def get_etud_rang_group(self, etudid: int, group_id: int):
|
|
||||||
"Le rang de l'étudiant dans ce groupe (NON IMPLEMENTE)"
|
|
||||||
return (None, 0) # XXX unimplemented TODO
|
|
||||||
|
|
||||||
def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]:
|
def get_evals_in_mod(self, moduleimpl_id: int) -> list[dict]:
|
||||||
"""Liste d'informations (compat NotesTable) sur évaluations completes
|
"""Liste d'informations (compat NotesTable) sur évaluations completes
|
||||||
de ce module.
|
de ce module.
|
||||||
|
@ -82,7 +82,9 @@ def configuration():
|
|||||||
form_bonus.data["bonus_sport_func_name"]
|
form_bonus.data["bonus_sport_func_name"]
|
||||||
)
|
)
|
||||||
app.clear_scodoc_cache()
|
app.clear_scodoc_cache()
|
||||||
flash(f"Fonction bonus sport&culture configurée.")
|
flash("""Fonction bonus sport&culture configurée.""")
|
||||||
|
else:
|
||||||
|
flash("Fonction bonus inchangée.")
|
||||||
return redirect(url_for("scodoc.index"))
|
return redirect(url_for("scodoc.index"))
|
||||||
elif form_scodoc.submit_scodoc.data and form_scodoc.validate():
|
elif form_scodoc.submit_scodoc.data and form_scodoc.validate():
|
||||||
if ScoDocSiteConfig.enable_entreprises(
|
if ScoDocSiteConfig.enable_entreprises(
|
||||||
|
@ -56,11 +56,11 @@ class Identite(db.Model):
|
|||||||
#
|
#
|
||||||
adresses = db.relationship("Adresse", lazy="dynamic", backref="etud")
|
adresses = db.relationship("Adresse", lazy="dynamic", backref="etud")
|
||||||
billets = db.relationship("BilletAbsence", backref="etudiant", lazy="dynamic")
|
billets = db.relationship("BilletAbsence", backref="etudiant", lazy="dynamic")
|
||||||
# one-to-one relation:
|
#
|
||||||
admission = db.relationship("Admission", backref="identite", lazy="dynamic")
|
admission = db.relationship("Admission", backref="identite", lazy="dynamic")
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Etud {self.id} {self.nom} {self.prenom}>"
|
return f"<Etud {self.id}/{self.departement.acronym} {self.nom} {self.prenom}>"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_request(cls, etudid=None, code_nip=None):
|
def from_request(cls, etudid=None, code_nip=None):
|
||||||
@ -146,6 +146,7 @@ class Identite(db.Model):
|
|||||||
return {
|
return {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"nip": self.code_nip,
|
"nip": self.code_nip,
|
||||||
|
"ine": self.code_ine,
|
||||||
"nom": self.nom,
|
"nom": self.nom,
|
||||||
"nom_usuel": self.nom_usuel,
|
"nom_usuel": self.nom_usuel,
|
||||||
"prenom": self.prenom,
|
"prenom": self.prenom,
|
||||||
@ -177,6 +178,8 @@ class Identite(db.Model):
|
|||||||
"date_naissance": self.date_naissance.strftime("%d/%m/%Y")
|
"date_naissance": self.date_naissance.strftime("%d/%m/%Y")
|
||||||
if self.date_naissance
|
if self.date_naissance
|
||||||
else "",
|
else "",
|
||||||
|
"dept_id": self.dept_id,
|
||||||
|
"dept_acronym": self.departement.acronym,
|
||||||
"email": self.get_first_email() or "",
|
"email": self.get_first_email() or "",
|
||||||
"emailperso": self.get_first_email("emailperso"),
|
"emailperso": self.get_first_email("emailperso"),
|
||||||
"etudid": self.id,
|
"etudid": self.id,
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.models import formsemestre
|
|
||||||
from app.models.formsemestre import FormSemestre
|
|
||||||
from app.models.moduleimpls import ModuleImpl
|
from app.models.moduleimpls import ModuleImpl
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
|
|
||||||
@ -48,13 +46,25 @@ class Evaluation(db.Model):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"""<Evaluation {self.id} {self.jour.isoformat() if self.jour else ''} "{self.description[:16] if self.description else ''}">"""
|
return f"""<Evaluation {self.id} {self.jour.isoformat() if self.jour else ''} "{self.description[:16] if self.description else ''}">"""
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self) -> dict:
|
||||||
|
"Représentation dict, pour json"
|
||||||
e = dict(self.__dict__)
|
e = dict(self.__dict__)
|
||||||
e.pop("_sa_instance_state", None)
|
e.pop("_sa_instance_state", None)
|
||||||
# ScoDoc7 output_formators
|
# ScoDoc7 output_formators
|
||||||
e["evaluation_id"] = self.id
|
e["evaluation_id"] = self.id
|
||||||
e["jour"] = ndb.DateISOtoDMY(e["jour"])
|
e["jour"] = e["jour"].strftime("%d/%m/%Y") if e["jour"] else ""
|
||||||
|
if self.jour is None:
|
||||||
|
e["date_debut"] = None
|
||||||
|
e["date_fin"] = None
|
||||||
|
else:
|
||||||
|
e["date_debut"] = datetime.datetime.combine(
|
||||||
|
self.jour, self.heure_debut or datetime.time(0, 0)
|
||||||
|
).isoformat()
|
||||||
|
e["date_fin"] = datetime.datetime.combine(
|
||||||
|
self.jour, self.heure_fin or datetime.time(0, 0)
|
||||||
|
).isoformat()
|
||||||
e["numero"] = ndb.int_null_is_zero(e["numero"])
|
e["numero"] = ndb.int_null_is_zero(e["numero"])
|
||||||
|
e["poids"] = self.get_ue_poids_dict() # { ue_id : poids }
|
||||||
return evaluation_enrich_dict(e)
|
return evaluation_enrich_dict(e)
|
||||||
|
|
||||||
def from_dict(self, data):
|
def from_dict(self, data):
|
||||||
@ -153,7 +163,7 @@ class EvaluationUEPoids(db.Model):
|
|||||||
|
|
||||||
# Fonction héritée de ScoDoc7 à refactorer
|
# Fonction héritée de ScoDoc7 à refactorer
|
||||||
def evaluation_enrich_dict(e):
|
def evaluation_enrich_dict(e):
|
||||||
"""add or convert some fileds in an evaluation dict"""
|
"""add or convert some fields in an evaluation dict"""
|
||||||
# For ScoDoc7 compat
|
# For ScoDoc7 compat
|
||||||
heure_debut_dt = e["heure_debut"] or datetime.time(
|
heure_debut_dt = e["heure_debut"] or datetime.time(
|
||||||
8, 00
|
8, 00
|
||||||
@ -178,11 +188,12 @@ def evaluation_enrich_dict(e):
|
|||||||
else:
|
else:
|
||||||
e["descrheure"] = ""
|
e["descrheure"] = ""
|
||||||
# matin, apresmidi: utile pour se referer aux absences:
|
# matin, apresmidi: utile pour se referer aux absences:
|
||||||
if heure_debut_dt < datetime.time(12, 00):
|
|
||||||
|
if e["jour"] and heure_debut_dt < datetime.time(12, 00):
|
||||||
e["matin"] = 1
|
e["matin"] = 1
|
||||||
else:
|
else:
|
||||||
e["matin"] = 0
|
e["matin"] = 0
|
||||||
if heure_fin_dt > datetime.time(12, 00):
|
if e["jour"] and heure_fin_dt > datetime.time(12, 00):
|
||||||
e["apresmidi"] = 1
|
e["apresmidi"] = 1
|
||||||
else:
|
else:
|
||||||
e["apresmidi"] = 0
|
e["apresmidi"] = 0
|
||||||
|
@ -139,6 +139,7 @@ class FormSemestre(db.Model):
|
|||||||
else:
|
else:
|
||||||
d["date_fin"] = d["date_fin_iso"] = ""
|
d["date_fin"] = d["date_fin_iso"] = ""
|
||||||
d["responsables"] = [u.id for u in self.responsables]
|
d["responsables"] = [u.id for u in self.responsables]
|
||||||
|
d["titre_formation"] = self.titre_formation()
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def get_infos_dict(self) -> dict:
|
def get_infos_dict(self) -> dict:
|
||||||
@ -286,7 +287,7 @@ class FormSemestre(db.Model):
|
|||||||
"""
|
"""
|
||||||
if not self.etapes:
|
if not self.etapes:
|
||||||
return ""
|
return ""
|
||||||
return ", ".join(sorted([str(x.etape_apo) for x in self.etapes]))
|
return ", ".join(sorted([etape.etape_apo for etape in self.etapes if etape]))
|
||||||
|
|
||||||
def responsables_str(self, abbrev_prenom=True) -> str:
|
def responsables_str(self, abbrev_prenom=True) -> str:
|
||||||
"""chaîne "J. Dupond, X. Martin"
|
"""chaîne "J. Dupond, X. Martin"
|
||||||
@ -329,9 +330,10 @@ class FormSemestre(db.Model):
|
|||||||
|
|
||||||
ANNEE=annee universitaire de debut (exemple: un S2 de 2013-2014 sera S2-2013)
|
ANNEE=annee universitaire de debut (exemple: un S2 de 2013-2014 sera S2-2013)
|
||||||
"""
|
"""
|
||||||
imputation_dept = sco_preferences.get_preference("ImputationDept", self.id)
|
prefs = sco_preferences.SemPreferences(dept_id=self.dept_id)
|
||||||
|
imputation_dept = prefs["ImputationDept"]
|
||||||
if not imputation_dept:
|
if not imputation_dept:
|
||||||
imputation_dept = sco_preferences.get_preference("DeptName")
|
imputation_dept = prefs["DeptName"]
|
||||||
imputation_dept = imputation_dept.upper()
|
imputation_dept = imputation_dept.upper()
|
||||||
parcours_name = self.formation.get_parcours().NAME
|
parcours_name = self.formation.get_parcours().NAME
|
||||||
modalite = self.modalite
|
modalite = self.modalite
|
||||||
@ -346,7 +348,7 @@ class FormSemestre(db.Model):
|
|||||||
scu.annee_scolaire_debut(self.date_debut.year, self.date_debut.month)
|
scu.annee_scolaire_debut(self.date_debut.year, self.date_debut.month)
|
||||||
)
|
)
|
||||||
return scu.sanitize_string(
|
return scu.sanitize_string(
|
||||||
"-".join((imputation_dept, parcours_name, modalite, semestre_id, annee_sco))
|
f"{imputation_dept}-{parcours_name}-{modalite}-{semestre_id}-{annee_sco}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def titre_annee(self) -> str:
|
def titre_annee(self) -> str:
|
||||||
@ -358,6 +360,12 @@ class FormSemestre(db.Model):
|
|||||||
titre_annee += "-" + str(self.date_fin.year)
|
titre_annee += "-" + str(self.date_fin.year)
|
||||||
return titre_annee
|
return titre_annee
|
||||||
|
|
||||||
|
def titre_formation(self):
|
||||||
|
"""Titre avec formation, court, pour passerelle: "BUT R&T"
|
||||||
|
(méthode de formsemestre car on pourrait ajouter le semestre, ou d'autres infos, à voir)
|
||||||
|
"""
|
||||||
|
return self.formation.acronyme
|
||||||
|
|
||||||
def titre_mois(self) -> str:
|
def titre_mois(self) -> str:
|
||||||
"""Le titre et les dates du semestre, pour affichage dans des listes
|
"""Le titre et les dates du semestre, pour affichage dans des listes
|
||||||
Ex: "BUT QLIO (PN 2022) semestre 1 FI (Sept 2022 - Jan 2023)"
|
Ex: "BUT QLIO (PN 2022) semestre 1 FI (Sept 2022 - Jan 2023)"
|
||||||
@ -441,10 +449,15 @@ class FormSemestreEtape(db.Model):
|
|||||||
db.Integer,
|
db.Integer,
|
||||||
db.ForeignKey("notes_formsemestre.id"),
|
db.ForeignKey("notes_formsemestre.id"),
|
||||||
)
|
)
|
||||||
|
# etape_apo aurait du etre not null, mais oublié
|
||||||
etape_apo = db.Column(db.String(APO_CODE_STR_LEN), index=True)
|
etape_apo = db.Column(db.String(APO_CODE_STR_LEN), index=True)
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
"Etape False if code empty"
|
||||||
|
return self.etape_apo is not None and (len(self.etape_apo) > 0)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Etape {self.id} apo={self.etape_apo}>"
|
return f"<Etape {self.id} apo={self.etape_apo!r}>"
|
||||||
|
|
||||||
def as_apovdi(self):
|
def as_apovdi(self):
|
||||||
return ApoEtapeVDI(self.etape_apo)
|
return ApoEtapeVDI(self.etape_apo)
|
||||||
|
@ -25,9 +25,11 @@ class Partition(db.Model):
|
|||||||
partition_name = db.Column(db.String(SHORT_STR_LEN))
|
partition_name = db.Column(db.String(SHORT_STR_LEN))
|
||||||
# numero = ordre de presentation)
|
# numero = ordre de presentation)
|
||||||
numero = db.Column(db.Integer)
|
numero = db.Column(db.Integer)
|
||||||
|
# Calculer le rang ?
|
||||||
bul_show_rank = db.Column(
|
bul_show_rank = db.Column(
|
||||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||||
)
|
)
|
||||||
|
# Montrer quand on indique les groupes de l'étudiant ?
|
||||||
show_in_lists = db.Column(
|
show_in_lists = db.Column(
|
||||||
db.Boolean(), nullable=False, default=True, server_default="true"
|
db.Boolean(), nullable=False, default=True, server_default="true"
|
||||||
)
|
)
|
||||||
@ -50,6 +52,18 @@ class Partition(db.Model):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"""<{self.__class__.__name__} {self.id} "{self.partition_name or '(default)'}">"""
|
return f"""<{self.__class__.__name__} {self.id} "{self.partition_name or '(default)'}">"""
|
||||||
|
|
||||||
|
def to_dict(self, with_groups=False) -> dict:
|
||||||
|
"""as a dict, with or without groups"""
|
||||||
|
d = {
|
||||||
|
"id": self.id,
|
||||||
|
"formsemestre_id": self.partition_id,
|
||||||
|
"name": self.partition_name,
|
||||||
|
"numero": self.numero,
|
||||||
|
}
|
||||||
|
if with_groups:
|
||||||
|
d["groups"] = [group.to_dict(with_partition=False) for group in self.groups]
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
class GroupDescr(db.Model):
|
class GroupDescr(db.Model):
|
||||||
"""Description d'un groupe d'une partition"""
|
"""Description d'un groupe d'une partition"""
|
||||||
@ -78,6 +92,17 @@ class GroupDescr(db.Model):
|
|||||||
"Nom avec partition: 'TD A'"
|
"Nom avec partition: 'TD A'"
|
||||||
return f"{self.partition.partition_name or ''} {self.group_name or '-'}"
|
return f"{self.partition.partition_name or ''} {self.group_name or '-'}"
|
||||||
|
|
||||||
|
def to_dict(self, with_partition=True) -> dict:
|
||||||
|
"""as a dict, with or without partition"""
|
||||||
|
d = {
|
||||||
|
"id": self.id,
|
||||||
|
"partition_id": self.partition_id,
|
||||||
|
"name": self.group_name,
|
||||||
|
}
|
||||||
|
if with_partition:
|
||||||
|
d["partition"] = self.partition.to_dict(with_groups=False)
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
group_membership = db.Table(
|
group_membership = db.Table(
|
||||||
"group_membership",
|
"group_membership",
|
||||||
@ -85,3 +110,11 @@ group_membership = db.Table(
|
|||||||
db.Column("group_id", db.Integer, db.ForeignKey("group_descr.id")),
|
db.Column("group_id", db.Integer, db.ForeignKey("group_descr.id")),
|
||||||
db.UniqueConstraint("etudid", "group_id"),
|
db.UniqueConstraint("etudid", "group_id"),
|
||||||
)
|
)
|
||||||
|
# class GroupMembership(db.Model):
|
||||||
|
# """Association groupe / étudiant"""
|
||||||
|
|
||||||
|
# __tablename__ = "group_membership"
|
||||||
|
# __table_args__ = (db.UniqueConstraint("etudid", "group_id"),)
|
||||||
|
# id = db.Column(db.Integer, primary_key=True)
|
||||||
|
# etudid = db.Column(db.Integer, db.ForeignKey("identite.id"))
|
||||||
|
# group_id = db.Column(db.Integer, db.ForeignKey("group_descr.id"))
|
||||||
|
@ -75,6 +75,15 @@ class UniteEns(db.Model):
|
|||||||
|
|
||||||
return sco_edit_ue.ue_is_locked(self.id)
|
return sco_edit_ue.ue_is_locked(self.id)
|
||||||
|
|
||||||
|
def can_be_deleted(self) -> bool:
|
||||||
|
"""True si l'UE n'est pas utilisée dans des formsemestre
|
||||||
|
et n'a pas de module rattachés
|
||||||
|
"""
|
||||||
|
# "pas un seul module de cette UE n'a de modimpl...""
|
||||||
|
return (self.modules.count() == 0) or not any(
|
||||||
|
m.modimpls.all() for m in self.modules
|
||||||
|
)
|
||||||
|
|
||||||
def guess_semestre_idx(self) -> None:
|
def guess_semestre_idx(self) -> None:
|
||||||
"""Lorsqu'on prend une ancienne formation non APC,
|
"""Lorsqu'on prend une ancienne formation non APC,
|
||||||
les UE n'ont pas d'indication de semestre.
|
les UE n'ont pas d'indication de semestre.
|
||||||
|
@ -97,7 +97,7 @@ class SetTag(pe_tagtable.TableTag):
|
|||||||
"""Mémorise les semtag nécessaires au jury."""
|
"""Mémorise les semtag nécessaires au jury."""
|
||||||
self.SemTagDict = {fid: SemTagDict[fid] for fid in self.get_Fids_in_settag()}
|
self.SemTagDict = {fid: SemTagDict[fid] for fid in self.get_Fids_in_settag()}
|
||||||
if PE_DEBUG >= 1:
|
if PE_DEBUG >= 1:
|
||||||
pe_print(u" => %d semestres fusionnés" % len(self.SemTagDict))
|
pe_print(" => %d semestres fusionnés" % len(self.SemTagDict))
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------------------------------------
|
||||||
def comp_data_settag(self):
|
def comp_data_settag(self):
|
||||||
@ -210,7 +210,7 @@ class SetTagInterClasse(pe_tagtable.TableTag):
|
|||||||
# -------------------------------------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------------------------------------
|
||||||
def __init__(self, nom_combinaison, diplome):
|
def __init__(self, nom_combinaison, diplome):
|
||||||
|
|
||||||
pe_tagtable.TableTag.__init__(self, nom=nom_combinaison + "_%d" % diplome)
|
pe_tagtable.TableTag.__init__(self, nom=f"{nom_combinaison}_{diplome or ''}")
|
||||||
self.combinaison = nom_combinaison
|
self.combinaison = nom_combinaison
|
||||||
self.parcoursDict = {}
|
self.parcoursDict = {}
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ class SetTagInterClasse(pe_tagtable.TableTag):
|
|||||||
fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None
|
fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None
|
||||||
}
|
}
|
||||||
if PE_DEBUG >= 1:
|
if PE_DEBUG >= 1:
|
||||||
pe_print(u" => %d semestres utilisés" % len(self.SetTagDict))
|
pe_print(" => %d semestres utilisés" % len(self.SetTagDict))
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------------------------------------------
|
||||||
def comp_data_settag(self):
|
def comp_data_settag(self):
|
||||||
|
@ -1,492 +0,0 @@
|
|||||||
# -*- mode: python -*-
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# Gestion scolarite IUT
|
|
||||||
#
|
|
||||||
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
from operator import mul
|
|
||||||
import pprint
|
|
||||||
|
|
||||||
""" ANCIENS BONUS SPORT pour ScoDoc < 9.2 NON UTILISES A PARTIR DE 9.2 (voir comp/bonus_spo.py)
|
|
||||||
|
|
||||||
La fonction bonus_sport reçoit:
|
|
||||||
|
|
||||||
- notes_sport: la liste des notes des modules de sport et culture (une note par module
|
|
||||||
de l'UE de type sport/culture, toujours dans remise sur 20);
|
|
||||||
- coefs: un coef (float) pondérant chaque note (la plupart des bonus les ignorent);
|
|
||||||
- infos: dictionnaire avec des données pouvant être utilisées pour les calculs.
|
|
||||||
Ces données dépendent du type de formation.
|
|
||||||
infos = {
|
|
||||||
"moy" : la moyenne générale (float). 0. en BUT.
|
|
||||||
"sem" : {
|
|
||||||
"date_debut_iso" : "2010-08-01", # date de début de semestre
|
|
||||||
}
|
|
||||||
"moy_ues": {
|
|
||||||
ue_id : { # ue_status
|
|
||||||
"is_capitalized" : True|False,
|
|
||||||
"moy" : float, # moyenne d'UE prise en compte (peut-être capitalisée)
|
|
||||||
"sum_coefs": float, # > 0 si UE avec la moyenne calculée
|
|
||||||
"cur_moy_ue": float, # moyenne de l'UE (sans capitalisation))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Les notes passées sont:
|
|
||||||
- pour les formations classiques, la moyenne dans le module, calculée comme d'habitude
|
|
||||||
(moyenne pondérée des notes d'évaluations);
|
|
||||||
- pour le BUT: pareil, *en ignorant* les éventuels poids des évaluations. Le coefficient
|
|
||||||
de l'évaluation est pris en compte, mais pas les poids vers les UE.
|
|
||||||
|
|
||||||
Pour modifier les moyennes d'UE:
|
|
||||||
- modifier infos["moy_ues"][ue_id][["cur_moy_ue"]
|
|
||||||
et, seulement si l'UE n'est pas capitalisée, infos["moy_ues"][ue_id][["moy"]/
|
|
||||||
|
|
||||||
La valeur retournée est:
|
|
||||||
- formations classiques: ajoutée à la moyenne générale
|
|
||||||
- BUT: valeur multipliée par la somme des coefs modules sport ajoutée à chaque UE.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutv(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus modules optionels (sport, culture), règle IUT Villetaneuse
|
|
||||||
|
|
||||||
Les étudiants de l'IUT peuvent suivre des enseignements optionnels
|
|
||||||
de l'Université Paris 13 (sports, musique, deuxième langue,
|
|
||||||
culture, etc) non rattachés à une unité d'enseignement. Les points
|
|
||||||
au-dessus de 10 sur 20 obtenus dans chacune des matières
|
|
||||||
optionnelles sont cumulés et 5% de ces points cumulés s'ajoutent à
|
|
||||||
la moyenne générale du semestre déjà obtenue par l'étudiant.
|
|
||||||
"""
|
|
||||||
bonus = sum([(x - 10) / 20.0 for x in notes_sport if x > 10])
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_direct(notes_sport, coefs, infos=None):
|
|
||||||
"""Un bonus direct et sans chichis: les points sont directement ajoutés à la moyenne générale.
|
|
||||||
Les coefficients sont ignorés: tous les points de bonus sont sommés.
|
|
||||||
(rappel: la note est ramenée sur 20 avant application).
|
|
||||||
"""
|
|
||||||
return sum(notes_sport)
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iut_stdenis(notes_sport, coefs, infos=None):
|
|
||||||
"""Semblable à bonus_iutv mais total limité à 0.5 points."""
|
|
||||||
points = sum([x - 10 for x in notes_sport if x > 10]) # points au dessus de 10
|
|
||||||
bonus = points * 0.05 # ou / 20
|
|
||||||
return min(bonus, 0.5) # bonus limité à 1/2 point
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_colmar(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus modules optionels (sport, culture), règle IUT Colmar.
|
|
||||||
|
|
||||||
Les étudiants de l'IUT peuvent suivre des enseignements optionnels
|
|
||||||
de l'U.H.A. (sports, musique, deuxième langue, culture, etc) non
|
|
||||||
rattachés à une unité d'enseignement. Les points au-dessus de 10
|
|
||||||
sur 20 obtenus dans chacune des matières optionnelles sont cumulés
|
|
||||||
dans la limite de 10 points. 5% de ces points cumulés s'ajoutent à
|
|
||||||
la moyenne générale du semestre déjà obtenue par l'étudiant.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# les coefs sont ignorés
|
|
||||||
points = sum([x - 10 for x in notes_sport if x > 10])
|
|
||||||
points = min(10, points) # limite total à 10
|
|
||||||
bonus = points / 20.0 # 5%
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutva(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus modules optionels (sport, culture), règle IUT Ville d'Avray
|
|
||||||
|
|
||||||
Les étudiants de l'IUT peuvent suivre des enseignements optionnels
|
|
||||||
de l'Université Paris 10 (C2I) non rattachés à une unité d'enseignement.
|
|
||||||
Si la note est >= 10 et < 12, bonus de 0.1 point
|
|
||||||
Si la note est >= 12 et < 16, bonus de 0.2 point
|
|
||||||
Si la note est >= 16, bonus de 0.3 point
|
|
||||||
Ce bonus s'ajoute à la moyenne générale du semestre déjà obtenue par
|
|
||||||
l'étudiant.
|
|
||||||
"""
|
|
||||||
sumc = sum(coefs) # assumes sum. coefs > 0
|
|
||||||
note_sport = sum(map(mul, notes_sport, coefs)) / sumc # moyenne pondérée
|
|
||||||
if note_sport >= 16.0:
|
|
||||||
return 0.3
|
|
||||||
if note_sport >= 12.0:
|
|
||||||
return 0.2
|
|
||||||
if note_sport >= 10.0:
|
|
||||||
return 0.1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iut1grenoble_2017(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus sport IUT Grenoble sur la moyenne générale (version 2017)
|
|
||||||
|
|
||||||
La note de sport de nos étudiants va de 0 à 5 points.
|
|
||||||
Chaque point correspond à un % qui augmente la moyenne de chaque UE et la moyenne générale.
|
|
||||||
Par exemple : note de sport 2/5 : la moyenne générale sera augmentée de 2%.
|
|
||||||
|
|
||||||
Calcul ici du bonus sur moyenne générale
|
|
||||||
"""
|
|
||||||
# les coefs sont ignorés
|
|
||||||
# notes de 0 à 5
|
|
||||||
points = sum([x for x in notes_sport])
|
|
||||||
factor = (points / 4.0) / 100.0
|
|
||||||
bonus = infos["moy"] * factor
|
|
||||||
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_lille(notes_sport, coefs, infos=None):
|
|
||||||
"""calcul bonus modules optionels (sport, culture), règle IUT Villeneuve d'Ascq
|
|
||||||
|
|
||||||
Les étudiants de l'IUT peuvent suivre des enseignements optionnels
|
|
||||||
de l'Université Lille 1 (sports,etc) non rattachés à une unité d'enseignement. Les points
|
|
||||||
au-dessus de 10 sur 20 obtenus dans chacune des matières
|
|
||||||
optionnelles sont cumulés et 4% (2% avant aout 2010) de ces points cumulés s'ajoutent à
|
|
||||||
la moyenne générale du semestre déjà obtenue par l'étudiant.
|
|
||||||
"""
|
|
||||||
if (
|
|
||||||
infos["sem"]["date_debut_iso"] > "2010-08-01"
|
|
||||||
): # changement de regle en aout 2010.
|
|
||||||
return sum([(x - 10) / 25.0 for x in notes_sport if x > 10])
|
|
||||||
return sum([(x - 10) / 50.0 for x in notes_sport if x > 10])
|
|
||||||
|
|
||||||
|
|
||||||
# Fonction Le Havre, par Dom. Soud.
|
|
||||||
def bonus_iutlh(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus sport IUT du Havre sur moyenne générale et UE
|
|
||||||
|
|
||||||
La note de sport de nos étudiants va de 0 à 20 points.
|
|
||||||
m2=m1*(1+0.005*((10-N1)+(10-N2))
|
|
||||||
m2 : Nouvelle moyenne de l'unité d'enseignement si note de sport et/ou de langue supérieure à 10
|
|
||||||
m1 : moyenne de l'unité d'enseignement avant bonification
|
|
||||||
N1 : note de sport si supérieure à 10
|
|
||||||
N2 : note de seconde langue si supérieure à 10
|
|
||||||
Par exemple : sport 15/20 et langue 12/20 : chaque UE sera multipliée par 1+0.005*7, ainsi que la moyenne générale.
|
|
||||||
Calcul ici de la moyenne générale et moyennes d'UE non capitalisées.
|
|
||||||
"""
|
|
||||||
# les coefs sont ignorés
|
|
||||||
points = sum([x - 10 for x in notes_sport if x > 10])
|
|
||||||
points = min(10, points) # limite total à 10
|
|
||||||
factor = 1.0 + (0.005 * points)
|
|
||||||
# bonus nul puisque les moyennes sont directement modifiées par factor
|
|
||||||
bonus = 0
|
|
||||||
# Modifie la moyenne générale
|
|
||||||
infos["moy"] = infos["moy"] * factor
|
|
||||||
# Modifie les moyennes de toutes les UE:
|
|
||||||
for ue_id in infos["moy_ues"]:
|
|
||||||
ue_status = infos["moy_ues"][ue_id]
|
|
||||||
if ue_status["sum_coefs"] > 0:
|
|
||||||
# modifie moyenne UE ds semestre courant
|
|
||||||
ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] * factor
|
|
||||||
if not ue_status["is_capitalized"]:
|
|
||||||
# si non capitalisee, modifie moyenne prise en compte
|
|
||||||
ue_status["moy"] = ue_status["cur_moy_ue"]
|
|
||||||
|
|
||||||
# open('/tmp/log','a').write( pprint.pformat(ue_status) + '\n\n' )
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_nantes(notes_sport, coefs, infos=None):
|
|
||||||
"""IUT de Nantes (Septembre 2018)
|
|
||||||
Nous avons différents types de bonification
|
|
||||||
bonfication Sport / Culture / engagement citoyen
|
|
||||||
Nous ajoutons sur le bulletin une bonification de 0,2 pour chaque item
|
|
||||||
la bonification totale ne doit pas excéder les 0,5 point.
|
|
||||||
Sur le bulletin nous ne mettons pas une note sur 20 mais directement les bonifications.
|
|
||||||
|
|
||||||
Dans ScoDoc: on a déclaré une UE "sport&culture" dans laquelle on aura des modules
|
|
||||||
pour chaque activité (Sport, Associations, ...)
|
|
||||||
avec à chaque fois une note (ScoDoc l'affichera comme une note sur 20, mais en fait ce sera la
|
|
||||||
valeur de la bonification: entrer 0,1/20 signifiera un bonus de 0,1 point la moyenne générale)
|
|
||||||
"""
|
|
||||||
bonus = min(0.5, sum([x for x in notes_sport])) # plafonnement à 0.5 points
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
# Bonus sport IUT Tours
|
|
||||||
def bonus_tours(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus sport & culture IUT Tours sur moyenne generale
|
|
||||||
|
|
||||||
La note de sport & culture de nos etudiants est applique sur la moyenne generale.
|
|
||||||
"""
|
|
||||||
return min(1.0, sum(notes_sport)) # bonus maximum de 1 point
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutr(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul du bonus , règle de l'IUT de Roanne
|
|
||||||
(contribuée par Raphael C., nov 2012)
|
|
||||||
|
|
||||||
Le bonus est compris entre 0 et 0.35 point.
|
|
||||||
cette procédure modifie la moyenne de chaque UE capitalisable.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# modifie les moyennes de toutes les UE:
|
|
||||||
# le bonus est le minimum entre 0.35 et la somme de toutes les bonifs
|
|
||||||
bonus = min(0.35, sum([x for x in notes_sport]))
|
|
||||||
for ue_id in infos["moy_ues"]:
|
|
||||||
# open('/tmp/log','a').write( str(ue_id) + infos['moy_ues'] + '\n\n' )
|
|
||||||
ue_status = infos["moy_ues"][ue_id]
|
|
||||||
if ue_status["sum_coefs"] > 0:
|
|
||||||
# modifie moyenne UE dans semestre courant
|
|
||||||
ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] + bonus
|
|
||||||
if not ue_status["is_capitalized"]:
|
|
||||||
ue_status["moy"] = ue_status["cur_moy_ue"]
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutam(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus modules optionels (sport), regle IUT d'Amiens.
|
|
||||||
Les etudiants de l'IUT peuvent suivre des enseignements optionnels.
|
|
||||||
Si la note est de 10.00 a 10.49 -> 0.50% de la moyenne
|
|
||||||
Si la note est de 10.50 a 10.99 -> 0.75%
|
|
||||||
Si la note est de 11.00 a 11.49 -> 1.00%
|
|
||||||
Si la note est de 11.50 a 11.99 -> 1.25%
|
|
||||||
Si la note est de 12.00 a 12.49 -> 1.50%
|
|
||||||
Si la note est de 12.50 a 12.99 -> 1.75%
|
|
||||||
Si la note est de 13.00 a 13.49 -> 2.00%
|
|
||||||
Si la note est de 13.50 a 13.99 -> 2.25%
|
|
||||||
Si la note est de 14.00 a 14.49 -> 2.50%
|
|
||||||
Si la note est de 14.50 a 14.99 -> 2.75%
|
|
||||||
Si la note est de 15.00 a 15.49 -> 3.00%
|
|
||||||
Si la note est de 15.50 a 15.99 -> 3.25%
|
|
||||||
Si la note est de 16.00 a 16.49 -> 3.50%
|
|
||||||
Si la note est de 16.50 a 16.99 -> 3.75%
|
|
||||||
Si la note est de 17.00 a 17.49 -> 4.00%
|
|
||||||
Si la note est de 17.50 a 17.99 -> 4.25%
|
|
||||||
Si la note est de 18.00 a 18.49 -> 4.50%
|
|
||||||
Si la note est de 18.50 a 18.99 -> 4.75%
|
|
||||||
Si la note est de 19.00 a 20.00 -> 5.00%
|
|
||||||
Ce bonus s'ajoute a la moyenne generale du semestre de l'etudiant.
|
|
||||||
"""
|
|
||||||
# une seule note
|
|
||||||
note_sport = notes_sport[0]
|
|
||||||
if note_sport < 10.0:
|
|
||||||
return 0.0
|
|
||||||
prc = min((int(2 * note_sport - 20.0) + 2) * 0.25, 5)
|
|
||||||
bonus = infos["moy"] * prc / 100.0
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_saint_etienne(notes_sport, coefs, infos=None):
|
|
||||||
"""IUT de Saint-Etienne (jan 2014)
|
|
||||||
Nous avons différents types de bonification
|
|
||||||
bonfication Sport / Associations
|
|
||||||
coopératives de département / Bureau Des Étudiants
|
|
||||||
/ engagement citoyen / Langues optionnelles
|
|
||||||
Nous ajoutons sur le bulletin une bonification qui varie entre 0,1 et 0,3 ou 0,35 pour chaque item
|
|
||||||
la bonification totale ne doit pas excéder les 0,6 point.
|
|
||||||
Sur le bulletin nous ne mettons pas une note sur 20 mais directement les bonifications.
|
|
||||||
|
|
||||||
|
|
||||||
Dans ScoDoc: on a déclarer une UE "sport&culture" dans laquelle on aura des modules
|
|
||||||
pour chaque activité (Sport, Associations, ...)
|
|
||||||
avec à chaque fois une note (ScoDoc l'affichera comme une note sur 20, mais en fait ce sera la
|
|
||||||
valeur de la bonification: entrer 0,1/20 signifiera un bonus de 0,1 point la moyenne générale)
|
|
||||||
"""
|
|
||||||
bonus = min(0.6, sum([x for x in notes_sport])) # plafonnement à 0.6 points
|
|
||||||
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutTarbes(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus modules optionnels
|
|
||||||
(sport, Langues, action sociale, Théâtre), règle IUT Tarbes
|
|
||||||
Les coefficients ne sont pas pris en compte,
|
|
||||||
seule la meilleure note est prise en compte
|
|
||||||
le 1/30ème des points au-dessus de 10 sur 20 est retenu et s'ajoute à
|
|
||||||
la moyenne générale du semestre déjà obtenue par l'étudiant.
|
|
||||||
"""
|
|
||||||
bonus = max([(x - 10) / 30.0 for x in notes_sport if x > 10] or [0.0])
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutSN(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus sport IUT Saint-Nazaire sur moyenne générale
|
|
||||||
|
|
||||||
La note de sport de nos étudiants va de 0 à 5 points.
|
|
||||||
La note de culture idem,
|
|
||||||
Elles sont cumulables,
|
|
||||||
Chaque point correspond à un % qui augmente la moyenne générale.
|
|
||||||
Par exemple : note de sport 2/5 : la moyenne générale sera augmentée de 2%.
|
|
||||||
|
|
||||||
Calcul ici du bonus sur moyenne générale et moyennes d'UE non capitalisées.
|
|
||||||
"""
|
|
||||||
# les coefs sont ignorés
|
|
||||||
# notes de 0 à 5
|
|
||||||
points = sum([x for x in notes_sport])
|
|
||||||
factor = points / 100.0
|
|
||||||
bonus = infos["moy"] * factor
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutBordeaux1(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus modules optionels (sport, culture), règle IUT Bordeaux 1, sur moyenne générale et UE
|
|
||||||
|
|
||||||
Les étudiants de l'IUT peuvent suivre des enseignements optionnels
|
|
||||||
de l'Université Bordeaux 1 (sport, théâtre) non rattachés à une unité d'enseignement.
|
|
||||||
En cas de double activité, c'est la meilleure des 2 notes qui compte.
|
|
||||||
Chaque point au-dessus de 10 sur 20 obtenus dans cet enseignement correspond à un %
|
|
||||||
qui augmente la moyenne de chaque UE et la moyenne générale.
|
|
||||||
Formule : le % = points>moyenne / 2
|
|
||||||
Par exemple : sport 13/20 : chaque UE sera multipliée par 1+0,015, ainsi que la moyenne générale.
|
|
||||||
|
|
||||||
Calcul ici du bonus sur moyenne générale et moyennes d'UE non capitalisées.
|
|
||||||
"""
|
|
||||||
# open('/tmp/log','a').write( '\n---------------\n' + pprint.pformat(infos) + '\n' )
|
|
||||||
# les coefs sont ignorés
|
|
||||||
# on récupère la note maximum et les points au-dessus de la moyenne
|
|
||||||
sport = max(notes_sport)
|
|
||||||
points = max(0, sport - 10)
|
|
||||||
# on calcule le bonus
|
|
||||||
factor = (points / 2.0) / 100.0
|
|
||||||
bonus = infos["moy"] * factor
|
|
||||||
# Modifie les moyennes de toutes les UE:
|
|
||||||
for ue_id in infos["moy_ues"]:
|
|
||||||
ue_status = infos["moy_ues"][ue_id]
|
|
||||||
if ue_status["sum_coefs"] > 0:
|
|
||||||
# modifie moyenne UE ds semestre courant
|
|
||||||
ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] * (1.0 + factor)
|
|
||||||
if not ue_status["is_capitalized"]:
|
|
||||||
# si non capitalisee, modifie moyenne prise en compte
|
|
||||||
ue_status["moy"] = ue_status["cur_moy_ue"]
|
|
||||||
|
|
||||||
# open('/tmp/log','a').write( pprint.pformat(ue_status) + '\n\n' )
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iuto(notes_sport, coefs, infos=None): # OBSOLETE => EN ATTENTE (27/01/2022)
|
|
||||||
"""Calcul bonus modules optionels (sport, culture), règle IUT Orleans
|
|
||||||
* Avant aout 2013
|
|
||||||
Un bonus de 2,5% de la note de sport est accordé à chaque UE sauf
|
|
||||||
les UE de Projet et Stages
|
|
||||||
* Après aout 2013
|
|
||||||
Un bonus de 2,5% de la note de sport est accordé à la moyenne générale
|
|
||||||
"""
|
|
||||||
sumc = sum(coefs) # assumes sum. coefs > 0
|
|
||||||
note_sport = sum(map(mul, notes_sport, coefs)) / sumc # moyenne pondérée
|
|
||||||
bonus = note_sport * 2.5 / 100
|
|
||||||
if (
|
|
||||||
infos["sem"]["date_debut_iso"] > "2013-08-01"
|
|
||||||
): # changement de regle en aout 2013.
|
|
||||||
return bonus
|
|
||||||
coefs = 0.0
|
|
||||||
coefs_total = 0.0
|
|
||||||
for ue_id in infos["moy_ues"]:
|
|
||||||
ue_status = infos["moy_ues"][ue_id]
|
|
||||||
coefs_total = coefs_total + ue_status["sum_coefs"]
|
|
||||||
# Extremement spécifique (et n'est plus utilisé)
|
|
||||||
if ue_status["ue"]["ue_code"] not in {
|
|
||||||
"ORA14",
|
|
||||||
"ORA24",
|
|
||||||
"ORA34",
|
|
||||||
"ORA44",
|
|
||||||
"ORB34",
|
|
||||||
"ORB44",
|
|
||||||
"ORD42",
|
|
||||||
"ORE14",
|
|
||||||
"ORE25",
|
|
||||||
"ORN44",
|
|
||||||
"ORO44",
|
|
||||||
"ORP44",
|
|
||||||
"ORV34",
|
|
||||||
"ORV42",
|
|
||||||
"ORV43",
|
|
||||||
}:
|
|
||||||
if ue_status["sum_coefs"] > 0:
|
|
||||||
coefs = coefs + ue_status["sum_coefs"]
|
|
||||||
# modifie moyenne UE ds semestre courant
|
|
||||||
ue_status["cur_moy_ue"] = ue_status["cur_moy_ue"] + bonus
|
|
||||||
if not ue_status["is_capitalized"]:
|
|
||||||
# si non capitalisee, modifie moyenne prise en compte
|
|
||||||
ue_status["moy"] = ue_status["cur_moy_ue"]
|
|
||||||
return bonus * coefs / coefs_total
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutbethune(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus modules optionels (sport), règle IUT Bethune
|
|
||||||
|
|
||||||
Les points au dessus de la moyenne de 10 apportent un bonus pour le semestre.
|
|
||||||
Ce bonus est égal au nombre de points divisé par 200 et multiplié par la
|
|
||||||
moyenne générale du semestre de l'étudiant.
|
|
||||||
"""
|
|
||||||
# les coefs sont ignorés
|
|
||||||
points = sum([x - 10 for x in notes_sport if x > 10])
|
|
||||||
points = min(10, points) # limite total à 10
|
|
||||||
bonus = int(infos["moy"] * points / 2) / 100.0 # moyenne-semestre x points x 0,5%
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutbeziers(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus modules optionels (sport, culture), regle IUT BEZIERS
|
|
||||||
|
|
||||||
Les étudiants de l'IUT peuvent suivre des enseignements optionnels
|
|
||||||
sport , etc) non rattaches à une unité d'enseignement. Les points
|
|
||||||
au-dessus de 10 sur 20 obtenus dans chacune des matières
|
|
||||||
optionnelles sont cumulés et 3% de ces points cumulés s'ajoutent à
|
|
||||||
la moyenne générale du semestre déjà obtenue par l'étudiant.
|
|
||||||
"""
|
|
||||||
sumc = sum(coefs) # assumes sum. coefs > 0
|
|
||||||
# note_sport = sum(map(mul, notes_sport, coefs)) / sumc # moyenne pondérée
|
|
||||||
bonus = sum([(x - 10) * 0.03 for x in notes_sport if x > 10])
|
|
||||||
# le total du bonus ne doit pas dépasser 0.3 - Fred, 28/01/2020
|
|
||||||
|
|
||||||
if bonus > 0.3:
|
|
||||||
bonus = 0.3
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutlemans(notes_sport, coefs, infos=None):
|
|
||||||
"fake: formule inutilisée en ScoDoc 9.2 mais doiut être présente"
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_iutlr(notes_sport, coefs, infos=None):
|
|
||||||
"""Calcul bonus modules optionels (sport, culture), règle IUT La Rochelle
|
|
||||||
Si la note de sport est comprise entre 0 et 10 : pas d'ajout de point
|
|
||||||
Si la note de sport est comprise entre 10.1 et 20 : ajout de 1% de cette note sur la moyenne générale du semestre
|
|
||||||
"""
|
|
||||||
# les coefs sont ignorés
|
|
||||||
# une seule note
|
|
||||||
note_sport = notes_sport[0]
|
|
||||||
if note_sport <= 10:
|
|
||||||
return 0
|
|
||||||
bonus = note_sport * 0.01 # 1%
|
|
||||||
return bonus
|
|
||||||
|
|
||||||
|
|
||||||
def bonus_demo(notes_sport, coefs, infos=None):
|
|
||||||
"""Fausse fonction "bonus" pour afficher les informations disponibles
|
|
||||||
et aider les développeurs.
|
|
||||||
Les informations sont placées dans le fichier /tmp/scodoc_bonus.log
|
|
||||||
qui est ECRASE à chaque appel.
|
|
||||||
*** Ne pas utiliser en production !!! ***
|
|
||||||
"""
|
|
||||||
with open("/tmp/scodoc_bonus.log", "w") as f: # mettre 'a' pour ajouter en fin
|
|
||||||
f.write("\n---------------\n" + pprint.pformat(infos) + "\n")
|
|
||||||
# Statut de chaque UE
|
|
||||||
# for ue_id in infos['moy_ues']:
|
|
||||||
# ue_status = infos['moy_ues'][ue_id]
|
|
||||||
# #open('/tmp/log','a').write( pprint.pformat(ue_status) + '\n\n' )
|
|
||||||
|
|
||||||
return 0.0
|
|
@ -965,7 +965,7 @@ def _tables_abs_etud(
|
|||||||
)[0]
|
)[0]
|
||||||
if format == "html":
|
if format == "html":
|
||||||
ex.append(
|
ex.append(
|
||||||
f"""<a href="{url_for('notes.moduleimpl_status',
|
f"""<a title="{mod['module']['titre']}" href="{url_for('notes.moduleimpl_status',
|
||||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
||||||
">{mod["module"]["code"] or "(module sans code)"}</a>"""
|
">{mod["module"]["code"] or "(module sans code)"}</a>"""
|
||||||
)
|
)
|
||||||
@ -983,7 +983,8 @@ def _tables_abs_etud(
|
|||||||
)[0]
|
)[0]
|
||||||
if format == "html":
|
if format == "html":
|
||||||
ex.append(
|
ex.append(
|
||||||
f"""<a href="{url_for('notes.moduleimpl_status',
|
f"""<a title="{mod['module']['titre']}"
|
||||||
|
href="{url_for('notes.moduleimpl_status',
|
||||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
||||||
">{mod["module"]["code"] or '(module sans code)'}</a>"""
|
">{mod["module"]["code"] or '(module sans code)'}</a>"""
|
||||||
)
|
)
|
||||||
|
@ -251,7 +251,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
rang = ""
|
rang = ""
|
||||||
|
|
||||||
rang_gr, ninscrits_gr, gr_name = get_etud_rangs_groups(
|
rang_gr, ninscrits_gr, gr_name = get_etud_rangs_groups(
|
||||||
etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
etudid, partitions, partitions_etud_groups, nt
|
||||||
)
|
)
|
||||||
|
|
||||||
if nt.get_moduleimpls_attente():
|
if nt.get_moduleimpls_attente():
|
||||||
@ -651,7 +651,7 @@ def _ue_mod_bulletin(
|
|||||||
|
|
||||||
|
|
||||||
def get_etud_rangs_groups(
|
def get_etud_rangs_groups(
|
||||||
etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
etudid: int, partitions, partitions_etud_groups, nt: NotesTableCompat
|
||||||
):
|
):
|
||||||
"""Ramene rang et nb inscrits dans chaque partition"""
|
"""Ramene rang et nb inscrits dans chaque partition"""
|
||||||
rang_gr, ninscrits_gr, gr_name = {}, {}, {}
|
rang_gr, ninscrits_gr, gr_name = {}, {}, {}
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from app.but import bulletin_but
|
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models.formsemestre import FormSemestre
|
from app.models.formsemestre import FormSemestre
|
||||||
@ -92,7 +91,7 @@ def formsemestre_bulletinetud_published_dict(
|
|||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||||
|
|
||||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
d = {}
|
d = {"type": "classic", "version": "0"}
|
||||||
|
|
||||||
if (not sem["bul_hide_xml"]) or force_publishing:
|
if (not sem["bul_hide_xml"]) or force_publishing:
|
||||||
published = True
|
published = True
|
||||||
@ -166,7 +165,7 @@ def formsemestre_bulletinetud_published_dict(
|
|||||||
else:
|
else:
|
||||||
rang = str(nt.get_etud_rang(etudid))
|
rang = str(nt.get_etud_rang(etudid))
|
||||||
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
|
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
|
||||||
etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
etudid, partitions, partitions_etud_groups, nt
|
||||||
)
|
)
|
||||||
|
|
||||||
d["note"] = dict(
|
d["note"] = dict(
|
||||||
|
@ -172,7 +172,7 @@ def make_xml_formsemestre_bulletinetud(
|
|||||||
else:
|
else:
|
||||||
rang = str(nt.get_etud_rang(etudid))
|
rang = str(nt.get_etud_rang(etudid))
|
||||||
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
|
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
|
||||||
etudid, formsemestre_id, partitions, partitions_etud_groups, nt
|
etudid, partitions, partitions_etud_groups, nt
|
||||||
)
|
)
|
||||||
|
|
||||||
doc.append(
|
doc.append(
|
||||||
|
@ -228,7 +228,7 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
|
|||||||
if getattr(g, "defer_cache_invalidation", False):
|
if getattr(g, "defer_cache_invalidation", False):
|
||||||
g.sem_to_invalidate.add(formsemestre_id)
|
g.sem_to_invalidate.add(formsemestre_id)
|
||||||
return
|
return
|
||||||
log("inval_cache, formsemestre_id={formsemestre_id} pdfonly={pdfonly}")
|
log(f"inval_cache, formsemestre_id={formsemestre_id} pdfonly={pdfonly}")
|
||||||
if formsemestre_id is None:
|
if formsemestre_id is None:
|
||||||
# clear all caches
|
# clear all caches
|
||||||
log("----- invalidate_formsemestre: clearing all caches -----")
|
log("----- invalidate_formsemestre: clearing all caches -----")
|
||||||
@ -272,7 +272,7 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
|
|||||||
SemBulletinsPDFCache.invalidate_sems(formsemestre_ids)
|
SemBulletinsPDFCache.invalidate_sems(formsemestre_ids)
|
||||||
|
|
||||||
|
|
||||||
class DefferedSemCacheManager:
|
class DeferredSemCacheManager:
|
||||||
"""Contexte pour effectuer des opérations indépendantes dans la
|
"""Contexte pour effectuer des opérations indépendantes dans la
|
||||||
même requete qui invalident le cache. Par exemple, quand on inscrit
|
même requete qui invalident le cache. Par exemple, quand on inscrit
|
||||||
des étudiants un par un à un semestre, chaque inscription va invalider
|
des étudiants un par un à un semestre, chaque inscription va invalider
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""Configuration de ScoDoc (version ScoDOc 9)
|
"""Configuration de ScoDoc (version ScoDoc 9)
|
||||||
NE PAS MODIFIER localement ce fichier !
|
NE PAS MODIFIER localement ce fichier !
|
||||||
mais éditer /opt/scodoc-data/config/scodoc_local.py
|
mais éditer /opt/scodoc-data/config/scodoc_local.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from app.scodoc import bonus_sport
|
|
||||||
|
|
||||||
|
|
||||||
class AttrDict(dict):
|
class AttrDict(dict):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -131,8 +131,10 @@ def index_html(showcodes=0, showsemtable=0):
|
|||||||
if not showsemtable:
|
if not showsemtable:
|
||||||
H.append(
|
H.append(
|
||||||
f"""<hr>
|
f"""<hr>
|
||||||
<p><a class="stdlink" href="{url_for('scolar.index_html', scodoc_dept=g.scodoc_dept, showsemtable=1)
|
<p><a class="stdlink" href="{url_for('scolar.index_html',
|
||||||
}">Voir tous les semestres ({len(othersems)} verrouillés)</a>
|
scodoc_dept=g.scodoc_dept, showsemtable=1)
|
||||||
|
}">Voir table des semestres (dont {len(othersems)}
|
||||||
|
verrouillé{'s' if len(othersems) else ''})</a>
|
||||||
</p>"""
|
</p>"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,8 +66,9 @@ def formation_delete(formation_id=None, dialog_confirmed=False):
|
|||||||
sems = sco_formsemestre.do_formsemestre_list({"formation_id": formation_id})
|
sems = sco_formsemestre.do_formsemestre_list({"formation_id": formation_id})
|
||||||
if sems:
|
if sems:
|
||||||
H.append(
|
H.append(
|
||||||
"""<p class="warning">Impossible de supprimer cette formation, car les sessions suivantes l'utilisent:</p>
|
"""<p class="warning">Impossible de supprimer cette formation,
|
||||||
<ul>"""
|
car les sessions suivantes l'utilisent:</p>
|
||||||
|
<ul>"""
|
||||||
)
|
)
|
||||||
for sem in sems:
|
for sem in sems:
|
||||||
H.append(
|
H.append(
|
||||||
|
@ -33,7 +33,7 @@ from flask import url_for, render_template
|
|||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
from app import log
|
from app import db, log
|
||||||
from app import models
|
from app import models
|
||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
from app.models import Formation, Matiere, Module, UniteEns
|
from app.models import Formation, Matiere, Module, UniteEns
|
||||||
@ -359,7 +359,6 @@ def can_delete_module(module):
|
|||||||
|
|
||||||
def do_module_delete(oid):
|
def do_module_delete(oid):
|
||||||
"delete module"
|
"delete module"
|
||||||
from app.scodoc import sco_formations
|
|
||||||
|
|
||||||
module = Module.query.get_or_404(oid)
|
module = Module.query.get_or_404(oid)
|
||||||
mod = module_list({"module_id": oid})[0] # sco7
|
mod = module_list({"module_id": oid})[0] # sco7
|
||||||
@ -422,13 +421,14 @@ def module_delete(module_id=None):
|
|||||||
|
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
html_sco_header.sco_header(page_title="Suppression d'un module"),
|
||||||
"""<h2>Suppression du module %(titre)s (%(code)s)</h2>""" % mod,
|
f"""<h2>Suppression du module {module.titre or "<em>sans titre</em>"} ({module.code})</h2>""",
|
||||||
]
|
]
|
||||||
|
|
||||||
dest_url = url_for(
|
dest_url = url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=str(mod["formation_id"]),
|
formation_id=module.formation_id,
|
||||||
|
semestre_idx=module.ue.semestre_idx,
|
||||||
)
|
)
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
request.base_url,
|
request.base_url,
|
||||||
@ -848,21 +848,13 @@ def module_count_moduleimpls(module_id):
|
|||||||
|
|
||||||
def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
||||||
"""Création d'un module de "malus" dans chaque UE d'une formation"""
|
"""Création d'un module de "malus" dans chaque UE d'une formation"""
|
||||||
from app.scodoc import sco_edit_ue
|
|
||||||
|
|
||||||
ues = sco_edit_ue.ue_list(args={"formation_id": formation_id})
|
formation = Formation.query.get_or_404(formation_id)
|
||||||
|
|
||||||
for ue in ues:
|
for ue in formation.ues:
|
||||||
# Un seul module de malus par UE:
|
ue_add_malus_module(ue, titre=titre)
|
||||||
nb_mod_malus = len(
|
|
||||||
[
|
formation.invalidate_cached_sems()
|
||||||
mod
|
|
||||||
for mod in module_list(args={"ue_id": ue["ue_id"]})
|
|
||||||
if mod["module_type"] == scu.ModuleType.MALUS
|
|
||||||
]
|
|
||||||
)
|
|
||||||
if nb_mod_malus == 0:
|
|
||||||
ue_add_malus_module(ue["ue_id"], titre=titre)
|
|
||||||
|
|
||||||
if redirect:
|
if redirect:
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
@ -872,46 +864,58 @@ def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ue_add_malus_module(ue_id, titre=None, code=None):
|
def ue_add_malus_module(ue: UniteEns, titre=None, code=None) -> int:
|
||||||
"""Add a malus module in this ue"""
|
"""Add a malus module in this ue.
|
||||||
from app.scodoc import sco_edit_ue
|
If already exists, do nothing.
|
||||||
|
Returns id of malus module.
|
||||||
|
"""
|
||||||
|
modules_malus = [m for m in ue.modules if m.module_type == scu.ModuleType.MALUS]
|
||||||
|
if len(modules_malus) > 0:
|
||||||
|
return modules_malus[0].id # déjà existant
|
||||||
|
|
||||||
ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
|
titre = titre or ""
|
||||||
|
code = code or f"MALUS{ue.numero}"
|
||||||
if titre is None:
|
|
||||||
titre = ""
|
|
||||||
if code is None:
|
|
||||||
code = "MALUS%d" % ue["numero"]
|
|
||||||
|
|
||||||
# Tout module doit avoir un semestre_id (indice 1, 2, ...)
|
# Tout module doit avoir un semestre_id (indice 1, 2, ...)
|
||||||
semestre_ids = sco_edit_ue.ue_list_semestre_ids(ue)
|
if ue.semestre_idx is None:
|
||||||
if semestre_ids:
|
semestre_ids = sorted(list(set([m.semestre_id for m in ue.modules])))
|
||||||
semestre_id = semestre_ids[0]
|
if len(semestre_ids) > 0:
|
||||||
|
semestre_id = semestre_ids[0]
|
||||||
|
else:
|
||||||
|
# c'est ennuyeux: dans ce cas, on pourrait demander à indiquer explicitement
|
||||||
|
# le semestre ? ou affecter le malus au semestre 1 ???
|
||||||
|
raise ScoValueError(
|
||||||
|
"Impossible d'ajouter un malus s'il n'y a pas d'autres modules"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# c'est ennuyeux: dans ce cas, on pourrait demander à indiquer explicitement
|
semestre_id = ue.semestre_idx
|
||||||
# le semestre ? ou affecter le malus au semestre 1 ???
|
|
||||||
raise ScoValueError(
|
|
||||||
"Impossible d'ajouter un malus s'il n'y a pas d'autres modules"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Matiere pour placer le module malus
|
# Matiere pour placer le module malus
|
||||||
Matlist = sco_edit_matiere.matiere_list(args={"ue_id": ue_id})
|
titre_matiere_malus = "Malus"
|
||||||
numero = max([mat["numero"] for mat in Matlist]) + 10
|
|
||||||
matiere_id = sco_edit_matiere.do_matiere_create(
|
|
||||||
{"ue_id": ue_id, "titre": "Malus", "numero": numero}
|
|
||||||
)
|
|
||||||
|
|
||||||
module_id = do_module_create(
|
matieres_malus = [mat for mat in ue.matieres if mat.titre == titre_matiere_malus]
|
||||||
{
|
if len(matieres_malus) > 0:
|
||||||
"titre": titre,
|
# matière Malus déjà existante, l'utilise
|
||||||
"code": code,
|
matiere = matieres_malus[0]
|
||||||
"coefficient": 0.0, # unused
|
else:
|
||||||
"ue_id": ue_id,
|
if ue.matieres.count() > 0:
|
||||||
"matiere_id": matiere_id,
|
numero = max([mat.numero for mat in ue.matieres]) + 10
|
||||||
"formation_id": ue["formation_id"],
|
else:
|
||||||
"semestre_id": semestre_id,
|
numero = 0
|
||||||
"module_type": scu.ModuleType.MALUS,
|
matiere = Matiere(ue_id=ue.id, titre=titre_matiere_malus, numero=numero)
|
||||||
},
|
db.session.add(matiere)
|
||||||
)
|
|
||||||
|
|
||||||
return module_id
|
module = Module(
|
||||||
|
titre=titre,
|
||||||
|
code=code,
|
||||||
|
coefficient=0.0,
|
||||||
|
ue=ue,
|
||||||
|
matiere=matiere,
|
||||||
|
formation=ue.formation,
|
||||||
|
semestre_id=semestre_id,
|
||||||
|
module_type=scu.ModuleType.MALUS,
|
||||||
|
)
|
||||||
|
db.session.add(module)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return module.id
|
||||||
|
@ -142,29 +142,23 @@ def do_ue_create(args):
|
|||||||
return ue_id
|
return ue_id
|
||||||
|
|
||||||
|
|
||||||
def can_delete_ue(ue: UniteEns) -> bool:
|
|
||||||
"""True si l'UE n'est pas utilisée dans des formsemestre
|
|
||||||
et n'a pas de module rattachés
|
|
||||||
"""
|
|
||||||
# "pas un seul module de cette UE n'a de modimpl...""
|
|
||||||
return (not len(ue.modules.all())) and not any(m.modimpls.all() for m in ue.modules)
|
|
||||||
|
|
||||||
|
|
||||||
def do_ue_delete(ue_id, delete_validations=False, force=False):
|
def do_ue_delete(ue_id, delete_validations=False, force=False):
|
||||||
"delete UE and attached matieres (but not modules)"
|
"delete UE and attached matieres (but not modules)"
|
||||||
from app.scodoc import sco_formations
|
from app.scodoc import sco_formations
|
||||||
from app.scodoc import sco_parcours_dut
|
from app.scodoc import sco_parcours_dut
|
||||||
|
|
||||||
ue = UniteEns.query.get_or_404(ue_id)
|
ue = UniteEns.query.get_or_404(ue_id)
|
||||||
if not can_delete_ue(ue):
|
formation_id = ue.formation_id
|
||||||
|
semestre_idx = ue.semestre_idx
|
||||||
|
if not ue.can_be_deleted():
|
||||||
raise ScoNonEmptyFormationObject(
|
raise ScoNonEmptyFormationObject(
|
||||||
"UE",
|
f"UE (id={ue.id}, dud)",
|
||||||
msg=ue.titre,
|
msg=ue.titre,
|
||||||
dest_url=url_for(
|
dest_url=url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=ue.formation_id,
|
formation_id=formation_id,
|
||||||
semestre_idx=ue.semestre_idx,
|
semestre_idx=semestre_idx,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -187,13 +181,13 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
cancel_url=url_for(
|
cancel_url=url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=ue.formation_id,
|
formation_id=formation_id,
|
||||||
semestre_idx=ue.semestre_idx,
|
semestre_idx=semestre_idx,
|
||||||
),
|
),
|
||||||
parameters={"ue_id": ue.id, "dialog_confirmed": 1},
|
parameters={"ue_id": ue.id, "dialog_confirmed": 1},
|
||||||
)
|
)
|
||||||
if delete_validations:
|
if delete_validations:
|
||||||
log("deleting all validations of UE %s" % ue.id)
|
log(f"deleting all validations of UE {ue.id}")
|
||||||
ndb.SimpleQuery(
|
ndb.SimpleQuery(
|
||||||
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
|
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
|
||||||
{"ue_id": ue.id},
|
{"ue_id": ue.id},
|
||||||
@ -215,10 +209,10 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
# utilisé: acceptable de tout invalider):
|
# utilisé: acceptable de tout invalider):
|
||||||
sco_cache.invalidate_formsemestre()
|
sco_cache.invalidate_formsemestre()
|
||||||
# news
|
# news
|
||||||
F = sco_formations.formation_list(args={"formation_id": ue.formation_id})[0]
|
F = sco_formations.formation_list(args={"formation_id": formation_id})[0]
|
||||||
ScolarNews.add(
|
ScolarNews.add(
|
||||||
typ=ScolarNews.NEWS_FORM,
|
typ=ScolarNews.NEWS_FORM,
|
||||||
obj=ue.formation_id,
|
obj=formation_id,
|
||||||
text=f"Modification de la formation {F['acronyme']}",
|
text=f"Modification de la formation {F['acronyme']}",
|
||||||
max_frequency=10 * 60,
|
max_frequency=10 * 60,
|
||||||
)
|
)
|
||||||
@ -228,8 +222,8 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
|||||||
url_for(
|
url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=ue.formation_id,
|
formation_id=formation_id,
|
||||||
semestre_idx=ue.semestre_idx,
|
semestre_idx=semestre_idx,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
@ -538,9 +532,9 @@ def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
|||||||
semestre_idx=ue.semestre_idx,
|
semestre_idx=ue.semestre_idx,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if not can_delete_ue(ue):
|
if not ue.can_be_deleted():
|
||||||
raise ScoNonEmptyFormationObject(
|
raise ScoNonEmptyFormationObject(
|
||||||
"UE",
|
f"UE",
|
||||||
msg=ue.titre,
|
msg=ue.titre,
|
||||||
dest_url=url_for(
|
dest_url=url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
@ -1352,16 +1346,6 @@ def ue_is_locked(ue_id):
|
|||||||
return len(r) > 0
|
return len(r) > 0
|
||||||
|
|
||||||
|
|
||||||
def ue_list_semestre_ids(ue: dict):
|
|
||||||
"""Liste triée des numeros de semestres des modules dans cette UE
|
|
||||||
Il est recommandable que tous les modules d'une UE aient le même indice de semestre.
|
|
||||||
Mais cela n'a pas toujours été le cas dans les programmes pédagogiques officiels,
|
|
||||||
aussi ScoDoc laisse le choix.
|
|
||||||
"""
|
|
||||||
modules = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
|
|
||||||
return sorted(list(set([mod["semestre_id"] for mod in modules])))
|
|
||||||
|
|
||||||
|
|
||||||
UE_PALETTE = [
|
UE_PALETTE = [
|
||||||
"#B80004", # rouge
|
"#B80004", # rouge
|
||||||
"#F97B3D", # Orange Crayola
|
"#F97B3D", # Orange Crayola
|
||||||
|
@ -606,12 +606,10 @@ def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"):
|
|||||||
|
|
||||||
|
|
||||||
# -------------- VIEWS
|
# -------------- VIEWS
|
||||||
def evaluation_describe(evaluation_id="", edit_in_place=True):
|
def evaluation_describe(evaluation_id="", edit_in_place=True, link_saisie=True):
|
||||||
"""HTML description of evaluation, for page headers
|
"""HTML description of evaluation, for page headers
|
||||||
edit_in_place: allow in-place editing when permitted (not implemented)
|
edit_in_place: allow in-place editing when permitted (not implemented)
|
||||||
"""
|
"""
|
||||||
from app.scodoc import sco_saisie_notes
|
|
||||||
|
|
||||||
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||||
moduleimpl_id = E["moduleimpl_id"]
|
moduleimpl_id = E["moduleimpl_id"]
|
||||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
|
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
|
||||||
@ -646,7 +644,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
|
|||||||
if Mod["module_type"] == ModuleType.MALUS:
|
if Mod["module_type"] == ModuleType.MALUS:
|
||||||
etit += ' <span class="eval_malus">(points de malus)</span>'
|
etit += ' <span class="eval_malus">(points de malus)</span>'
|
||||||
H = [
|
H = [
|
||||||
'<span class="eval_title">Evaluation%s</span><p><b>Module : %s</b></p>'
|
'<span class="eval_title">Évaluation%s</span><p><b>Module : %s</b></p>'
|
||||||
% (etit, mod_descr)
|
% (etit, mod_descr)
|
||||||
]
|
]
|
||||||
if Mod["module_type"] == ModuleType.MALUS:
|
if Mod["module_type"] == ModuleType.MALUS:
|
||||||
@ -689,12 +687,16 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
|
|||||||
<a class="stdlink" href="{url_for(
|
<a class="stdlink" href="{url_for(
|
||||||
"notes.evaluation_edit", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id)
|
"notes.evaluation_edit", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id)
|
||||||
}">modifier l'évaluation</a>
|
}">modifier l'évaluation</a>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
if link_saisie:
|
||||||
|
H.append(
|
||||||
|
f"""
|
||||||
<a class="stdlink" href="{url_for(
|
<a class="stdlink" href="{url_for(
|
||||||
"notes.saisie_notes", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id)
|
"notes.saisie_notes", scodoc_dept=g.scodoc_dept, evaluation_id=evaluation_id)
|
||||||
}">saisie des notes</a>
|
}">saisie des notes</a>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
H.append("</p>")
|
H.append("</p>")
|
||||||
|
|
||||||
return '<div class="eval_description">' + "\n".join(H) + "</div>"
|
return '<div class="eval_description">' + "\n".join(H) + "</div>"
|
||||||
|
@ -208,25 +208,29 @@ def _build_results_list(dpv_by_sem, etuds_infos):
|
|||||||
return rows, titles, columns_ids
|
return rows, titles, columns_ids
|
||||||
|
|
||||||
|
|
||||||
def get_set_formsemestre_id_dates(start_date, end_date):
|
def get_set_formsemestre_id_dates(start_date, end_date) -> set:
|
||||||
"""Ensemble des formsemestre_id entre ces dates"""
|
"""Ensemble des formsemestre_id entre ces dates"""
|
||||||
s = ndb.SimpleDictFetch(
|
s = ndb.SimpleDictFetch(
|
||||||
"""SELECT id
|
"""SELECT id
|
||||||
FROM notes_formsemestre
|
FROM notes_formsemestre
|
||||||
WHERE date_debut >= %(start_date)s AND date_fin <= %(end_date)s
|
WHERE date_debut >= %(start_date)s
|
||||||
|
AND date_fin <= %(end_date)s
|
||||||
|
AND dept_id = %(dept_id)s
|
||||||
""",
|
""",
|
||||||
{"start_date": start_date, "end_date": end_date},
|
{"start_date": start_date, "end_date": end_date, "dept_id": g.scodoc_dept_id},
|
||||||
)
|
)
|
||||||
return {x["id"] for x in s}
|
return {x["id"] for x in s}
|
||||||
|
|
||||||
|
|
||||||
def scodoc_table_results(start_date="", end_date="", types_parcours=[], format="html"):
|
def scodoc_table_results(
|
||||||
|
start_date="", end_date="", types_parcours: list = None, format="html"
|
||||||
|
):
|
||||||
"""Page affichant la table des résultats
|
"""Page affichant la table des résultats
|
||||||
Les dates sont en dd/mm/yyyy (datepicker javascript)
|
Les dates sont en dd/mm/yyyy (datepicker javascript)
|
||||||
types_parcours est la liste des types de parcours à afficher
|
types_parcours est la liste des types de parcours à afficher
|
||||||
(liste de chaines, eg ['100', '210'] )
|
(liste de chaines, eg ['100', '210'] )
|
||||||
"""
|
"""
|
||||||
log("scodoc_table_results: start_date=%s" % (start_date,)) # XXX
|
log(f"scodoc_table_results: start_date={start_date!r}")
|
||||||
if not types_parcours:
|
if not types_parcours:
|
||||||
types_parcours = []
|
types_parcours = []
|
||||||
if not isinstance(types_parcours, list):
|
if not isinstance(types_parcours, list):
|
||||||
|
@ -256,6 +256,8 @@ def formation_import_xml(doc: str, import_tags=True):
|
|||||||
mod_info[1]["formation_id"] = formation_id
|
mod_info[1]["formation_id"] = formation_id
|
||||||
mod_info[1]["matiere_id"] = mat_id
|
mod_info[1]["matiere_id"] = mat_id
|
||||||
mod_info[1]["ue_id"] = ue_id
|
mod_info[1]["ue_id"] = ue_id
|
||||||
|
if not "module_type" in mod_info[1]:
|
||||||
|
mod_info[1]["module_type"] = scu.ModuleType.STANDARD
|
||||||
mod_id = sco_edit_module.do_module_create(mod_info[1])
|
mod_id = sco_edit_module.do_module_create(mod_info[1])
|
||||||
if xml_module_id:
|
if xml_module_id:
|
||||||
modules_old2new[int(xml_module_id)] = mod_id
|
modules_old2new[int(xml_module_id)] = mod_id
|
||||||
|
@ -262,7 +262,7 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _make_page(etud, sem, tf, message=""):
|
def _make_page(etud: dict, sem, tf, message="") -> list:
|
||||||
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
|
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
|
||||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
moy_gen = nt.get_etud_moy_gen(etud["etudid"])
|
moy_gen = nt.get_etud_moy_gen(etud["etudid"])
|
||||||
@ -277,21 +277,20 @@ def _make_page(etud, sem, tf, message=""):
|
|||||||
</p>
|
</p>
|
||||||
"""
|
"""
|
||||||
% etud,
|
% etud,
|
||||||
"""<p>La moyenne de ce semestre serait:
|
f"""<p>La moyenne de ce semestre serait:
|
||||||
<span class="ext_sem_moy"><span class="ext_sem_moy_val">%s</span> / 20</span>
|
<span class="ext_sem_moy"><span class="ext_sem_moy_val">{moy_gen}</span> / 20</span>
|
||||||
</p>
|
</p>
|
||||||
"""
|
""",
|
||||||
% moy_gen,
|
|
||||||
'<div id="formsemestre_ext_edit_ue_validations">',
|
'<div id="formsemestre_ext_edit_ue_validations">',
|
||||||
tf[1],
|
tf[1],
|
||||||
"</div>",
|
"</div>",
|
||||||
"""<div>
|
f"""<div>
|
||||||
<a class="stdlink"
|
<a class="stdlink"
|
||||||
href="formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s">
|
href="{url_for("notes.formsemestre_bulletinetud", scodoc_dept=g.scodoc_dept,
|
||||||
retour au bulletin de notes
|
formsemestre_id=formsemestre.id, etudid=etud['etudid']
|
||||||
</a></div>
|
)}">retour au bulletin de notes</a>
|
||||||
"""
|
</div>
|
||||||
% (sem["formsemestre_id"], etud["etudid"]),
|
""",
|
||||||
html_sco_header.sco_footer(),
|
html_sco_header.sco_footer(),
|
||||||
]
|
]
|
||||||
return H
|
return H
|
||||||
|
@ -43,13 +43,14 @@ from xml.etree.ElementTree import Element
|
|||||||
import flask
|
import flask
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask import url_for, make_response
|
from flask import url_for, make_response
|
||||||
|
from sqlalchemy.sql import text
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models import FormSemestre, formsemestre
|
from app.models import FormSemestre, Identite
|
||||||
from app.models import GROUPNAME_STR_LEN, SHORT_STR_LEN
|
from app.models import GROUPNAME_STR_LEN, SHORT_STR_LEN
|
||||||
from app.models.groups import Partition
|
from app.models.groups import GroupDescr, Partition
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
from app import log, cache
|
from app import log, cache
|
||||||
@ -61,7 +62,6 @@ from app.scodoc import sco_etud
|
|||||||
from app.scodoc import sco_permissions_check
|
from app.scodoc import sco_permissions_check
|
||||||
from app.scodoc import sco_xml
|
from app.scodoc import sco_xml
|
||||||
from app.scodoc.sco_exceptions import ScoException, AccessDenied, ScoValueError
|
from app.scodoc.sco_exceptions import ScoException, AccessDenied, ScoValueError
|
||||||
from app.scodoc.sco_permissions import Permission
|
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||||
|
|
||||||
|
|
||||||
@ -413,8 +413,43 @@ def formsemestre_get_etud_groupnames(formsemestre_id, attr="group_name"):
|
|||||||
return R
|
return R
|
||||||
|
|
||||||
|
|
||||||
def etud_add_group_infos(etud, formsemestre_id, sep=" "):
|
def get_etud_formsemestre_groups(
|
||||||
"""Add informations on partitions and group memberships to etud (a dict with an etudid)"""
|
etud: Identite, formsemestre: FormSemestre, only_to_show=True
|
||||||
|
) -> list[GroupDescr]:
|
||||||
|
"""Liste les groupes auxquels est inscrit"""
|
||||||
|
# Note: je n'ai pas réussi à cosntruire une requete SQLAlechemy avec
|
||||||
|
# la Table d'association group_membership
|
||||||
|
cursor = db.session.execute(
|
||||||
|
text(
|
||||||
|
"""
|
||||||
|
SELECT g.id
|
||||||
|
FROM group_descr g, group_membership gm, partition p
|
||||||
|
WHERE gm.etudid = :etudid
|
||||||
|
AND gm.group_id = g.id
|
||||||
|
AND g.partition_id = p.id
|
||||||
|
AND p.formsemestre_id = :formsemestre_id
|
||||||
|
AND p.partition_name is not NULL
|
||||||
|
"""
|
||||||
|
+ (" and (p.show_in_lists is True) " if only_to_show else "")
|
||||||
|
+ """
|
||||||
|
ORDER BY p.numero
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
{"etudid": etud.id, "formsemestre_id": formsemestre.id},
|
||||||
|
)
|
||||||
|
return [GroupDescr.query.get(group_id) for group_id in cursor]
|
||||||
|
|
||||||
|
|
||||||
|
# Ancienne fonction:
|
||||||
|
def etud_add_group_infos(etud, formsemestre_id, sep=" ", only_to_show=False):
|
||||||
|
"""Add informations on partitions and group memberships to etud
|
||||||
|
(a dict with an etudid)
|
||||||
|
If only_to_show, restrict to partions such that show_in_lists is True.
|
||||||
|
|
||||||
|
etud['partitions'] = { partition_id : group + partition_name }
|
||||||
|
etud['groupes'] = "TDB, Gr2, TPB1"
|
||||||
|
etud['partitionsgroupes'] = "Groupes TD:TDB, Groupes TP:Gr2 (...)"
|
||||||
|
"""
|
||||||
etud[
|
etud[
|
||||||
"partitions"
|
"partitions"
|
||||||
] = collections.OrderedDict() # partition_id : group + partition_name
|
] = collections.OrderedDict() # partition_id : group + partition_name
|
||||||
@ -423,11 +458,14 @@ def etud_add_group_infos(etud, formsemestre_id, sep=" "):
|
|||||||
return etud
|
return etud
|
||||||
|
|
||||||
infos = ndb.SimpleDictFetch(
|
infos = ndb.SimpleDictFetch(
|
||||||
"""SELECT p.partition_name, g.*, g.id AS group_id
|
"""SELECT p.partition_name, p.show_in_lists, g.*, g.id AS group_id
|
||||||
FROM group_descr g, partition p, group_membership gm WHERE gm.etudid=%(etudid)s
|
FROM group_descr g, partition p, group_membership gm WHERE gm.etudid=%(etudid)s
|
||||||
and gm.group_id = g.id
|
and gm.group_id = g.id
|
||||||
and g.partition_id = p.id
|
and g.partition_id = p.id
|
||||||
and p.formsemestre_id = %(formsemestre_id)s
|
and p.formsemestre_id = %(formsemestre_id)s
|
||||||
|
"""
|
||||||
|
+ (" and (p.show_in_lists is True) " if only_to_show else "")
|
||||||
|
+ """
|
||||||
ORDER BY p.numero
|
ORDER BY p.numero
|
||||||
""",
|
""",
|
||||||
{"etudid": etud["etudid"], "formsemestre_id": formsemestre_id},
|
{"etudid": etud["etudid"], "formsemestre_id": formsemestre_id},
|
||||||
@ -443,7 +481,7 @@ def etud_add_group_infos(etud, formsemestre_id, sep=" "):
|
|||||||
)
|
)
|
||||||
etud["partitionsgroupes"] = sep.join(
|
etud["partitionsgroupes"] = sep.join(
|
||||||
[
|
[
|
||||||
gr["partition_name"] + ":" + gr["group_name"]
|
(gr["partition_name"] or "") + ":" + gr["group_name"]
|
||||||
for gr in infos
|
for gr in infos
|
||||||
if gr["group_name"] is not None
|
if gr["group_name"] is not None
|
||||||
]
|
]
|
||||||
@ -1008,9 +1046,7 @@ def partition_set_attr(partition_id, attr, value):
|
|||||||
partition[attr] = value
|
partition[attr] = value
|
||||||
partitionEditor.edit(cnx, partition)
|
partitionEditor.edit(cnx, partition)
|
||||||
# invalid bulletin cache
|
# invalid bulletin cache
|
||||||
sco_cache.invalidate_formsemestre(
|
sco_cache.invalidate_formsemestre(formsemestre_id=partition["formsemestre_id"])
|
||||||
pdfonly=True, formsemestre_id=partition["formsemestre_id"]
|
|
||||||
)
|
|
||||||
return "enregistré"
|
return "enregistré"
|
||||||
|
|
||||||
|
|
||||||
|
@ -277,7 +277,11 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
|||||||
if modimpl.module.is_apc():
|
if modimpl.module.is_apc():
|
||||||
H.append(_ue_coefs_html(modimpl.module.ue_coefs_list()))
|
H.append(_ue_coefs_html(modimpl.module.ue_coefs_list()))
|
||||||
else:
|
else:
|
||||||
H.append(f"Coef. dans le semestre: {modimpl.module.coefficient}")
|
H.append(
|
||||||
|
f"""Coef. dans le semestre: {
|
||||||
|
"non défini" if modimpl.module.coefficient is None else modimpl.module.coefficient
|
||||||
|
}"""
|
||||||
|
)
|
||||||
H.append("""</td><td></td></tr>""")
|
H.append("""</td><td></td></tr>""")
|
||||||
# 3ieme ligne: Formation
|
# 3ieme ligne: Formation
|
||||||
H.append(
|
H.append(
|
||||||
|
@ -153,14 +153,14 @@ def ficheEtud(etudid=None):
|
|||||||
try: # pour les bookmarks avec d'anciens ids...
|
try: # pour les bookmarks avec d'anciens ids...
|
||||||
etudid = int(etudid)
|
etudid = int(etudid)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ScoValueError("id invalide !")
|
raise ScoValueError("id invalide !") from ValueError
|
||||||
# la sidebar est differente s'il y a ou pas un etudid
|
# la sidebar est differente s'il y a ou pas un etudid
|
||||||
# voir html_sidebar.sidebar()
|
# voir html_sidebar.sidebar()
|
||||||
g.etudid = etudid
|
g.etudid = etudid
|
||||||
args = make_etud_args(etudid=etudid)
|
args = make_etud_args(etudid=etudid)
|
||||||
etuds = sco_etud.etudident_list(cnx, args)
|
etuds = sco_etud.etudident_list(cnx, args)
|
||||||
if not etuds:
|
if not etuds:
|
||||||
log("ficheEtud: etudid=%s request.args=%s" % (etudid, request.args))
|
log(f"ficheEtud: etudid={etudid!r} request.args={request.args!r}")
|
||||||
raise ScoValueError("Etudiant inexistant !")
|
raise ScoValueError("Etudiant inexistant !")
|
||||||
etud = etuds[0]
|
etud = etuds[0]
|
||||||
etudid = etud["etudid"]
|
etudid = etud["etudid"]
|
||||||
@ -173,7 +173,7 @@ def ficheEtud(etudid=None):
|
|||||||
if info["lieu_naissance"]:
|
if info["lieu_naissance"]:
|
||||||
info["info_naissance"] += " à " + info["lieu_naissance"]
|
info["info_naissance"] += " à " + info["lieu_naissance"]
|
||||||
if info["dept_naissance"]:
|
if info["dept_naissance"]:
|
||||||
info["info_naissance"] += " (%s)" % info["dept_naissance"]
|
info["info_naissance"] += f" ({info['dept_naissance']})"
|
||||||
info["etudfoto"] = sco_photos.etud_photo_html(etud)
|
info["etudfoto"] = sco_photos.etud_photo_html(etud)
|
||||||
if (
|
if (
|
||||||
(not info["domicile"])
|
(not info["domicile"])
|
||||||
@ -205,7 +205,7 @@ def ficheEtud(etudid=None):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
info["emaillink"] = "<em>(pas d'adresse e-mail)</em>"
|
info["emaillink"] = "<em>(pas d'adresse e-mail)</em>"
|
||||||
# champs dependant des permissions
|
# Champ dépendant des permissions:
|
||||||
if authuser.has_permission(Permission.ScoEtudChangeAdr):
|
if authuser.has_permission(Permission.ScoEtudChangeAdr):
|
||||||
info["modifadresse"] = (
|
info["modifadresse"] = (
|
||||||
'<a class="stdlink" href="formChangeCoordonnees?etudid=%s">modifier adresse</a>'
|
'<a class="stdlink" href="formChangeCoordonnees?etudid=%s">modifier adresse</a>'
|
||||||
@ -216,9 +216,10 @@ def ficheEtud(etudid=None):
|
|||||||
|
|
||||||
# Groupes:
|
# Groupes:
|
||||||
sco_groups.etud_add_group_infos(
|
sco_groups.etud_add_group_infos(
|
||||||
info, info["cursem"]["formsemestre_id"] if info["cursem"] else None
|
info,
|
||||||
|
info["cursem"]["formsemestre_id"] if info["cursem"] else None,
|
||||||
|
only_to_show=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Parcours de l'étudiant
|
# Parcours de l'étudiant
|
||||||
if info["sems"]:
|
if info["sems"]:
|
||||||
info["last_formsemestre_id"] = info["sems"][0]["formsemestre_id"]
|
info["last_formsemestre_id"] = info["sems"][0]["formsemestre_id"]
|
||||||
@ -235,15 +236,28 @@ def ficheEtud(etudid=None):
|
|||||||
)
|
)
|
||||||
grlink = '<span class="fontred">%s</span>' % descr["situation"]
|
grlink = '<span class="fontred">%s</span>' % descr["situation"]
|
||||||
else:
|
else:
|
||||||
group = sco_groups.get_etud_main_group(etudid, sem["formsemestre_id"])
|
e = {"etudid": etudid}
|
||||||
if group["partition_name"]:
|
sco_groups.etud_add_group_infos(
|
||||||
gr_name = group["group_name"]
|
e,
|
||||||
else:
|
sem["formsemestre_id"],
|
||||||
gr_name = "tous"
|
only_to_show=True,
|
||||||
grlink = (
|
|
||||||
'<a class="discretelink" href="groups_view?group_ids=%s" title="Liste du groupe">groupe %s</a>'
|
|
||||||
% (group["group_id"], gr_name)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
grlinks = []
|
||||||
|
for partition in e["partitions"].values():
|
||||||
|
if partition["partition_name"]:
|
||||||
|
gr_name = partition["group_name"]
|
||||||
|
else:
|
||||||
|
gr_name = "tous"
|
||||||
|
|
||||||
|
grlinks.append(
|
||||||
|
f"""<a class="discretelink" href="{
|
||||||
|
url_for('scolar.groups_view',
|
||||||
|
scodoc_dept=g.scodoc_dept, group_ids=partition['group_id'])
|
||||||
|
}" title="Liste du groupe {gr_name}">{gr_name}</a>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
grlink = ", ".join(grlinks)
|
||||||
# infos ajoutées au semestre dans le parcours (groupe, menu)
|
# infos ajoutées au semestre dans le parcours (groupe, menu)
|
||||||
menu = _menuScolarite(authuser, sem, etudid)
|
menu = _menuScolarite(authuser, sem, etudid)
|
||||||
if menu:
|
if menu:
|
||||||
@ -296,17 +310,18 @@ def ficheEtud(etudid=None):
|
|||||||
if not sco_permissions_check.can_suppress_annotation(a["id"]):
|
if not sco_permissions_check.can_suppress_annotation(a["id"]):
|
||||||
a["dellink"] = ""
|
a["dellink"] = ""
|
||||||
else:
|
else:
|
||||||
a[
|
a["dellink"] = (
|
||||||
"dellink"
|
'<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&annotation_id=%s">%s</a></td>'
|
||||||
] = '<td class="annodel"><a href="doSuppressAnnotation?etudid=%s&annotation_id=%s">%s</a></td>' % (
|
% (
|
||||||
etudid,
|
etudid,
|
||||||
a["id"],
|
a["id"],
|
||||||
scu.icontag(
|
scu.icontag(
|
||||||
"delete_img",
|
"delete_img",
|
||||||
border="0",
|
border="0",
|
||||||
alt="suppress",
|
alt="suppress",
|
||||||
title="Supprimer cette annotation",
|
title="Supprimer cette annotation",
|
||||||
),
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
author = sco_users.user_info(a["author"])
|
author = sco_users.user_info(a["author"])
|
||||||
alist.append(
|
alist.append(
|
||||||
@ -422,9 +437,11 @@ def ficheEtud(etudid=None):
|
|||||||
|
|
||||||
#
|
#
|
||||||
if info["groupes"].strip():
|
if info["groupes"].strip():
|
||||||
info["groupes_row"] = (
|
info[
|
||||||
'<tr><td class="fichetitre2">Groupe :</td><td>%(groupes)s</td></tr>' % info
|
"groupes_row"
|
||||||
)
|
] = f"""<tr>
|
||||||
|
<td class="fichetitre2">Groupes :</td><td>{info['groupes']}</td>
|
||||||
|
</tr>"""
|
||||||
else:
|
else:
|
||||||
info["groupes_row"] = ""
|
info["groupes_row"] = ""
|
||||||
info["menus_etud"] = menus_etud(etudid)
|
info["menus_etud"] = menus_etud(etudid)
|
||||||
|
@ -50,10 +50,10 @@ _SCO_PERMISSIONS = (
|
|||||||
(1 << 27, "RelationsEntreprisesCorrespondants", "Voir les correspondants"),
|
(1 << 27, "RelationsEntreprisesCorrespondants", "Voir les correspondants"),
|
||||||
# 27 à 39 ... réservé pour "entreprises"
|
# 27 à 39 ... réservé pour "entreprises"
|
||||||
# Api scodoc9
|
# Api scodoc9
|
||||||
(1 << 40, "APIView", "Voir"),
|
(1 << 40, "APIView", "API: Lecture"),
|
||||||
(1 << 41, "APIEtudChangeGroups", "Modifier les groupes"),
|
(1 << 41, "APIEtudChangeGroups", "API: Modifier les groupes"),
|
||||||
(1 << 42, "APIEditAllNotes", "Modifier toutes les notes"),
|
(1 << 42, "APIEditAllNotes", "API: Modifier toutes les notes"),
|
||||||
(1 << 43, "APIAbsChange", "Saisir des absences"),
|
(1 << 43, "APIAbsChange", "API: Saisir des absences"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +70,8 @@ class Permission(object):
|
|||||||
setattr(Permission, symbol, perm)
|
setattr(Permission, symbol, perm)
|
||||||
Permission.description[symbol] = description
|
Permission.description[symbol] = description
|
||||||
Permission.permission_by_name[symbol] = perm
|
Permission.permission_by_name[symbol] = perm
|
||||||
Permission.NBITS = len(_SCO_PERMISSIONS)
|
max_perm = max(p[0] for p in _SCO_PERMISSIONS)
|
||||||
|
Permission.NBITS = max_perm.bit_length()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_name(permission_name: str) -> int:
|
def get_by_name(permission_name: str) -> int:
|
||||||
|
@ -132,9 +132,12 @@ def clear_base_preferences():
|
|||||||
g._SCO_BASE_PREFERENCES = {} # { dept_id: BasePreferences instance }
|
g._SCO_BASE_PREFERENCES = {} # { dept_id: BasePreferences instance }
|
||||||
|
|
||||||
|
|
||||||
def get_base_preferences():
|
def get_base_preferences(dept_id: int = None):
|
||||||
"""Return global preferences for the current department"""
|
"""Return global preferences for the specified department
|
||||||
dept_id = g.scodoc_dept_id
|
or the current departement
|
||||||
|
"""
|
||||||
|
if dept_id is None:
|
||||||
|
dept_id = g.scodoc_dept_id
|
||||||
if not hasattr(g, "_SCO_BASE_PREFERENCES"):
|
if not hasattr(g, "_SCO_BASE_PREFERENCES"):
|
||||||
g._SCO_BASE_PREFERENCES = {}
|
g._SCO_BASE_PREFERENCES = {}
|
||||||
if not dept_id in g._SCO_BASE_PREFERENCES:
|
if not dept_id in g._SCO_BASE_PREFERENCES:
|
||||||
@ -142,12 +145,12 @@ def get_base_preferences():
|
|||||||
return g._SCO_BASE_PREFERENCES[dept_id]
|
return g._SCO_BASE_PREFERENCES[dept_id]
|
||||||
|
|
||||||
|
|
||||||
def get_preference(name, formsemestre_id=None):
|
def get_preference(name, formsemestre_id=None, dept_id=None):
|
||||||
"""Returns value of named preference.
|
"""Returns value of named preference.
|
||||||
All preferences have a sensible default value, so this
|
All preferences have a sensible default value, so this
|
||||||
function always returns a usable value for all defined preferences names.
|
function always returns a usable value for all defined preferences names.
|
||||||
"""
|
"""
|
||||||
return get_base_preferences().get(formsemestre_id, name)
|
return get_base_preferences(dept_id=dept_id).get(formsemestre_id, name)
|
||||||
|
|
||||||
|
|
||||||
def _convert_pref_type(p, pref_spec):
|
def _convert_pref_type(p, pref_spec):
|
||||||
@ -2145,9 +2148,9 @@ class BasePreferences(object):
|
|||||||
class SemPreferences:
|
class SemPreferences:
|
||||||
"""Preferences for a formsemestre"""
|
"""Preferences for a formsemestre"""
|
||||||
|
|
||||||
def __init__(self, formsemestre_id=None):
|
def __init__(self, formsemestre_id=None, dept_id=None):
|
||||||
self.formsemestre_id = formsemestre_id
|
self.formsemestre_id = formsemestre_id
|
||||||
self.base_prefs = get_base_preferences()
|
self.base_prefs = get_base_preferences(dept_id=dept_id)
|
||||||
|
|
||||||
def __getitem__(self, name):
|
def __getitem__(self, name):
|
||||||
return self.base_prefs.get(self.formsemestre_id, name)
|
return self.base_prefs.get(self.formsemestre_id, name)
|
||||||
|
@ -32,7 +32,7 @@ import time
|
|||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask import url_for
|
from flask import abort, url_for
|
||||||
|
|
||||||
from app import log
|
from app import log
|
||||||
from app.but import bulletin_but
|
from app.but import bulletin_but
|
||||||
@ -83,13 +83,18 @@ def formsemestre_recapcomplet(
|
|||||||
force_publishing: publie les xml et json même si bulletins non publiés
|
force_publishing: publie les xml et json même si bulletins non publiés
|
||||||
selected_etudid: etudid sélectionné (pour scroller au bon endroit)
|
selected_etudid: etudid sélectionné (pour scroller au bon endroit)
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(formsemestre_id, int):
|
||||||
|
abort(404)
|
||||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
|
file_formats = {"csv", "json", "xls", "xlsx", "xlsall", "xml"}
|
||||||
|
supported_formats = file_formats | {"html", "evals"}
|
||||||
|
if tabformat not in supported_formats:
|
||||||
|
raise ScoValueError(f"Format non supporté: {tabformat}")
|
||||||
|
is_file = tabformat in file_formats
|
||||||
modejury = int(modejury)
|
modejury = int(modejury)
|
||||||
|
|
||||||
xml_with_decisions = int(xml_with_decisions)
|
xml_with_decisions = int(xml_with_decisions)
|
||||||
force_publishing = int(force_publishing)
|
force_publishing = int(force_publishing)
|
||||||
is_file = tabformat in {"csv", "json", "xls", "xlsx", "xlsall", "xml"}
|
|
||||||
data = _do_formsemestre_recapcomplet(
|
data = _do_formsemestre_recapcomplet(
|
||||||
formsemestre_id,
|
formsemestre_id,
|
||||||
format=tabformat,
|
format=tabformat,
|
||||||
@ -128,6 +133,8 @@ def formsemestre_recapcomplet(
|
|||||||
for (format, label) in (
|
for (format, label) in (
|
||||||
("html", "Tableau"),
|
("html", "Tableau"),
|
||||||
("evals", "Avec toutes les évaluations"),
|
("evals", "Avec toutes les évaluations"),
|
||||||
|
("xlsx", "Excel (non formaté)"),
|
||||||
|
("xlsall", "Excel avec évaluations"),
|
||||||
("xml", "Bulletins XML (obsolète)"),
|
("xml", "Bulletins XML (obsolète)"),
|
||||||
("json", "Bulletins JSON"),
|
("json", "Bulletins JSON"),
|
||||||
):
|
):
|
||||||
|
@ -799,22 +799,22 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
|||||||
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
|
evals = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||||
if not evals:
|
if not evals:
|
||||||
raise ScoValueError("invalid evaluation_id")
|
raise ScoValueError("invalid evaluation_id")
|
||||||
E = evals[0]
|
eval_dict = evals[0]
|
||||||
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=eval_dict["moduleimpl_id"])[0]
|
||||||
formsemestre_id = M["formsemestre_id"]
|
formsemestre_id = M["formsemestre_id"]
|
||||||
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
|
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]
|
||||||
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
|
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
|
||||||
mod_responsable = sco_users.user_info(M["responsable_id"])
|
mod_responsable = sco_users.user_info(M["responsable_id"])
|
||||||
if E["jour"]:
|
if eval_dict["jour"]:
|
||||||
indication_date = ndb.DateDMYtoISO(E["jour"])
|
indication_date = ndb.DateDMYtoISO(eval_dict["jour"])
|
||||||
else:
|
else:
|
||||||
indication_date = scu.sanitize_filename(E["description"])[:12]
|
indication_date = scu.sanitize_filename(eval_dict["description"])[:12]
|
||||||
evalname = "%s-%s" % (Mod["code"], indication_date)
|
eval_name = "%s-%s" % (Mod["code"], indication_date)
|
||||||
|
|
||||||
if E["description"]:
|
if eval_dict["description"]:
|
||||||
evaltitre = "%s du %s" % (E["description"], E["jour"])
|
evaltitre = "%s du %s" % (eval_dict["description"], eval_dict["jour"])
|
||||||
else:
|
else:
|
||||||
evaltitre = "évaluation du %s" % E["jour"]
|
evaltitre = "évaluation du %s" % eval_dict["jour"]
|
||||||
description = "%s en %s (%s) resp. %s" % (
|
description = "%s en %s (%s) resp. %s" % (
|
||||||
evaltitre,
|
evaltitre,
|
||||||
Mod["abbrev"] or "",
|
Mod["abbrev"] or "",
|
||||||
@ -847,7 +847,7 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
|||||||
# une liste de liste de chaines: lignes de la feuille de calcul
|
# une liste de liste de chaines: lignes de la feuille de calcul
|
||||||
L = []
|
L = []
|
||||||
|
|
||||||
etuds = _get_sorted_etuds(E, etudids, formsemestre_id)
|
etuds = _get_sorted_etuds(eval_dict, etudids, formsemestre_id)
|
||||||
for e in etuds:
|
for e in etuds:
|
||||||
etudid = e["etudid"]
|
etudid = e["etudid"]
|
||||||
groups = sco_groups.get_etud_groups(etudid, formsemestre_id)
|
groups = sco_groups.get_etud_groups(etudid, formsemestre_id)
|
||||||
@ -865,8 +865,10 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
filename = "notes_%s_%s" % (evalname, gr_title_filename)
|
filename = "notes_%s_%s" % (eval_name, gr_title_filename)
|
||||||
xls = sco_excel.excel_feuille_saisie(E, sem["titreannee"], description, lines=L)
|
xls = sco_excel.excel_feuille_saisie(
|
||||||
|
eval_dict, sem["titreannee"], description, lines=L
|
||||||
|
)
|
||||||
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
|
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
|
||||||
# return sco_excel.send_excel_file(xls, filename)
|
# return sco_excel.send_excel_file(xls, filename)
|
||||||
|
|
||||||
@ -941,7 +943,9 @@ def saisie_notes(evaluation_id, group_ids=[]):
|
|||||||
cssstyles=sco_groups_view.CSSSTYLES,
|
cssstyles=sco_groups_view.CSSSTYLES,
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
),
|
),
|
||||||
sco_evaluations.evaluation_describe(evaluation_id=evaluation_id),
|
sco_evaluations.evaluation_describe(
|
||||||
|
evaluation_id=evaluation_id, link_saisie=False
|
||||||
|
),
|
||||||
'<div id="saisie_notes"><span class="eval_title">Saisie des notes</span>',
|
'<div id="saisie_notes"><span class="eval_title">Saisie des notes</span>',
|
||||||
]
|
]
|
||||||
H.append("""<div id="group-tabs"><table><tr><td>""")
|
H.append("""<div id="group-tabs"><table><tr><td>""")
|
||||||
@ -1008,10 +1012,9 @@ def saisie_notes(evaluation_id, group_ids=[]):
|
|||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
def _get_sorted_etuds(E, etudids, formsemestre_id):
|
def _get_sorted_etuds(eval_dict: dict, etudids: list, formsemestre_id: int):
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
|
||||||
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(
|
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(
|
||||||
E["evaluation_id"]
|
eval_dict["evaluation_id"]
|
||||||
) # Notes existantes
|
) # Notes existantes
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
etuds = []
|
etuds = []
|
||||||
@ -1028,9 +1031,9 @@ def _get_sorted_etuds(E, etudids, formsemestre_id):
|
|||||||
e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id)
|
e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id)
|
||||||
|
|
||||||
# Information sur absence (tenant compte de la demi-journée)
|
# Information sur absence (tenant compte de la demi-journée)
|
||||||
jour_iso = ndb.DateDMYtoISO(E["jour"])
|
jour_iso = ndb.DateDMYtoISO(eval_dict["jour"])
|
||||||
warn_abs_lst = []
|
warn_abs_lst = []
|
||||||
if E["matin"]:
|
if eval_dict["matin"]:
|
||||||
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=1)
|
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=1)
|
||||||
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=1)
|
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=1)
|
||||||
if nbabs:
|
if nbabs:
|
||||||
@ -1038,7 +1041,7 @@ def _get_sorted_etuds(E, etudids, formsemestre_id):
|
|||||||
warn_abs_lst.append("absent justifié le matin !")
|
warn_abs_lst.append("absent justifié le matin !")
|
||||||
else:
|
else:
|
||||||
warn_abs_lst.append("absent le matin !")
|
warn_abs_lst.append("absent le matin !")
|
||||||
if E["apresmidi"]:
|
if eval_dict["apresmidi"]:
|
||||||
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=0)
|
nbabs = sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=0)
|
||||||
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=0)
|
nbabsjust = sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=0)
|
||||||
if nbabs:
|
if nbabs:
|
||||||
|
@ -252,7 +252,7 @@ def formsemestre_synchro_etuds(
|
|||||||
etudids_a_desinscrire = [nip2etudid(x) for x in a_desinscrire]
|
etudids_a_desinscrire = [nip2etudid(x) for x in a_desinscrire]
|
||||||
etudids_a_desinscrire += a_desinscrire_without_key
|
etudids_a_desinscrire += a_desinscrire_without_key
|
||||||
#
|
#
|
||||||
with sco_cache.DefferedSemCacheManager():
|
with sco_cache.DeferredSemCacheManager():
|
||||||
do_import_etuds_from_portal(sem, a_importer, etudsapo_ident)
|
do_import_etuds_from_portal(sem, a_importer, etudsapo_ident)
|
||||||
sco_inscr_passage.do_inscrit(sem, etudids_a_inscrire)
|
sco_inscr_passage.do_inscrit(sem, etudids_a_inscrire)
|
||||||
sco_inscr_passage.do_desinscrit(sem, etudids_a_desinscrire)
|
sco_inscr_passage.do_desinscrit(sem, etudids_a_desinscrire)
|
||||||
|
@ -56,6 +56,7 @@ Solution proposée (nov 2014):
|
|||||||
import flask
|
import flask
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
from app.models.formsemestre import FormSemestre
|
||||||
|
|
||||||
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
|
||||||
@ -65,7 +66,6 @@ from app.scodoc import sco_codes_parcours
|
|||||||
from app.scodoc import sco_edit_matiere
|
from app.scodoc import sco_edit_matiere
|
||||||
from app.scodoc import sco_edit_module
|
from app.scodoc import sco_edit_module
|
||||||
from app.scodoc import sco_edit_ue
|
from app.scodoc import sco_edit_ue
|
||||||
from app.scodoc import sco_evaluations
|
|
||||||
from app.scodoc import sco_evaluation_db
|
from app.scodoc import sco_evaluation_db
|
||||||
from app.scodoc import sco_formations
|
from app.scodoc import sco_formations
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_formsemestre
|
||||||
@ -85,17 +85,21 @@ def external_ue_create(
|
|||||||
ects=0.0,
|
ects=0.0,
|
||||||
):
|
):
|
||||||
"""Crée UE/matiere/module/evaluation puis saisie les notes"""
|
"""Crée UE/matiere/module/evaluation puis saisie les notes"""
|
||||||
log("external_ue_create( formsemestre_id=%s, titre=%s )" % (formsemestre_id, titre))
|
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
log(f"creating external UE in {formsemestre}: {acronyme}")
|
||||||
|
|
||||||
# Contrôle d'accès:
|
# Contrôle d'accès:
|
||||||
if not current_user.has_permission(Permission.ScoImplement):
|
if not current_user.has_permission(Permission.ScoImplement):
|
||||||
if not sem["resp_can_edit"] or (current_user.id not in sem["responsables"]):
|
if (not formsemestre.resp_can_edit) or (
|
||||||
|
current_user.id not in [u.id for u in formsemestre.responsables]
|
||||||
|
):
|
||||||
raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
|
raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
|
||||||
#
|
#
|
||||||
formation_id = sem["formation_id"]
|
formation_id = formsemestre.formation.id
|
||||||
log("creating external UE in %s: %s" % (formsemestre_id, acronyme))
|
|
||||||
|
|
||||||
numero = sco_edit_ue.next_ue_numero(formation_id, semestre_id=sem["semestre_id"])
|
numero = sco_edit_ue.next_ue_numero(
|
||||||
|
formation_id, semestre_id=formsemestre.semestre_id
|
||||||
|
)
|
||||||
ue_id = sco_edit_ue.do_ue_create(
|
ue_id = sco_edit_ue.do_ue_create(
|
||||||
{
|
{
|
||||||
"formation_id": formation_id,
|
"formation_id": formation_id,
|
||||||
@ -120,7 +124,8 @@ def external_ue_create(
|
|||||||
"ue_id": ue_id,
|
"ue_id": ue_id,
|
||||||
"matiere_id": matiere_id,
|
"matiere_id": matiere_id,
|
||||||
"formation_id": formation_id,
|
"formation_id": formation_id,
|
||||||
"semestre_id": sem["semestre_id"],
|
"semestre_id": formsemestre.semestre_id,
|
||||||
|
"module_type": scu.ModuleType.STANDARD,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -129,17 +134,23 @@ def external_ue_create(
|
|||||||
"module_id": module_id,
|
"module_id": module_id,
|
||||||
"formsemestre_id": formsemestre_id,
|
"formsemestre_id": formsemestre_id,
|
||||||
# affecte le 1er responsable du semestre comme resp. du module
|
# affecte le 1er responsable du semestre comme resp. du module
|
||||||
"responsable_id": sem["responsables"][0],
|
"responsable_id": formsemestre.responsables[0].id
|
||||||
|
if len(formsemestre.responsables)
|
||||||
|
else None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return moduleimpl_id
|
return moduleimpl_id
|
||||||
|
|
||||||
|
|
||||||
def external_ue_inscrit_et_note(moduleimpl_id, formsemestre_id, notes_etuds):
|
def external_ue_inscrit_et_note(
|
||||||
|
moduleimpl_id: int, formsemestre_id: int, notes_etuds: dict
|
||||||
|
):
|
||||||
|
"""Inscrit les étudiants au moduleimpl, crée au besoin une évaluation
|
||||||
|
et enregistre les notes.
|
||||||
|
"""
|
||||||
log(
|
log(
|
||||||
"external_ue_inscrit_et_note(moduleimpl_id=%s, notes_etuds=%s)"
|
f"external_ue_inscrit_et_note(moduleimpl_id={moduleimpl_id}, notes_etuds={notes_etuds})"
|
||||||
% (moduleimpl_id, notes_etuds)
|
|
||||||
)
|
)
|
||||||
# Inscription des étudiants
|
# Inscription des étudiants
|
||||||
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
||||||
@ -175,17 +186,17 @@ def external_ue_inscrit_et_note(moduleimpl_id, formsemestre_id, notes_etuds):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_existing_external_ue(formation_id):
|
def get_existing_external_ue(formation_id: int) -> list[dict]:
|
||||||
"la liste de toutes les UE externes définies dans cette formation"
|
"Liste de toutes les UE externes définies dans cette formation"
|
||||||
return sco_edit_ue.ue_list(args={"formation_id": formation_id, "is_external": True})
|
return sco_edit_ue.ue_list(args={"formation_id": formation_id, "is_external": True})
|
||||||
|
|
||||||
|
|
||||||
def get_external_moduleimpl_id(formsemestre_id, ue_id):
|
def get_external_moduleimpl_id(formsemestre_id: int, ue_id: int) -> int:
|
||||||
"moduleimpl correspondant à l'UE externe indiquée de ce formsemestre"
|
"moduleimpl correspondant à l'UE externe indiquée de ce formsemestre"
|
||||||
r = ndb.SimpleDictFetch(
|
r = ndb.SimpleDictFetch(
|
||||||
"""
|
"""
|
||||||
SELECT mi.id AS moduleimpl_id FROM notes_moduleimpl mi, notes_modules mo
|
SELECT mi.id AS moduleimpl_id FROM notes_moduleimpl mi, notes_modules mo
|
||||||
WHERE mi.id = %(formsemestre_id)s
|
WHERE mi.formsemestre_id = %(formsemestre_id)s
|
||||||
AND mi.module_id = mo.id
|
AND mi.module_id = mo.id
|
||||||
AND mo.ue_id = %(ue_id)s
|
AND mo.ue_id = %(ue_id)s
|
||||||
""",
|
""",
|
||||||
@ -194,11 +205,14 @@ def get_external_moduleimpl_id(formsemestre_id, ue_id):
|
|||||||
if r:
|
if r:
|
||||||
return r[0]["moduleimpl_id"]
|
return r[0]["moduleimpl_id"]
|
||||||
else:
|
else:
|
||||||
raise ScoValueError("aucun module externe ne correspond")
|
raise ScoValueError(
|
||||||
|
f"""Aucun module externe ne correspond
|
||||||
|
(formsemestre_id={formsemestre_id}, ue_id={ue_id})"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Web function
|
# Web function
|
||||||
def external_ue_create_form(formsemestre_id, etudid):
|
def external_ue_create_form(formsemestre_id: int, etudid: int):
|
||||||
"""Formulaire création UE externe + inscription étudiant et saisie note
|
"""Formulaire création UE externe + inscription étudiant et saisie note
|
||||||
- Demande UE: peut-être existante (liste les UE externes de cette formation),
|
- Demande UE: peut-être existante (liste les UE externes de cette formation),
|
||||||
ou sinon spécifier titre, acronyme, type, ECTS
|
ou sinon spécifier titre, acronyme, type, ECTS
|
||||||
@ -233,7 +247,9 @@ def external_ue_create_form(formsemestre_id, etudid):
|
|||||||
html_footer = html_sco_header.sco_footer()
|
html_footer = html_sco_header.sco_footer()
|
||||||
Fo = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
|
Fo = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
|
||||||
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
|
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
|
||||||
ue_types = parcours.ALLOWED_UE_TYPES
|
ue_types = [
|
||||||
|
typ for typ in parcours.ALLOWED_UE_TYPES if typ != sco_codes_parcours.UE_SPORT
|
||||||
|
]
|
||||||
ue_types.sort()
|
ue_types.sort()
|
||||||
ue_types_names = [sco_codes_parcours.UE_TYPE_NAME[k] for k in ue_types]
|
ue_types_names = [sco_codes_parcours.UE_TYPE_NAME[k] for k in ue_types]
|
||||||
ue_types = [str(x) for x in ue_types]
|
ue_types = [str(x) for x in ue_types]
|
||||||
@ -255,7 +271,7 @@ def external_ue_create_form(formsemestre_id, etudid):
|
|||||||
"input_type": "menu",
|
"input_type": "menu",
|
||||||
"title": "UE externe existante:",
|
"title": "UE externe existante:",
|
||||||
"allowed_values": [""]
|
"allowed_values": [""]
|
||||||
+ [ue["ue_id"] for ue in existing_external_ue],
|
+ [str(ue["ue_id"]) for ue in existing_external_ue],
|
||||||
"labels": [default_label]
|
"labels": [default_label]
|
||||||
+ [
|
+ [
|
||||||
"%s (%s)" % (ue["titre"], ue["acronyme"])
|
"%s (%s)" % (ue["titre"], ue["acronyme"])
|
||||||
@ -337,7 +353,7 @@ def external_ue_create_form(formsemestre_id, etudid):
|
|||||||
+ html_footer
|
+ html_footer
|
||||||
)
|
)
|
||||||
if tf[2]["existing_ue"]:
|
if tf[2]["existing_ue"]:
|
||||||
ue_id = tf[2]["existing_ue"]
|
ue_id = int(tf[2]["existing_ue"])
|
||||||
moduleimpl_id = get_external_moduleimpl_id(formsemestre_id, ue_id)
|
moduleimpl_id = get_external_moduleimpl_id(formsemestre_id, ue_id)
|
||||||
else:
|
else:
|
||||||
acronyme = tf[2]["acronyme"].strip()
|
acronyme = tf[2]["acronyme"].strip()
|
||||||
|
@ -191,7 +191,7 @@ def fmt_note(val, note_max=None, keep_numeric=False):
|
|||||||
return "EXC" # excuse, note neutralise
|
return "EXC" # excuse, note neutralise
|
||||||
if val == NOTES_ATTENTE:
|
if val == NOTES_ATTENTE:
|
||||||
return "ATT" # attente, note neutralisee
|
return "ATT" # attente, note neutralisee
|
||||||
if isinstance(val, float) or isinstance(val, int):
|
if not isinstance(val, str):
|
||||||
if np.isnan(val):
|
if np.isnan(val):
|
||||||
return "~"
|
return "~"
|
||||||
if (note_max is not None) and note_max > 0:
|
if (note_max is not None) and note_max > 0:
|
||||||
|
@ -9,19 +9,22 @@ function toggle_new_ue_form(state) {
|
|||||||
text_color = 'rgb(0,0,0)';
|
text_color = 'rgb(0,0,0)';
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#tf_extue_titre td:eq(1) input").prop( "disabled", state );
|
$("#tf_extue_titre td:eq(1) input").prop("disabled", state);
|
||||||
$("#tf_extue_titre td:eq(1) input").css('color', text_color)
|
$("#tf_extue_titre").css('color', text_color)
|
||||||
|
|
||||||
$("#tf_extue_acronyme td:eq(1) input").prop( "disabled", state );
|
$("#tf_extue_acronyme td:eq(1) input").prop("disabled", state);
|
||||||
$("#tf_extue_acronyme td:eq(1) input").css('color', text_color)
|
$("#tf_extue_acronyme").css('color', text_color)
|
||||||
|
|
||||||
$("#tf_extue_ects td:eq(1) input").prop( "disabled", state );
|
$("#tf_extue_type td:eq(1) select").prop("disabled", state);
|
||||||
$("#tf_extue_ects td:eq(1) input").css('color', text_color)
|
$("#tf_extue_type").css('color', text_color)
|
||||||
|
|
||||||
|
$("#tf_extue_ects td:eq(1) input").prop("disabled", state);
|
||||||
|
$("#tf_extue_ects").css('color', text_color)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function update_external_ue_form() {
|
function update_external_ue_form() {
|
||||||
var state = (tf.existing_ue.value != "")
|
var state = (tf.existing_ue.value != "");
|
||||||
toggle_new_ue_form(state);
|
toggle_new_ue_form(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +15,23 @@ $(function () {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "toggle_partitions",
|
name: "toggle_partitions",
|
||||||
text: "Toutes les partitions",
|
text: "Montrer groupes",
|
||||||
action: function (e, dt, node, config) {
|
action: function (e, dt, node, config) {
|
||||||
let visible = dt.columns(".partition_aux").visible()[0];
|
let visible = dt.columns(".partition_aux").visible()[0];
|
||||||
dt.columns(".partition_aux").visible(!visible);
|
dt.columns(".partition_aux").visible(!visible);
|
||||||
dt.buttons('toggle_partitions:name').text(visible ? "Toutes les partitions" : "Cacher les partitions");
|
dt.buttons('toggle_partitions:name').text(visible ? "Montrer groupes" : "Cacher les groupes");
|
||||||
}
|
}
|
||||||
}];
|
},
|
||||||
|
{
|
||||||
|
name: "toggle_partitions_rangs",
|
||||||
|
text: "Rangs groupes",
|
||||||
|
action: function (e, dt, node, config) {
|
||||||
|
let rangs_visible = dt.columns(".partition_rangs").visible()[0];
|
||||||
|
dt.columns(".partition_rangs").visible(!rangs_visible);
|
||||||
|
dt.buttons('toggle_partitions_rangs:name').text(rangs_visible ? "Rangs groupes" : "Cacher rangs groupes");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
if (!$('table.table_recap').hasClass("jury")) {
|
if (!$('table.table_recap').hasClass("jury")) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
$('table.table_recap').hasClass("apc") ?
|
$('table.table_recap').hasClass("apc") ?
|
||||||
@ -95,12 +105,12 @@ $(function () {
|
|||||||
"columnDefs": [
|
"columnDefs": [
|
||||||
{
|
{
|
||||||
// cache les codes, le détail de l'identité, les groupes, les colonnes admission et les vides
|
// cache les codes, le détail de l'identité, les groupes, les colonnes admission et les vides
|
||||||
targets: ["codes", "identite_detail", "partition_aux", "admission", "col_empty"],
|
targets: ["codes", "identite_detail", "partition_aux", "partition_rangs", "admission", "col_empty"],
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Elimine les 0 à gauche pour les exports excel et les "copy"
|
// Elimine les 0 à gauche pour les exports excel et les "copy"
|
||||||
targets: ["col_mod", "col_moy_gen", "col_ue", "col_res", "col_sae"],
|
targets: ["col_mod", "col_moy_gen", "col_ue", "col_res", "col_sae", "evaluation"],
|
||||||
render: function (data, type, row) {
|
render: function (data, type, row) {
|
||||||
return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data;
|
return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
{# Liste des permissions #}
|
{# Liste des permissions #}
|
||||||
<div class="permissions">
|
<div class="permissions">
|
||||||
<p>Permissions de cet utilisateur dans le département {dept}:</p>
|
<p>Permissions de cet utilisateur dans le département {{dept}}:</p>
|
||||||
<ul>
|
<ul>
|
||||||
{% for p in Permission.description %}
|
{% for p in Permission.description %}
|
||||||
<li>{{Permission.description[p]}} :
|
<li>{{Permission.description[p]}} :
|
||||||
|
@ -292,7 +292,7 @@ def formsemestre_bulletinetud(
|
|||||||
format = format or "html"
|
format = format or "html"
|
||||||
|
|
||||||
if not isinstance(formsemestre_id, int):
|
if not isinstance(formsemestre_id, int):
|
||||||
raise ValueError("formsemestre_id must be an integer !")
|
abort(404, description="formsemestre_id must be an integer !")
|
||||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
if etudid:
|
if etudid:
|
||||||
etud = models.Identite.query.get_or_404(etudid)
|
etud = models.Identite.query.get_or_404(etudid)
|
||||||
@ -314,7 +314,7 @@ def formsemestre_bulletinetud(
|
|||||||
)
|
)
|
||||||
if format == "json":
|
if format == "json":
|
||||||
return sco_bulletins.get_formsemestre_bulletin_etud_json(
|
return sco_bulletins.get_formsemestre_bulletin_etud_json(
|
||||||
formsemestre, etud, version=version
|
formsemestre, etud, version=version, force_publishing=force_publishing
|
||||||
)
|
)
|
||||||
if formsemestre.formation.is_apc() and format == "html":
|
if formsemestre.formation.is_apc() and format == "html":
|
||||||
return render_template(
|
return render_template(
|
||||||
@ -648,17 +648,6 @@ def formation_export(formation_id, export_ids=False, format=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formation_import_xml")
|
|
||||||
@scodoc
|
|
||||||
@permission_required(Permission.ScoChangeFormation)
|
|
||||||
@scodoc7func
|
|
||||||
def formation_import_xml(file):
|
|
||||||
"import d'une formation en XML"
|
|
||||||
log("formation_import_xml")
|
|
||||||
doc = file.read()
|
|
||||||
return sco_formations.formation_import_xml(doc)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formation_import_xml_form", methods=["GET", "POST"])
|
@bp.route("/formation_import_xml_form", methods=["GET", "POST"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoChangeFormation)
|
@permission_required(Permission.ScoChangeFormation)
|
||||||
|
20
config.py
20
config.py
@ -13,7 +13,7 @@ class Config:
|
|||||||
|
|
||||||
SQLALCHEMY_DATABASE_URI = None # set in subclass
|
SQLALCHEMY_DATABASE_URI = None # set in subclass
|
||||||
FLASK_ENV = None # # set in subclass
|
FLASK_ENV = None # # set in subclass
|
||||||
SECRET_KEY = os.environ.get("SECRET_KEY") or "90e01e75831e4176a3c70d29564b425f"
|
SECRET_KEY = os.environ.get("SECRET_KEY") or "90e01e75831e4276a4c70d29564b425f"
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
LOG_TO_STDOUT = os.environ.get("LOG_TO_STDOUT")
|
LOG_TO_STDOUT = os.environ.get("LOG_TO_STDOUT")
|
||||||
MAIL_SERVER = os.environ.get("MAIL_SERVER", "localhost")
|
MAIL_SERVER = os.environ.get("MAIL_SERVER", "localhost")
|
||||||
@ -46,6 +46,7 @@ class Config:
|
|||||||
|
|
||||||
|
|
||||||
class ProdConfig(Config):
|
class ProdConfig(Config):
|
||||||
|
"mode production, normalement derrière nginx/gunicorn"
|
||||||
FLASK_ENV = "production"
|
FLASK_ENV = "production"
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
TESTING = False
|
TESTING = False
|
||||||
@ -56,6 +57,7 @@ class ProdConfig(Config):
|
|||||||
|
|
||||||
|
|
||||||
class DevConfig(Config):
|
class DevConfig(Config):
|
||||||
|
"mode développement"
|
||||||
FLASK_ENV = "development"
|
FLASK_ENV = "development"
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
TESTING = False
|
TESTING = False
|
||||||
@ -66,6 +68,7 @@ class DevConfig(Config):
|
|||||||
|
|
||||||
|
|
||||||
class TestConfig(DevConfig):
|
class TestConfig(DevConfig):
|
||||||
|
"Pour les tests unitaires"
|
||||||
TESTING = True
|
TESTING = True
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
SQLALCHEMY_DATABASE_URI = (
|
SQLALCHEMY_DATABASE_URI = (
|
||||||
@ -76,6 +79,19 @@ class TestConfig(DevConfig):
|
|||||||
SECRET_KEY = os.environ.get("TEST_SECRET_KEY") or "c7ecff5db1594c208f573ff30e0f6bca"
|
SECRET_KEY = os.environ.get("TEST_SECRET_KEY") or "c7ecff5db1594c208f573ff30e0f6bca"
|
||||||
|
|
||||||
|
|
||||||
|
class TestAPIConfig(Config):
|
||||||
|
"Pour les tests de l'API"
|
||||||
|
FLASK_ENV = "test_api"
|
||||||
|
TESTING = False
|
||||||
|
DEBUG = True
|
||||||
|
SQLALCHEMY_DATABASE_URI = (
|
||||||
|
os.environ.get("SCODOC_TEST_API_DATABASE_URI")
|
||||||
|
or "postgresql:///SCODOC_TEST_API"
|
||||||
|
)
|
||||||
|
DEPT_TEST = "TAPI_" # nom du département, ne pas l'utiliser pour un "vrai"
|
||||||
|
SECRET_KEY = os.environ.get("TEST_SECRET_KEY") or "c7ecff5db15946789Hhahbh88aja175"
|
||||||
|
|
||||||
|
|
||||||
mode = os.environ.get("FLASK_ENV", "production")
|
mode = os.environ.get("FLASK_ENV", "production")
|
||||||
if mode == "production":
|
if mode == "production":
|
||||||
RunningConfig = ProdConfig
|
RunningConfig = ProdConfig
|
||||||
@ -83,3 +99,5 @@ elif mode == "development":
|
|||||||
RunningConfig = DevConfig
|
RunningConfig = DevConfig
|
||||||
elif mode == "test":
|
elif mode == "test":
|
||||||
RunningConfig = TestConfig
|
RunningConfig = TestConfig
|
||||||
|
elif mode == "test_api":
|
||||||
|
RunningConfig = TestAPIConfig
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.2.15"
|
SCOVERSION = "9.2.24"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
@ -496,12 +496,11 @@ def clear_cache(sanitize): # clear-cache
|
|||||||
|
|
||||||
|
|
||||||
@app.cli.command()
|
@app.cli.command()
|
||||||
def init_test_database():
|
def init_test_database(): # init-test-database
|
||||||
"""Initialise les objets en base pour les tests API
|
"""Initialise les objets en base pour les tests API
|
||||||
(à appliquer sur SCODOC_TEST ou SCODOC_DEV)
|
(à appliquer sur SCODOC_TEST ou SCODOC_DEV)
|
||||||
"""
|
"""
|
||||||
click.echo("Initialisation base de test API...")
|
click.echo("Initialisation base de test API...")
|
||||||
# import app as mapp # le package app
|
|
||||||
|
|
||||||
ctx = app.test_request_context()
|
ctx = app.test_request_context()
|
||||||
ctx.push()
|
ctx.push()
|
||||||
|
1
tests/api/__init__.py
Normal file
1
tests/api/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# API tests
|
11
tests/api/dotenv_exemple
Normal file
11
tests/api/dotenv_exemple
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Configuration du _client_ test API
|
||||||
|
# A renommer .env
|
||||||
|
# and /opt/scodoc/tests/api/
|
||||||
|
# et à remplir.
|
||||||
|
|
||||||
|
# URL du serveur ScoDoc à interroger
|
||||||
|
SCODOC_URL = "http://localhost:5000/"
|
||||||
|
|
||||||
|
# Le client (python) doit-il vérifier le certificat SSL du serveur ?
|
||||||
|
# ou True si serveur de production avec certif SSL valide
|
||||||
|
CHECK_CERTIFICATE = False
|
@ -16,29 +16,32 @@ export CHECK_CERTIFICATE=0 # ou 1 si serveur de production avec certif SSL valid
|
|||||||
(on peut aussi placer ces valeurs dans un fichier .env du répertoire tests/api).
|
(on peut aussi placer ces valeurs dans un fichier .env du répertoire tests/api).
|
||||||
|
|
||||||
|
|
||||||
Travail en cours, un seul point d'API (list_depts).
|
Travail en cours.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
import os
|
import os
|
||||||
import pdb
|
|
||||||
import requests
|
import requests
|
||||||
import urllib3
|
import urllib3
|
||||||
from pprint import pprint as pp
|
from pprint import pprint as pp
|
||||||
|
|
||||||
# --- Lecture configuration (variables d'env ou .env)
|
# --- Lecture configuration (variables d'env ou .env)
|
||||||
BASEDIR = os.path.abspath(os.path.dirname(__file__))
|
try:
|
||||||
|
BASEDIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
except NameError:
|
||||||
|
BASEDIR = "."
|
||||||
|
|
||||||
load_dotenv(os.path.join(BASEDIR, ".env"))
|
load_dotenv(os.path.join(BASEDIR, ".env"))
|
||||||
CHECK_CERTIFICATE = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
|
CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
|
||||||
SCODOC_URL = os.environ["SCODOC_URL"]
|
SCODOC_URL = os.environ["SCODOC_URL"] or "http://localhost:5000"
|
||||||
SCODOC_DEPT = os.environ["SCODOC_DEPT"]
|
API_URL = SCODOC_URL + "/ScoDoc/api"
|
||||||
DEPT_URL = SCODOC_URL + "/ScoDoc/" + SCODOC_DEPT + "/Scolarite/"
|
|
||||||
SCODOC_USER = os.environ["SCODOC_USER"]
|
SCODOC_USER = os.environ["SCODOC_USER"]
|
||||||
SCODOC_PASSWORD = os.environ["SCODOC_PASSWD"]
|
SCODOC_PASSWORD = os.environ["SCODOC_PASSWORD"]
|
||||||
print(f"SCODOC_URL={SCODOC_URL}")
|
print(f"SCODOC_URL={SCODOC_URL}")
|
||||||
|
print(f"API URL={API_URL}")
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
if not CHECK_CERTIFICATE:
|
if not CHK_CERT:
|
||||||
urllib3.disable_warnings()
|
urllib3.disable_warnings()
|
||||||
|
|
||||||
|
|
||||||
@ -48,9 +51,7 @@ class ScoError(Exception):
|
|||||||
|
|
||||||
def GET(path: str, headers={}, errmsg=None):
|
def GET(path: str, headers={}, errmsg=None):
|
||||||
"""Get and returns as JSON"""
|
"""Get and returns as JSON"""
|
||||||
r = requests.get(
|
r = requests.get(API_URL + "/" + path, headers=headers or HEADERS, verify=CHK_CERT)
|
||||||
DEPT_URL + "/" + path, headers=headers or HEADERS, verify=CHECK_CERTIFICATE
|
|
||||||
)
|
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
raise ScoError(errmsg or "erreur !")
|
raise ScoError(errmsg or "erreur !")
|
||||||
return r.json() # decode la reponse JSON
|
return r.json() # decode la reponse JSON
|
||||||
@ -58,39 +59,59 @@ def GET(path: str, headers={}, errmsg=None):
|
|||||||
|
|
||||||
def POST(s, path: str, data: dict, errmsg=None):
|
def POST(s, path: str, data: dict, errmsg=None):
|
||||||
"""Post"""
|
"""Post"""
|
||||||
r = s.post(DEPT_URL + "/" + path, data=data, verify=CHECK_CERTIFICATE)
|
r = s.post(API_URL + "/" + path, data=data, verify=CHK_CERT)
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
raise ScoError(errmsg or "erreur !")
|
raise ScoError(errmsg or "erreur !")
|
||||||
return r.text
|
return r.text
|
||||||
|
|
||||||
|
|
||||||
# --- Obtention du jeton (token)
|
# --- Obtention du jeton (token)
|
||||||
r = requests.post(
|
r = requests.post(API_URL + "/tokens", auth=(SCODOC_USER, SCODOC_PASSWORD))
|
||||||
SCODOC_URL + "/ScoDoc/api/tokens", auth=(SCODOC_USER, SCODOC_PASSWORD)
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
token = r.json()["token"]
|
token = r.json()["token"]
|
||||||
HEADERS = {"Authorization": f"Bearer {token}"}
|
HEADERS = {"Authorization": f"Bearer {token}"}
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(API_URL + "/departements", headers=HEADERS, verify=CHK_CERT)
|
||||||
SCODOC_URL + "/ScoDoc/api/list_depts",
|
|
||||||
headers=HEADERS,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
raise ScoError("erreur de connexion: vérifier adresse et identifiants")
|
raise ScoError("erreur de connexion: vérifier adresse et identifiants")
|
||||||
|
|
||||||
pp(r.json())
|
pp(r.json())
|
||||||
|
|
||||||
# Liste des tous les étudiants en cours (de tous les depts)
|
# Liste de tous les étudiants en cours (de tous les depts)
|
||||||
r = requests.get(
|
r = requests.get(API_URL + "/etudiants/courant", headers=HEADERS, verify=CHK_CERT)
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiants/courant",
|
|
||||||
headers=HEADERS,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
raise ScoError("erreur de connexion: vérifier adresse et identifiants")
|
raise ScoError("erreur de connexion: vérifier adresse et identifiants")
|
||||||
|
|
||||||
|
print(f"{len(r.json())} étudiants courants")
|
||||||
|
|
||||||
|
# Bulletin d'un BUT
|
||||||
|
formsemestre_id = 1052 # A adapter
|
||||||
|
etudid = 16400
|
||||||
|
bul = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin")
|
||||||
|
|
||||||
|
# d'un DUT
|
||||||
|
formsemestre_id = 1028 # A adapter
|
||||||
|
etudid = 14721
|
||||||
|
bul_dut = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin")
|
||||||
|
|
||||||
|
|
||||||
|
# Infos sur un étudiant
|
||||||
|
etudid = 3561
|
||||||
|
code_nip = "11303314"
|
||||||
|
etud = GET(f"/etudiant/etudid/{etudid}")
|
||||||
|
print(etud)
|
||||||
|
|
||||||
|
etud = GET(f"/etudiant/nip/{code_nip}")
|
||||||
|
print(etud)
|
||||||
|
|
||||||
|
sems = GET(f"/etudiant/etudid/{etudid}/formsemestres")
|
||||||
|
print("\n".join([s["titre_num"] for s in sems]))
|
||||||
|
|
||||||
|
sems = GET(f"/etudiant/nip/{code_nip}/formsemestres")
|
||||||
|
print("\n".join([s["titre_num"] for s in sems]))
|
||||||
|
|
||||||
|
# Evaluation
|
||||||
|
evals = GET("/evaluations/1")
|
||||||
|
|
||||||
# # --- 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 !")
|
||||||
@ -146,15 +167,3 @@ if r.status_code != 200:
|
|||||||
# print(
|
# print(
|
||||||
# f"Pour vérifier, aller sur: {DEPT_URL}/Notes/moduleimpl_status?moduleimpl_id={mod['moduleimpl_id']}",
|
# f"Pour vérifier, aller sur: {DEPT_URL}/Notes/moduleimpl_status?moduleimpl_id={mod['moduleimpl_id']}",
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# # ---- Saisie d'une note
|
|
||||||
# junk = POST(
|
|
||||||
# s,
|
|
||||||
# "/Notes/save_note",
|
|
||||||
# data={
|
|
||||||
# "etudid": etudid,
|
|
||||||
# "evaluation_id": evaluation_id,
|
|
||||||
# "value": 16.66, # la note !
|
|
||||||
# "comment": "test API",
|
|
||||||
# },
|
|
||||||
# )
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""Test Logos
|
"""Test API
|
||||||
|
|
||||||
Utilisation :
|
Utilisation :
|
||||||
créer les variables d'environnement: (indiquer les valeurs
|
créer les variables d'environnement: (indiquer les valeurs
|
||||||
pour le serveur ScoDoc que vous voulez interroger)
|
pour le serveur ScoDoc que vous voulez interroger)
|
||||||
|
|
||||||
export SCODOC_URL="https://scodoc.xxx.net/"
|
export SCODOC_URL="https://scodoc.xxx.net/"
|
||||||
export SCODOC_USER="xxx"
|
export API_USER="xxx"
|
||||||
export SCODOC_PASSWD="xxx"
|
export SCODOC_PASSWD="xxx"
|
||||||
export CHECK_CERTIFICATE=0 # ou 1 si serveur de production avec certif SSL valide
|
export CHECK_CERTIFICATE=0 # ou 1 si serveur de production avec certif SSL valide
|
||||||
|
|
||||||
@ -15,23 +15,26 @@ Utilisation :
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
BASEDIR = "/opt/scodoc/tests/api"
|
||||||
|
load_dotenv(os.path.join(BASEDIR, ".env"))
|
||||||
|
CHECK_CERTIFICATE = bool(os.environ.get("CHECK_CERTIFICATE", False))
|
||||||
|
SCODOC_URL = os.environ["SCODOC_URL"]
|
||||||
|
API_URL = SCODOC_URL + "/ScoDoc/api"
|
||||||
|
API_USER = os.environ.get("API_USER", "test")
|
||||||
|
API_PASSWORD = os.environ.get("API_PASSWD", "test")
|
||||||
|
DEPT_ACRONYM = "TAPI"
|
||||||
|
print(f"SCODOC_URL={SCODOC_URL}")
|
||||||
|
print(f"API URL={API_URL}")
|
||||||
|
|
||||||
|
|
||||||
SCODOC_USER = "test"
|
@pytest.fixture
|
||||||
SCODOC_PASSWORD = "test"
|
def api_headers() -> dict:
|
||||||
SCODOC_URL = "http://192.168.1.12:5000"
|
|
||||||
CHECK_CERTIFICATE = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
|
|
||||||
|
|
||||||
|
|
||||||
def get_token():
|
|
||||||
"""
|
"""
|
||||||
Permet de set le token dans le header
|
Demande un jeton et renvoie un dict à utiliser dans les en-têtes de requêtes http
|
||||||
"""
|
"""
|
||||||
r0 = requests.post(
|
r0 = requests.post(API_URL + "/tokens", auth=(API_USER, API_PASSWORD))
|
||||||
SCODOC_URL + "/ScoDoc/api/tokens", auth=(SCODOC_USER, SCODOC_PASSWORD)
|
|
||||||
)
|
|
||||||
token = r0.json()["token"]
|
token = r0.json()["token"]
|
||||||
return {"Authorization": f"Bearer {token}"}
|
return {"Authorization": f"Bearer {token}"}
|
||||||
|
|
||||||
|
|
||||||
HEADERS = get_token()
|
|
||||||
|
@ -18,63 +18,47 @@ Utilisation :
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from tests.api.setup_test_api import SCODOC_URL, CHECK_CERTIFICATE, HEADERS
|
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
||||||
|
|
||||||
|
# Etudiant pour les tests
|
||||||
|
ETUDID = 1
|
||||||
|
|
||||||
|
|
||||||
# absences
|
# absences
|
||||||
def test_absences():
|
def test_absences(api_headers):
|
||||||
|
"""
|
||||||
|
Route: /absences/etudid/<int:etudid>
|
||||||
|
"""
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/absences/etudid/<int:etudid>",
|
f"{API_URL}/absences/etudid/{ETUDID}",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
|
|
||||||
r = requests.get(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/absences/nip/<int:nip>",
|
|
||||||
headers=HEADERS,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
|
|
||||||
r = requests.get(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/absences/ine/<int:ine>",
|
|
||||||
headers=HEADERS,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
# absences_justify
|
# absences_justify
|
||||||
def test_absences_justify():
|
def test_absences_justify(api_headers):
|
||||||
|
"""
|
||||||
|
Route: /absences/etudid/<etudid:int>/just
|
||||||
|
"""
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/absences/etudid/1/just",
|
API_URL + f"/absences/etudid/{ETUDID}/just",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
|
|
||||||
r = requests.get(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/absences/nip/1/just",
|
|
||||||
headers=HEADERS,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
|
||||||
)
|
|
||||||
assert r.status_code == 200
|
|
||||||
|
|
||||||
r = requests.get(
|
|
||||||
SCODOC_URL + "/ScoDoc/api/absences/ine/1/just",
|
|
||||||
headers=HEADERS,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
# TODO vérifier résultat
|
||||||
|
|
||||||
|
|
||||||
# abs_groupe_etat
|
# XXX TODO
|
||||||
def test_abs_groupe_etat():
|
# def test_abs_groupe_etat(api_headers):
|
||||||
r = requests.get(
|
# """
|
||||||
SCODOC_URL
|
# Route:
|
||||||
+ "/ScoDoc/api/absences/abs_group_etat/?group_id=<int:group_id>&date_debut=date_debut&date_fin=date_fin",
|
# """
|
||||||
headers=HEADERS,
|
# r = requests.get(
|
||||||
verify=CHECK_CERTIFICATE,
|
# API_URL + "/absences/abs_group_etat/?group_id=<int:group_id>&date_debut=date_debut&date_fin=date_fin",
|
||||||
)
|
# headers=api_headers,
|
||||||
assert r.status_code == 200
|
# verify=CHECK_CERTIFICATE,
|
||||||
|
# )
|
||||||
|
# assert r.status_code == 200
|
||||||
|
@ -19,94 +19,89 @@ Utilisation :
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from tests.api.setup_test_api import SCODOC_URL, CHECK_CERTIFICATE, HEADERS
|
from tests.api.setup_test_api import (
|
||||||
|
API_URL,
|
||||||
|
CHECK_CERTIFICATE,
|
||||||
|
DEPT_ACRONYM,
|
||||||
|
api_headers,
|
||||||
|
)
|
||||||
from tests.api.tools_test_api import verify_fields
|
from tests.api.tools_test_api import verify_fields
|
||||||
|
|
||||||
# departements
|
DEPARTEMENT_FIELDS = [
|
||||||
def test_departements():
|
"id",
|
||||||
fields = [
|
"acronym",
|
||||||
"id",
|
"description",
|
||||||
"acronym",
|
"visible",
|
||||||
"description",
|
"date_creation",
|
||||||
"visible",
|
]
|
||||||
"date_creation",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_departements(api_headers):
|
||||||
|
""" "
|
||||||
|
Routes: /departements_ids, /departement, /departement/<string:dept>/formsemestres_ids
|
||||||
|
|
||||||
|
"""
|
||||||
|
# --- Liste des ids
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/departements",
|
API_URL + "/departements_ids",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 1
|
departements_ids = r.json()
|
||||||
|
assert isinstance(departements_ids, list)
|
||||||
dept = r.json()[0]
|
assert len(departements_ids) > 0
|
||||||
|
assert all(isinstance(x, int) for x in departements_ids)
|
||||||
fields_OK = verify_fields(dept, fields)
|
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
|
|
||||||
# liste_etudiants
|
|
||||||
def test_liste_etudiants():
|
|
||||||
fields = [
|
|
||||||
"civilite",
|
|
||||||
"code_ine",
|
|
||||||
"code_nip",
|
|
||||||
"date_naissance",
|
|
||||||
"email",
|
|
||||||
"emailperso",
|
|
||||||
"etudid",
|
|
||||||
"nom",
|
|
||||||
"prenom",
|
|
||||||
"nomprenom",
|
|
||||||
"lieu_naissance",
|
|
||||||
"dept_naissance",
|
|
||||||
"nationalite",
|
|
||||||
"boursier",
|
|
||||||
"id",
|
|
||||||
"codepostaldomicile",
|
|
||||||
"paysdomicile",
|
|
||||||
"telephonemobile",
|
|
||||||
"typeadresse",
|
|
||||||
"domicile",
|
|
||||||
"villedomicile",
|
|
||||||
"telephone",
|
|
||||||
"fax",
|
|
||||||
"description",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
dept_id = departements_ids[0]
|
||||||
|
# --- Infos sur un département, accès par id
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/departements/TAPI/etudiants/liste",
|
f"{API_URL}/departement/{dept_id}",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
etu = r.json()[0]
|
|
||||||
|
|
||||||
fields_OK = verify_fields(etu, fields)
|
|
||||||
|
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 16
|
dept_a = r.json()
|
||||||
assert fields_OK is True
|
assert verify_fields(dept_a, DEPARTEMENT_FIELDS) is True
|
||||||
|
# --- Infos sur un département, accès par acronyme4
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/departements/TAPI/etudiants/liste/1",
|
f"{API_URL}/departement/{dept_a['acronym']}",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
etu = r.json()[0]
|
|
||||||
|
|
||||||
fields_OK = verify_fields(etu, fields)
|
|
||||||
|
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 16
|
dept_b = r.json()
|
||||||
assert fields_OK is True
|
assert dept_a == dept_b
|
||||||
|
|
||||||
|
# Liste des formsemestres
|
||||||
|
r = requests.get(
|
||||||
|
f"{API_URL}/departement/{dept_a['acronym']}/formsemestres_ids",
|
||||||
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
dept_ids = r.json()
|
||||||
|
assert isinstance(dept_ids, list)
|
||||||
|
assert all(isinstance(x, int) for x in dept_ids)
|
||||||
|
assert len(dept_ids) > 0
|
||||||
|
assert dept_id in dept_ids
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_etudiants(api_headers):
|
||||||
|
fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"}
|
||||||
|
|
||||||
|
r = requests.get(
|
||||||
|
f"{API_URL}/departement/{DEPT_ACRONYM}/etudiants",
|
||||||
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
etud = r.json()[0]
|
||||||
|
assert verify_fields(etud, fields) is True
|
||||||
|
|
||||||
|
|
||||||
# liste_semestres_courant
|
# liste_semestres_courant
|
||||||
def test_semestres_courant():
|
def test_semestres_courant(api_headers):
|
||||||
fields = [
|
fields = [
|
||||||
"titre",
|
"titre",
|
||||||
"gestion_semestrielle",
|
"gestion_semestrielle",
|
||||||
@ -130,32 +125,39 @@ def test_semestres_courant():
|
|||||||
"block_moyennes",
|
"block_moyennes",
|
||||||
"formsemestre_id",
|
"formsemestre_id",
|
||||||
"titre_num",
|
"titre_num",
|
||||||
|
"titre_formation",
|
||||||
"date_debut_iso",
|
"date_debut_iso",
|
||||||
"date_fin_iso",
|
"date_fin_iso",
|
||||||
"responsables",
|
"responsables",
|
||||||
]
|
]
|
||||||
|
dept_id = 1
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/departements/TAPI/semestres_courants",
|
f"{API_URL}/departement/{dept_id}",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
sem = r.json()[0]
|
|
||||||
|
|
||||||
fields_OK = verify_fields(sem, fields)
|
|
||||||
|
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 1
|
dept = r.json()
|
||||||
assert fields_OK is True
|
assert dept["id"] == dept_id
|
||||||
|
# Accès via acronyme
|
||||||
|
|
||||||
# referenciel_competences
|
|
||||||
def test_referenciel_competences():
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL
|
f"{API_URL}/departement/{dept['acronym']}/formsemestres_courants",
|
||||||
+ "/ScoDoc/api/departements/TAPI/formations/1/referentiel_competences",
|
headers=api_headers,
|
||||||
headers=HEADERS,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200 or 204
|
assert r.status_code == 200
|
||||||
|
result_a = r.json()
|
||||||
|
assert isinstance(result_a, list) # liste de formsemestres
|
||||||
|
assert len(result_a) > 0
|
||||||
|
sem = result_a[0]
|
||||||
|
assert verify_fields(sem, fields) is True
|
||||||
|
|
||||||
|
# accès via dept_id
|
||||||
|
r = requests.get(
|
||||||
|
f"{API_URL}/departement/{dept['id']}/formsemestres_courants",
|
||||||
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
result_b = r.json()
|
||||||
|
assert result_a == result_b
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""Test Logos
|
"""Test API: accès aux étudiants
|
||||||
|
|
||||||
Utilisation :
|
Utilisation :
|
||||||
créer les variables d'environnement: (indiquer les valeurs
|
créer les variables d'environnement: (indiquer les valeurs
|
||||||
@ -16,281 +16,222 @@ Utilisation :
|
|||||||
Lancer :
|
Lancer :
|
||||||
pytest tests/api/test_api_etudiants.py
|
pytest tests/api/test_api_etudiants.py
|
||||||
"""
|
"""
|
||||||
from random import randint
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from tests.api.setup_test_api import SCODOC_URL, CHECK_CERTIFICATE, HEADERS
|
from tests.api.setup_test_api import (
|
||||||
|
API_URL,
|
||||||
|
CHECK_CERTIFICATE,
|
||||||
|
DEPT_ACRONYM,
|
||||||
|
api_headers,
|
||||||
|
)
|
||||||
from tests.api.tools_test_api import verify_fields
|
from tests.api.tools_test_api import verify_fields
|
||||||
|
from tests.api.tools_test_api import ETUD_FIELDS, FSEM_FIELDS
|
||||||
|
|
||||||
# etudiants_courant
|
|
||||||
def test_etudiants_courant():
|
|
||||||
|
|
||||||
fields = [
|
def test_etudiants_courant(api_headers):
|
||||||
"id",
|
"""
|
||||||
"nip",
|
Route: /etudiants/courant
|
||||||
"nom",
|
"""
|
||||||
"nom_usuel",
|
fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"}
|
||||||
"prenom",
|
|
||||||
"civilite",
|
|
||||||
]
|
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiants/courant",
|
API_URL + "/etudiants/courant",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 16
|
etudiants = r.json()
|
||||||
|
assert len(etudiants) > 0
|
||||||
|
|
||||||
# Choisis aléatoirement un étudiant dans la liste des étudiants
|
etud = etudiants[-1]
|
||||||
etu = r.json()[randint(0, len(r.json())) - 1]
|
assert verify_fields(etud, fields) is True
|
||||||
|
|
||||||
fields_OK = verify_fields(etu, fields)
|
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
########## Version long################
|
|
||||||
|
|
||||||
fields_long = [
|
|
||||||
"civilite",
|
|
||||||
"code_ine",
|
|
||||||
"code_nip",
|
|
||||||
"date_naissance",
|
|
||||||
"email",
|
|
||||||
"emailperso",
|
|
||||||
"etudid",
|
|
||||||
"nom",
|
|
||||||
"prenom",
|
|
||||||
"nomprenom",
|
|
||||||
"lieu_naissance",
|
|
||||||
"dept_naissance",
|
|
||||||
"nationalite",
|
|
||||||
"boursier",
|
|
||||||
"id",
|
|
||||||
"codepostaldomicile",
|
|
||||||
"paysdomicile",
|
|
||||||
"telephonemobile",
|
|
||||||
"typeadresse",
|
|
||||||
"domicile",
|
|
||||||
"villedomicile",
|
|
||||||
"telephone",
|
|
||||||
"fax",
|
|
||||||
"description",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
########## Version long ################
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiants/courant/long",
|
API_URL + "/etudiants/courant/long",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 16
|
etudiants = r.json()
|
||||||
|
assert len(etudiants) == 16 # HARDCODED
|
||||||
|
|
||||||
# Choisis aléatoirement un étudiant dans la liste des étudiants
|
etud = etudiants[-1]
|
||||||
etu = r.json()[randint(0, len(r.json())) - 1]
|
assert verify_fields(etud, ETUD_FIELDS) is True
|
||||||
|
|
||||||
fields_OK = verify_fields(etu, fields_long)
|
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
|
|
||||||
# etudiant
|
def test_etudiant(api_headers):
|
||||||
def test_etudiant():
|
"""
|
||||||
|
Routes: /etudiant/etudid, /etudiant/nip, /etudiant/ine
|
||||||
fields = [
|
"""
|
||||||
"civilite",
|
|
||||||
"code_ine",
|
|
||||||
"code_nip",
|
|
||||||
"date_naissance",
|
|
||||||
"email",
|
|
||||||
"emailperso",
|
|
||||||
"etudid",
|
|
||||||
"nom",
|
|
||||||
"prenom",
|
|
||||||
"nomprenom",
|
|
||||||
"lieu_naissance",
|
|
||||||
"dept_naissance",
|
|
||||||
"nationalite",
|
|
||||||
"boursier",
|
|
||||||
"id",
|
|
||||||
"domicile",
|
|
||||||
"villedomicile",
|
|
||||||
"telephone",
|
|
||||||
"fax",
|
|
||||||
"description",
|
|
||||||
"codepostaldomicile",
|
|
||||||
"paysdomicile",
|
|
||||||
"telephonemobile",
|
|
||||||
"typeadresse",
|
|
||||||
]
|
|
||||||
|
|
||||||
######### Test etudid #########
|
######### Test etudid #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1",
|
API_URL + "/etudiant/etudid/1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 24
|
etud = r.json()
|
||||||
|
|
||||||
etu = r.json()
|
assert verify_fields(etud, ETUD_FIELDS) is True
|
||||||
|
|
||||||
fields_OK = verify_fields(etu, fields)
|
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
######### Test code nip #########
|
######### Test code nip #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/nip/1",
|
API_URL + "/etudiant/nip/1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 24
|
etud = r.json()
|
||||||
|
fields_ok = verify_fields(etud, ETUD_FIELDS)
|
||||||
etu = r.json()
|
assert fields_ok is True
|
||||||
|
assert etud["dept_acronym"] == DEPT_ACRONYM
|
||||||
fields_OK = verify_fields(etu, fields)
|
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
######### Test code ine #########
|
######### Test code ine #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/ine/1",
|
API_URL + "/etudiant/ine/INE1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 24
|
etud = r.json()
|
||||||
|
assert len(etud) == 25
|
||||||
|
fields_ok = verify_fields(etud, ETUD_FIELDS)
|
||||||
|
assert fields_ok is True
|
||||||
|
|
||||||
etu = r.json()
|
# Vérifie le requetage des 3 1er étudiants
|
||||||
|
for etudid in (1, 2, 3):
|
||||||
fields_OK = verify_fields(etu, fields)
|
r = requests.get(
|
||||||
|
f"{API_URL }/etudiant/etudid/{etudid}",
|
||||||
assert fields_OK is True
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
etud = r.json()
|
||||||
|
nip = etud["code_nip"]
|
||||||
|
ine = etud["code_ine"]
|
||||||
|
assert isinstance(etud["id"], int)
|
||||||
|
assert isinstance(nip, str)
|
||||||
|
assert isinstance(ine, str)
|
||||||
|
r = requests.get(
|
||||||
|
f"{API_URL }/etudiant/nip/{nip}",
|
||||||
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
etud_nip = r.json()
|
||||||
|
# On doit avoir obtenue le même étudiant
|
||||||
|
assert etud_nip == etud
|
||||||
|
r = requests.get(
|
||||||
|
f"{API_URL }/etudiant/ine/{ine}",
|
||||||
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
etud_ine = r.json()
|
||||||
|
# On doit avoir obtenue le même étudiant
|
||||||
|
assert etud_ine == etud
|
||||||
|
|
||||||
|
|
||||||
# etudiant_formsemestres
|
def test_etudiant_formsemestres(api_headers):
|
||||||
def test_etudiant_formsemestres():
|
"""
|
||||||
|
Route: /etudiant/etudid/<etudid:int>/formsemestres
|
||||||
fields = [
|
"""
|
||||||
"date_fin",
|
|
||||||
"resp_can_edit",
|
|
||||||
"dept_id",
|
|
||||||
"etat",
|
|
||||||
"resp_can_change_ens",
|
|
||||||
"id",
|
|
||||||
"modalite",
|
|
||||||
"ens_can_edit_eval",
|
|
||||||
"formation_id",
|
|
||||||
"gestion_compensation",
|
|
||||||
"elt_sem_apo",
|
|
||||||
"semestre_id",
|
|
||||||
"bul_hide_xml",
|
|
||||||
"elt_annee_apo",
|
|
||||||
"titre",
|
|
||||||
"block_moyennes",
|
|
||||||
"scodoc7_id",
|
|
||||||
"date_debut",
|
|
||||||
"gestion_semestrielle",
|
|
||||||
"bul_bgcolor",
|
|
||||||
"formsemestre_id",
|
|
||||||
"titre_num",
|
|
||||||
"date_debut_iso",
|
|
||||||
"date_fin_iso",
|
|
||||||
"responsables",
|
|
||||||
]
|
|
||||||
|
|
||||||
######### Test etudid #########
|
######### Test etudid #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1/formsemestres",
|
API_URL + "/etudiant/etudid/1/formsemestres",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 1
|
formsemestres = r.json()
|
||||||
|
assert len(formsemestres) == 1
|
||||||
|
|
||||||
formsemestre = r.json()[0]
|
formsemestre = formsemestres[0]
|
||||||
|
assert verify_fields(formsemestre, FSEM_FIELDS) is True
|
||||||
fields_OK = verify_fields(formsemestre, fields)
|
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
######### Test code nip #########
|
######### Test code nip #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/nip/1/formsemestres",
|
API_URL + "/etudiant/nip/1/formsemestres",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 1
|
formsemestres = r.json()
|
||||||
|
assert len(formsemestres) == 1
|
||||||
|
|
||||||
formsemestre = r.json()[0]
|
formsemestre = formsemestres[0]
|
||||||
|
assert verify_fields(formsemestre, FSEM_FIELDS) is True
|
||||||
fields_OK = verify_fields(formsemestre, fields)
|
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
######### Test code ine #########
|
######### Test code ine #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/ine/1/formsemestres",
|
API_URL + "/etudiant/ine/INE1/formsemestres",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 1
|
formsemestres = r.json()
|
||||||
|
assert len(formsemestres) == 1
|
||||||
|
|
||||||
formsemestre = r.json()[0]
|
formsemestre = formsemestres[0]
|
||||||
|
assert verify_fields(formsemestre, FSEM_FIELDS) is True
|
||||||
fields_OK = verify_fields(formsemestre, fields)
|
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
|
|
||||||
# etudiant_bulletin_semestre
|
def test_etudiant_bulletin_semestre(api_headers):
|
||||||
def test_etudiant_bulletin_semestre():
|
"""
|
||||||
|
Route: /etudiant/etudid/<etudid>/formsemestre/<formsemestre_id>/bulletin
|
||||||
|
"""
|
||||||
######### Test etudid #########
|
######### Test etudid #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1/formsemestre/1/bulletin",
|
API_URL + "/etudiant/etudid/1/formsemestre/1/bulletin",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 13
|
bul = r.json()
|
||||||
|
assert len(bul) == 13 # HARDCODED
|
||||||
|
|
||||||
######### Test code nip #########
|
######### Test code nip #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/nip/1/formsemestre/1/bulletin",
|
API_URL + "/etudiant/nip/1/formsemestre/1/bulletin",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 13
|
bul = r.json()
|
||||||
|
assert len(bul) == 13 # HARDCODED
|
||||||
|
|
||||||
######### Test code ine #########
|
######### Test code ine #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/ine/1/formsemestre/1/bulletin",
|
API_URL + "/etudiant/ine/INE1/formsemestre/1/bulletin",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 13
|
bul = r.json()
|
||||||
|
assert len(bul) == 13 # HARDCODED
|
||||||
|
|
||||||
|
### --- Test étudiant inexistant
|
||||||
|
r = requests.get(
|
||||||
|
API_URL + "/etudiant/ine/189919919119191/formsemestre/1/bulletin",
|
||||||
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
# etudiant_groups
|
def test_etudiant_groups(api_headers):
|
||||||
def test_etudiant_groups():
|
"""
|
||||||
|
Route:
|
||||||
|
/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/groups
|
||||||
|
"""
|
||||||
fields = [
|
fields = [
|
||||||
"partition_id",
|
"partition_id",
|
||||||
"id",
|
"id",
|
||||||
@ -306,47 +247,39 @@ def test_etudiant_groups():
|
|||||||
######### Test etudid #########
|
######### Test etudid #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1/semestre/1/groups",
|
API_URL + "/etudiant/etudid/1/formsemestre/1/groups",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 1
|
groups = r.json()
|
||||||
|
assert len(groups) == 1 # dans un seul groupe
|
||||||
groups = r.json()[0]
|
group = groups[0]
|
||||||
|
fields_ok = verify_fields(group, fields)
|
||||||
fields_OK = verify_fields(groups, fields)
|
assert fields_ok is True
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
######### Test code nip #########
|
######### Test code nip #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/nip/1/semestre/1/groups",
|
API_URL + "/etudiant/nip/1/formsemestre/1/groups",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 1
|
groups = r.json()
|
||||||
|
assert len(groups) == 1 # dans un seul groupe
|
||||||
groups = r.json()[0]
|
group = groups[0]
|
||||||
|
fields_ok = verify_fields(group, fields)
|
||||||
fields_OK = verify_fields(groups, fields)
|
assert fields_ok is True
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
######### Test code ine #########
|
######### Test code ine #########
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/etudiant/ine/1/semestre/1/groups",
|
API_URL + "/etudiant/ine/INE1/formsemestre/1/groups",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 1
|
groups = r.json()
|
||||||
|
assert len(groups) == 1 # dans un seul groupe
|
||||||
groups = r.json()[0]
|
group = groups[0]
|
||||||
|
fields_ok = verify_fields(group, fields)
|
||||||
fields_OK = verify_fields(groups, fields)
|
assert fields_ok is True
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
@ -19,23 +19,31 @@ Utilisation :
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from tests.api.setup_test_api import SCODOC_URL, CHECK_CERTIFICATE, HEADERS
|
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
||||||
|
|
||||||
# evaluations
|
|
||||||
def test_evaluations():
|
def test_evaluations(api_headers):
|
||||||
|
"""
|
||||||
|
Route: /evaluation/<int:moduleimpl_id>
|
||||||
|
"""
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/evaluations/1",
|
API_URL + "/evaluations/1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
|
||||||
# evaluation_notes
|
# TODO car pas d'évaluations créées à ce stade
|
||||||
def test_evaluation_notes():
|
# def test_evaluation_notes(api_headers):
|
||||||
r = requests.get(
|
# """
|
||||||
SCODOC_URL + "/ScoDoc/api/evaluations/eval_notes/1",
|
# Route: /evaluation/eval_notes/<int:evaluation_id>
|
||||||
headers=HEADERS,
|
# """
|
||||||
verify=CHECK_CERTIFICATE,
|
# r = requests.get(
|
||||||
)
|
# API_URL + "/evaluation/eval_notes/1",
|
||||||
assert r.status_code == 200
|
# headers=api_headers,
|
||||||
|
# verify=CHECK_CERTIFICATE,
|
||||||
|
# )
|
||||||
|
# assert r.status_code == 200
|
||||||
|
# # TODO
|
||||||
|
@ -19,163 +19,91 @@ Utilisation :
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from tests.api.setup_test_api import SCODOC_URL, CHECK_CERTIFICATE, HEADERS
|
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
||||||
from tests.api.tools_test_api import verify_fields
|
from tests.api.tools_test_api import verify_fields
|
||||||
|
from tests.api.tools_test_api import FORMATION_FIELDS, MODIMPL_FIELDS
|
||||||
|
|
||||||
|
|
||||||
# formations
|
def test_formations_ids(api_headers):
|
||||||
def test_formations():
|
"""
|
||||||
fields = [
|
Route: /formations_ids
|
||||||
"id",
|
"""
|
||||||
"acronyme",
|
|
||||||
"titre_officiel",
|
|
||||||
"formation_code",
|
|
||||||
"code_specialite",
|
|
||||||
"dept_id",
|
|
||||||
"titre",
|
|
||||||
"version",
|
|
||||||
"type_parcours",
|
|
||||||
"referentiel_competence_id",
|
|
||||||
"formation_id",
|
|
||||||
]
|
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formations",
|
API_URL + "/formations_ids",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
formation = r.json()[0]
|
|
||||||
|
|
||||||
fields_OK = verify_fields(formation, fields)
|
|
||||||
|
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 1
|
formations_ids = r.json()
|
||||||
assert fields_OK is True
|
# Une liste non vide d'entiers
|
||||||
|
assert isinstance(formations_ids, list)
|
||||||
|
assert len(formations_ids) > 0
|
||||||
|
assert all(isinstance(x, int) for x in formations_ids)
|
||||||
|
|
||||||
|
|
||||||
# formations_by_id
|
def test_formations_by_id(api_headers):
|
||||||
def test_formations_by_id():
|
"""
|
||||||
fields = [
|
Route: /formation/<int:formation_id>
|
||||||
"id",
|
"""
|
||||||
"acronyme",
|
|
||||||
"titre_officiel",
|
|
||||||
"formation_code",
|
|
||||||
"code_specialite",
|
|
||||||
"dept_id",
|
|
||||||
"titre",
|
|
||||||
"version",
|
|
||||||
"type_parcours",
|
|
||||||
"referentiel_competence_id",
|
|
||||||
"formation_id",
|
|
||||||
]
|
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formations/1",
|
API_URL + "/formation/1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
formation = r.json()
|
formation = r.json()
|
||||||
|
assert verify_fields(formation, FORMATION_FIELDS) is True
|
||||||
fields_OK = verify_fields(formation, fields)
|
# TODO tester le contenu de certains champs
|
||||||
|
|
||||||
assert r.status_code == 200
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
|
|
||||||
# formation_export_by_formation_id
|
def test_formation_export(api_headers):
|
||||||
def test_formation_export_by_formation_id():
|
"""
|
||||||
fields = [
|
Route: /formation/formation_export/<int:formation_id>
|
||||||
"id",
|
"""
|
||||||
"acronyme",
|
|
||||||
"titre_officiel",
|
|
||||||
"formation_code",
|
|
||||||
"code_specialite",
|
|
||||||
"dept_id",
|
|
||||||
"titre",
|
|
||||||
"version",
|
|
||||||
"type_parcours",
|
|
||||||
"referentiel_competence_id",
|
|
||||||
"formation_id",
|
|
||||||
"ue",
|
|
||||||
]
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formations/formation_export/1",
|
API_URL + "/formation/formation_export/1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
export_formation = r.json()
|
|
||||||
|
|
||||||
fields_OK = verify_fields(export_formation, fields)
|
|
||||||
|
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert fields_OK is True
|
export_formation = r.json()
|
||||||
|
assert verify_fields(export_formation, FORMATION_FIELDS) is True
|
||||||
|
# TODO tester le contenu de certains champs
|
||||||
|
|
||||||
|
|
||||||
# formsemestre_apo
|
# TODO
|
||||||
# def test_formsemestre_apo():
|
# def test_formsemestre_apo(api_headers):
|
||||||
# r = requests.get(
|
# r = requests.get(
|
||||||
# SCODOC_URL + "/ScoDoc/api/formations/apo/<string:etape_apo>",
|
# API_URL + "/formation/apo/<string:etape_apo>",
|
||||||
# headers=HEADERS,
|
# headers=api_headers,
|
||||||
# verify=CHECK_CERTIFICATE,
|
# verify=CHECK_CERTIFICATE,
|
||||||
# )
|
# )
|
||||||
# assert r.status_code == 200
|
# assert r.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
# moduleimpl
|
def test_moduleimpl(api_headers):
|
||||||
def test_moduleimpl():
|
"""
|
||||||
|
Route: /formation/moduleimpl/<int:moduleimpl_id>
|
||||||
fields = [
|
"""
|
||||||
"id",
|
|
||||||
"formsemestre_id",
|
|
||||||
"computation_expr",
|
|
||||||
"module_id",
|
|
||||||
"responsable_id",
|
|
||||||
"moduleimpl_id",
|
|
||||||
"ens",
|
|
||||||
"module",
|
|
||||||
]
|
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formations/moduleimpl/1",
|
API_URL + "/formation/moduleimpl/1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
moduleimpl = r.json()
|
moduleimpl = r.json()
|
||||||
|
assert verify_fields(moduleimpl, MODIMPL_FIELDS) is True
|
||||||
fields_OK = verify_fields(moduleimpl, fields)
|
# TODO tester le contenu de certains champs
|
||||||
|
|
||||||
assert r.status_code == 200
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
|
|
||||||
# moduleimpls_sem
|
def test_referentiel_competences(api_headers):
|
||||||
def test_moduleimpls_sem():
|
"""
|
||||||
|
Route: "/formation/<int:formation_id>/referentiel_competences",
|
||||||
fields = [
|
"""
|
||||||
"id",
|
|
||||||
"formsemestre_id",
|
|
||||||
"computation_expr",
|
|
||||||
"module_id",
|
|
||||||
"responsable_id",
|
|
||||||
"moduleimpl_id",
|
|
||||||
"ens",
|
|
||||||
"module",
|
|
||||||
"moduleimpl_id",
|
|
||||||
"ens",
|
|
||||||
]
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formations/moduleimpl/formsemestre/1/liste",
|
API_URL + "/formation/1/referentiel_competences",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
moduleimpl = r.json()[0]
|
|
||||||
|
|
||||||
fields_OK = verify_fields(moduleimpl, fields)
|
|
||||||
|
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 21
|
# XXX A compléter
|
||||||
assert fields_OK is True
|
|
||||||
|
@ -18,83 +18,74 @@ Utilisation :
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
from app.api.formsemestres import formsemestre
|
||||||
|
|
||||||
from tests.api.setup_test_api import SCODOC_URL, CHECK_CERTIFICATE, HEADERS
|
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
||||||
from tests.api.tools_test_api import verify_fields
|
from tests.api.tools_test_api import MODIMPL_FIELDS, verify_fields
|
||||||
|
from tests.api.tools_test_api import FSEM_FIELDS, UE_FIELDS, MODULE_FIELDS
|
||||||
|
|
||||||
# formsemestre
|
# Etudiant pour les tests
|
||||||
def test_formsemestre():
|
ETUDID = 1
|
||||||
|
NIP = "1"
|
||||||
|
INE = "INE1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_formsemestre(api_headers):
|
||||||
|
"""
|
||||||
|
Route: /formsemestre/<id>
|
||||||
|
"""
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formsemestre/1",
|
API_URL + "/formsemestre/1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
formsemestre = r.json()
|
formsemestre = r.json()
|
||||||
|
assert verify_fields(formsemestre, FSEM_FIELDS)
|
||||||
fields = [
|
|
||||||
"date_fin",
|
|
||||||
"resp_can_edit",
|
|
||||||
"dept_id",
|
|
||||||
"etat",
|
|
||||||
"resp_can_change_ens",
|
|
||||||
"id",
|
|
||||||
"modalite",
|
|
||||||
"ens_can_edit_eval",
|
|
||||||
"formation_id",
|
|
||||||
"gestion_compensation",
|
|
||||||
"elt_sem_apo",
|
|
||||||
"semestre_id",
|
|
||||||
"bul_hide_xml",
|
|
||||||
"elt_annee_apo",
|
|
||||||
"titre",
|
|
||||||
"block_moyennes",
|
|
||||||
"scodoc7_id",
|
|
||||||
"date_debut",
|
|
||||||
"gestion_semestrielle",
|
|
||||||
"bul_bgcolor",
|
|
||||||
"formsemestre_id",
|
|
||||||
"titre_num",
|
|
||||||
"date_debut_iso",
|
|
||||||
"date_fin_iso",
|
|
||||||
"responsables",
|
|
||||||
]
|
|
||||||
|
|
||||||
fields_OK = verify_fields(formsemestre, fields)
|
|
||||||
|
|
||||||
assert fields_OK is True
|
|
||||||
|
|
||||||
|
|
||||||
# etudiant_bulletin
|
def test_etudiant_bulletin(api_headers):
|
||||||
def test_etudiant_bulletin():
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
|
formsemestre_id = 1
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formsemestre/1/etudiant/etudid/1/bulletin",
|
f"{API_URL}/etudiant/etudid/1/formsemestre/{formsemestre_id}/bulletin",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
bull_a = r.json()
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formsemestre/1/etudiant/nip/1/bulletin",
|
f"{API_URL}/etudiant/nip/{NIP}/formsemestre/{formsemestre_id}/bulletin",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
bull_b = r.json()
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formsemestre/1/etudiant/ine/1/bulletin",
|
f"{API_URL}/etudiant/ine/{INE}/formsemestre/{formsemestre_id}/bulletin",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
bull_c = r.json()
|
||||||
|
# elimine les dates de publication pour comparer les autres champs
|
||||||
|
del bull_a["date"]
|
||||||
|
del bull_b["date"]
|
||||||
|
del bull_c["date"]
|
||||||
|
assert bull_a == bull_b == bull_c
|
||||||
|
|
||||||
|
|
||||||
# bulletins
|
def test_bulletins(api_headers):
|
||||||
def test_bulletins():
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formsemestre/1/bulletins",
|
API_URL + "/formsemestre/1/bulletins",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
@ -103,88 +94,40 @@ def test_bulletins():
|
|||||||
# # jury
|
# # jury
|
||||||
# def test_jury():
|
# def test_jury():
|
||||||
# r = requests.get(
|
# r = requests.get(
|
||||||
# SCODOC_URL + "/ScoDoc/api/formsemestre/1/jury",
|
# API_URL + "/formsemestre/1/jury",
|
||||||
# headers=HEADERS,
|
# headers=api_headers,
|
||||||
# verify=CHECK_CERTIFICATE,
|
# verify=CHECK_CERTIFICATE,
|
||||||
# )
|
# )
|
||||||
# assert r.status_code == 200
|
# assert r.status_code == 200
|
||||||
|
|
||||||
# semestre_index
|
|
||||||
def test_semestre_index():
|
|
||||||
|
|
||||||
ue_fields = [
|
def test_formsemestre_programme(api_headers):
|
||||||
"semestre_idx",
|
"""
|
||||||
"type",
|
Route: /formsemestre/1/programme
|
||||||
"formation_id",
|
"""
|
||||||
"ue_code",
|
|
||||||
"id",
|
|
||||||
"ects",
|
|
||||||
"acronyme",
|
|
||||||
"is_external",
|
|
||||||
"numero",
|
|
||||||
"code_apogee",
|
|
||||||
"titre",
|
|
||||||
"coefficient",
|
|
||||||
"color",
|
|
||||||
"ue_id",
|
|
||||||
]
|
|
||||||
|
|
||||||
ressource_fields = [
|
|
||||||
"heures_tp",
|
|
||||||
"code_apogee",
|
|
||||||
"titre",
|
|
||||||
"coefficient",
|
|
||||||
"module_type",
|
|
||||||
"id",
|
|
||||||
"ects",
|
|
||||||
"abbrev",
|
|
||||||
"ue_id",
|
|
||||||
"code",
|
|
||||||
"formation_id",
|
|
||||||
"heures_cours",
|
|
||||||
"matiere_id",
|
|
||||||
"heures_td",
|
|
||||||
"semestre_id",
|
|
||||||
"numero",
|
|
||||||
"module_id",
|
|
||||||
]
|
|
||||||
|
|
||||||
sae_fields = [
|
|
||||||
"heures_tp",
|
|
||||||
"code_apogee",
|
|
||||||
"titre",
|
|
||||||
"coefficient",
|
|
||||||
"module_type",
|
|
||||||
"id",
|
|
||||||
"ects",
|
|
||||||
"abbrev",
|
|
||||||
"ue_id",
|
|
||||||
"code",
|
|
||||||
"formation_id",
|
|
||||||
"heures_cours",
|
|
||||||
"matiere_id",
|
|
||||||
"heures_td",
|
|
||||||
"semestre_id",
|
|
||||||
"numero",
|
|
||||||
"module_id",
|
|
||||||
]
|
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/formsemestre/1/programme",
|
API_URL + "/formsemestre/1/programme",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 3
|
prog = r.json()
|
||||||
|
assert isinstance(prog, dict)
|
||||||
|
assert "ues" in prog
|
||||||
|
assert "modules" in prog
|
||||||
|
assert "ressources" in prog
|
||||||
|
assert "saes" in prog
|
||||||
|
assert isinstance(prog["ues"], list)
|
||||||
|
assert isinstance(prog["modules"], list)
|
||||||
|
ue = prog["ues"][0]
|
||||||
|
modules = prog["modules"]
|
||||||
|
# Il y a toujours au moins une SAE et une ressources dans notre base de test
|
||||||
|
ressource = prog["ressources"][0]
|
||||||
|
sae = prog["saes"][0]
|
||||||
|
|
||||||
ue = r.json()["ues"][0]
|
assert verify_fields(ue, UE_FIELDS)
|
||||||
ressource = r.json()["ressources"][0]
|
if len(modules) > 1:
|
||||||
sae = r.json()["saes"][0]
|
assert verify_fields(modules[0], MODIMPL_FIELDS)
|
||||||
|
assert verify_fields(ressource, MODIMPL_FIELDS)
|
||||||
fields_ue_OK = verify_fields(ue, ue_fields)
|
assert verify_fields(sae, MODIMPL_FIELDS)
|
||||||
fields_ressource_OK = verify_fields(ressource, ressource_fields)
|
|
||||||
fields_sae_OK = verify_fields(sae, sae_fields)
|
|
||||||
|
|
||||||
assert fields_ue_OK is True
|
|
||||||
assert fields_ressource_OK is True
|
|
||||||
assert fields_sae_OK is True
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""Test Logos
|
"""Test API Jurys XXX TODO A ECRIRE
|
||||||
|
|
||||||
Utilisation :
|
Utilisation :
|
||||||
créer les variables d'environnement: (indiquer les valeurs
|
créer les variables d'environnement: (indiquer les valeurs
|
||||||
@ -19,37 +19,41 @@ Utilisation :
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from tests.api.setup_test_api import SCODOC_URL, CHECK_CERTIFICATE, HEADERS
|
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
||||||
|
|
||||||
# jury_preparation
|
|
||||||
def test_jury_preparation():
|
def test_jury_preparation(api_headers):
|
||||||
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL
|
SCODOC_URL
|
||||||
+ "/ScoDoc/api/jury/formsemestre/<int:formsemestre_id>/preparation_jury",
|
+ "/ScoDoc/api/jury/formsemestre/<int:formsemestre_id>/preparation_jury",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
# jury_decisions
|
def test_jury_decisions(api_headers):
|
||||||
def test_jury_decisions():
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL
|
API_URL + "/jury/formsemestre/<int:formsemestre_id>/decisions_jury",
|
||||||
+ "/ScoDoc/api/jury/formsemestre/<int:formsemestre_id>/decisions_jury",
|
headers=api_headers,
|
||||||
headers=HEADERS,
|
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
# set_decision_jury
|
# set_decision_jury
|
||||||
def test_set_decision_jury():
|
def test_set_decision_jury(api_headers):
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL
|
SCODOC_URL
|
||||||
+ "/ScoDoc/api/jury/set_decision/etudid?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
|
+ "/ScoDoc/api/jury/set_decision/etudid?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
|
||||||
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
|
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
@ -58,7 +62,7 @@ def test_set_decision_jury():
|
|||||||
SCODOC_URL
|
SCODOC_URL
|
||||||
+ "/ScoDoc/api/jury/set_decision/nip?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
|
+ "/ScoDoc/api/jury/set_decision/nip?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
|
||||||
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
|
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
@ -67,34 +71,36 @@ def test_set_decision_jury():
|
|||||||
SCODOC_URL
|
SCODOC_URL
|
||||||
+ "/ScoDoc/api/jury/set_decision/ine?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
|
+ "/ScoDoc/api/jury/set_decision/ine?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
|
||||||
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
|
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
# annule_decision_jury
|
# def test_annule_decision_jury(api_headers):
|
||||||
def test_annule_decision_jury():
|
# """
|
||||||
r = requests.get(
|
# Route:
|
||||||
SCODOC_URL
|
# """
|
||||||
+ "/ScoDoc/api/jury/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/annule_decision",
|
# r = requests.get(
|
||||||
headers=HEADERS,
|
# SCODOC_URL
|
||||||
verify=CHECK_CERTIFICATE,
|
# + "/ScoDoc/api/jury/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/annule_decision",
|
||||||
)
|
# headers=api_headers,
|
||||||
assert r.status_code == 200
|
# verify=CHECK_CERTIFICATE,
|
||||||
|
# )
|
||||||
|
# assert r.status_code == 200
|
||||||
|
|
||||||
r = requests.get(
|
# r = requests.get(
|
||||||
SCODOC_URL
|
# SCODOC_URL
|
||||||
+ "/ScoDoc/api/jury/nip/<int:nip>/formsemestre/<int:formsemestre_id>/annule_decision",
|
# + "/ScoDoc/api/jury/nip/<int:nip>/formsemestre/<int:formsemestre_id>/annule_decision",
|
||||||
headers=HEADERS,
|
# headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
# verify=CHECK_CERTIFICATE,
|
||||||
)
|
# )
|
||||||
assert r.status_code == 200
|
# assert r.status_code == 200
|
||||||
|
|
||||||
r = requests.get(
|
# r = requests.get(
|
||||||
SCODOC_URL
|
# SCODOC_URL
|
||||||
+ "/ScoDoc/api/jury/ine/<int:ine>/formsemestre/<int:formsemestre_id>/annule_decision",
|
# + "/ScoDoc/api/jury/ine/<int:ine>/formsemestre/<int:formsemestre_id>/annule_decision",
|
||||||
headers=HEADERS,
|
# headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
# verify=CHECK_CERTIFICATE,
|
||||||
)
|
# )
|
||||||
assert r.status_code == 200
|
# assert r.status_code == 200
|
||||||
|
@ -5,11 +5,16 @@
|
|||||||
"""Exemple utilisation API ScoDoc 9 avec jeton obtenu par basic authentication
|
"""Exemple utilisation API ScoDoc 9 avec jeton obtenu par basic authentication
|
||||||
|
|
||||||
utilisation:
|
utilisation:
|
||||||
à faire fonctionner en environnment de test (FLASK_ENV=test dans le fichier .env)
|
à faire fonctionner en environnment de test (FLASK_ENV=test_api dans le fichier .env)
|
||||||
pytest tests/api/test_api_logos.py
|
pytest tests/api/test_api_logos.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from tests.api.setup_test_api import SCODOC_URL, CHECK_CERTIFICATE, HEADERS
|
|
||||||
|
# XXX TODO
|
||||||
|
# Ce test a une logique très différente des autres : A UNIFIER
|
||||||
|
|
||||||
|
|
||||||
|
from tests.api.setup_test_api import API_URL
|
||||||
|
|
||||||
from scodoc import app
|
from scodoc import app
|
||||||
from tests.unit.config_test_logos import (
|
from tests.unit.config_test_logos import (
|
||||||
@ -22,35 +27,47 @@ from tests.unit.config_test_logos import (
|
|||||||
|
|
||||||
|
|
||||||
def test_super_access(create_super_token):
|
def test_super_access(create_super_token):
|
||||||
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
dept1, dept2, dept3, token = create_super_token
|
dept1, dept2, dept3, token = create_super_token
|
||||||
HEADERS = {"Authorization": f"Bearer {token}"}
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
with app.test_client() as client:
|
with app.test_client() as client:
|
||||||
response = client.get("/ScoDoc/api/logos", headers=HEADERS)
|
response = client.get(API_URL + "/logos", headers=headers)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
|
|
||||||
|
|
||||||
def test_admin_access(create_admin_token):
|
def test_admin_access(create_admin_token):
|
||||||
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
dept1, dept2, dept3, token = create_admin_token
|
dept1, dept2, dept3, token = create_admin_token
|
||||||
headers = {"Authorization": f"Bearer {token}"}
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
with app.test_client() as client:
|
with app.test_client() as client:
|
||||||
response = client.get("/ScoDoc/api/logos", headers=headers)
|
response = client.get(API_URL + "/logos", headers=headers)
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_lambda_access(create_lambda_token):
|
def test_lambda_access(create_lambda_token):
|
||||||
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
dept1, dept2, dept3, token = create_lambda_token
|
dept1, dept2, dept3, token = create_lambda_token
|
||||||
headers = {"Authorization": f"Bearer {token}"}
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
with app.test_client() as client:
|
with app.test_client() as client:
|
||||||
response = client.get("/ScoDoc/api/logos", headers=headers)
|
response = client.get(API_URL + "/logos", headers=headers)
|
||||||
assert response.status_code == 401
|
assert response.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_initial_with_header_and_footer(create_super_token):
|
def test_initial_with_header_and_footer(create_super_token):
|
||||||
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
dept1, dept2, dept3, token = create_super_token
|
dept1, dept2, dept3, token = create_super_token
|
||||||
headers = {"Authorization": f"Bearer {token}"}
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
with app.test_client() as client:
|
with app.test_client() as client:
|
||||||
response = client.get("/ScoDoc/api/logos", headers=headers)
|
response = client.get(API_URL + "/logos", headers=headers)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
assert len(response.json) == 7
|
assert len(response.json) == 7
|
||||||
|
@ -19,12 +19,14 @@ Utilisation :
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from tests.api.setup_test_api import SCODOC_URL, CHECK_CERTIFICATE, HEADERS
|
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
|
||||||
from tests.api.tools_test_api import verify_fields
|
from tests.api.tools_test_api import verify_fields
|
||||||
|
|
||||||
|
|
||||||
# partition
|
def test_partition(api_headers):
|
||||||
def test_partition():
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
fields = [
|
fields = [
|
||||||
"partition_id",
|
"partition_id",
|
||||||
"id",
|
"id",
|
||||||
@ -36,23 +38,22 @@ def test_partition():
|
|||||||
]
|
]
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/partitions/1",
|
API_URL + "/partitions/1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
partition = r.json()[0]
|
|
||||||
|
|
||||||
fields_OK = verify_fields(partition, fields)
|
|
||||||
|
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 2
|
partitions = r.json()
|
||||||
assert fields_OK is True
|
assert len(partitions) == 1
|
||||||
|
partition = partitions[0]
|
||||||
|
fields_ok = verify_fields(partition, fields)
|
||||||
|
assert fields_ok is True
|
||||||
|
|
||||||
|
|
||||||
# etud_in_group
|
def test_etud_in_group(api_headers):
|
||||||
def test_etud_in_group():
|
"""
|
||||||
|
Route:
|
||||||
|
"""
|
||||||
fields = [
|
fields = [
|
||||||
"etudid",
|
"etudid",
|
||||||
"id",
|
"id",
|
||||||
@ -92,33 +93,36 @@ def test_etud_in_group():
|
|||||||
]
|
]
|
||||||
|
|
||||||
r = requests.get(
|
r = requests.get(
|
||||||
SCODOC_URL + "/ScoDoc/api/partitions/groups/1",
|
API_URL + "/partitions/groups/1",
|
||||||
headers=HEADERS,
|
headers=api_headers,
|
||||||
verify=CHECK_CERTIFICATE,
|
verify=CHECK_CERTIFICATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
etu = r.json()[0]
|
etu = r.json()[0]
|
||||||
|
|
||||||
fields_OK = verify_fields(etu, fields)
|
fields_ok = verify_fields(etu, fields)
|
||||||
|
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert len(r.json()) == 16
|
assert len(r.json()) == 16
|
||||||
assert fields_OK is True
|
assert fields_ok is True
|
||||||
# r = requests.get(
|
# r = requests.get(
|
||||||
# SCODOC_URL + "/ScoDoc/api/partitions/groups/1/etat/<string:etat>",
|
# API_URL + "/partitions/groups/1/etat/<string:etat>",
|
||||||
# headers=HEADERS,
|
# headers=api_headers,
|
||||||
# verify=CHECK_CERTIFICATE,
|
# verify=CHECK_CERTIFICATE,
|
||||||
# )
|
# )
|
||||||
# assert r.status_code == 200
|
# assert r.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
# # set_groups
|
# # set_groups
|
||||||
# def test_set_groups():
|
# def test_set_groups(api_headers):
|
||||||
|
# """
|
||||||
|
# Route:
|
||||||
|
# """
|
||||||
# r = requests.get(
|
# r = requests.get(
|
||||||
# SCODOC_URL
|
# SCODOC_URL
|
||||||
# + "/partitions/set_groups/partition/<int:partition_id>/groups/<string:groups_id>"
|
# + "/partitions/set_groups/partition/<int:partition_id>/groups/<string:groups_id>"
|
||||||
# "/delete/<string:groups_to_delete>/create/<string:groups_to_create>",
|
# "/delete/<string:groups_to_delete>/create/<string:groups_to_create>",
|
||||||
# headers=HEADERS,
|
# headers=api_headers,
|
||||||
# verify=CHECK_CERTIFICATE,
|
# verify=CHECK_CERTIFICATE,
|
||||||
# )
|
# )
|
||||||
# assert r.status_code == 200
|
# assert r.status_code == 200
|
||||||
|
100
tests/api/test_api_permissions.py
Normal file
100
tests/api/test_api_permissions.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""Test permissions
|
||||||
|
|
||||||
|
On a deux utilisateurs dans la base test API:
|
||||||
|
- "test", avec le rôle LecteurAPI qui a APIView,
|
||||||
|
- et "other", qui n'a aucune permission.
|
||||||
|
|
||||||
|
|
||||||
|
Lancer :
|
||||||
|
pytest tests/api/test_api_permissions.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
import flask
|
||||||
|
from tests.api.setup_test_api import API_URL, SCODOC_URL, CHECK_CERTIFICATE, api_headers
|
||||||
|
from tests.api.tools_test_api import verify_fields
|
||||||
|
|
||||||
|
from app import create_app
|
||||||
|
from config import RunningConfig
|
||||||
|
|
||||||
|
|
||||||
|
def test_permissions(api_headers):
|
||||||
|
"""
|
||||||
|
vérification de la permissions APIView et du non accès sans role
|
||||||
|
de toutes les routes de l'API
|
||||||
|
"""
|
||||||
|
# Ce test va récupérer toutes les routes de l'API
|
||||||
|
app = create_app(RunningConfig)
|
||||||
|
assert app
|
||||||
|
# Les routes de l'API avec GET, excluant les logos pour le momeent XXX
|
||||||
|
api_rules = [
|
||||||
|
r
|
||||||
|
for r in app.url_map.iter_rules()
|
||||||
|
if str(r).startswith("/ScoDoc/api")
|
||||||
|
and not "logo" in str(r) # ignore logos
|
||||||
|
and "GET" in r.methods
|
||||||
|
]
|
||||||
|
assert len(api_rules) > 0
|
||||||
|
args = {
|
||||||
|
"etudid": 1,
|
||||||
|
# "date_debut":
|
||||||
|
# "date_fin":
|
||||||
|
"dept": "TAPI",
|
||||||
|
"dept_ident": "TAPI",
|
||||||
|
"dept_id": 1,
|
||||||
|
"etape_apo": "???",
|
||||||
|
"etat": "I",
|
||||||
|
"evaluation_id": 1,
|
||||||
|
"formation_id": 1,
|
||||||
|
"formsemestre_id": 1,
|
||||||
|
"group_id": 1,
|
||||||
|
"ine": "1",
|
||||||
|
"module_id": 1,
|
||||||
|
"moduleimpl_id": 1,
|
||||||
|
"nip": 1,
|
||||||
|
"partition_id": 1,
|
||||||
|
}
|
||||||
|
for rule in api_rules:
|
||||||
|
path = rule.build(args)[1]
|
||||||
|
if not "GET" in rule.methods:
|
||||||
|
# skip all POST routes
|
||||||
|
continue
|
||||||
|
r = requests.get(
|
||||||
|
SCODOC_URL + path,
|
||||||
|
headers=api_headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
# Même chose sans le jeton:
|
||||||
|
for rule in api_rules:
|
||||||
|
path = rule.build(args)[1]
|
||||||
|
if not "GET" in rule.methods:
|
||||||
|
# skip all POST routes
|
||||||
|
continue
|
||||||
|
r = requests.get(
|
||||||
|
SCODOC_URL + path,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 401
|
||||||
|
|
||||||
|
# Demande un jeton pour "other"
|
||||||
|
r = requests.post(API_URL + "/tokens", auth=("other", "other"))
|
||||||
|
assert r.status_code == 200
|
||||||
|
token = r.json()["token"]
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
# Vérifie que tout est interdit
|
||||||
|
for rule in api_rules:
|
||||||
|
path = rule.build(args)[1]
|
||||||
|
if not "GET" in rule.methods:
|
||||||
|
# skip all POST routes
|
||||||
|
continue
|
||||||
|
r = requests.get(
|
||||||
|
SCODOC_URL + path,
|
||||||
|
headers=headers,
|
||||||
|
verify=CHECK_CERTIFICATE,
|
||||||
|
)
|
||||||
|
assert r.status_code == 403
|
@ -1,13 +1,133 @@
|
|||||||
def verify_fields(json_response, fields):
|
"""Utilitaires pour les tests de l'API
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def verify_fields(json_response: dict, expected_fields: set) -> bool:
|
||||||
"""
|
"""
|
||||||
Vérifie si les champs de la réponse json sont corrects
|
Vérifie si les champs attendu de la réponse json sont présents
|
||||||
|
|
||||||
json_response : la réponse de la requête
|
json_response : la réponse de la requête
|
||||||
fields : une liste avec l'ensemble des champs à vérifier
|
expected_fields : ensemble des champs à vérifier
|
||||||
|
|
||||||
Retourne True ou False
|
Retourne True ou False
|
||||||
"""
|
"""
|
||||||
for field in json_response:
|
return all(field in json_response for field in expected_fields)
|
||||||
if field not in fields:
|
|
||||||
return False
|
|
||||||
return True
|
ETUD_FIELDS = {
|
||||||
|
"boursier",
|
||||||
|
"civilite",
|
||||||
|
"code_ine",
|
||||||
|
"code_nip",
|
||||||
|
"codepostaldomicile",
|
||||||
|
"date_naissance",
|
||||||
|
"dept_acronym",
|
||||||
|
"dept_id",
|
||||||
|
"dept_naissance",
|
||||||
|
"description",
|
||||||
|
"domicile",
|
||||||
|
"email",
|
||||||
|
"emailperso",
|
||||||
|
"etudid",
|
||||||
|
"id",
|
||||||
|
"lieu_naissance",
|
||||||
|
"nationalite",
|
||||||
|
"nom",
|
||||||
|
"nomprenom",
|
||||||
|
"paysdomicile",
|
||||||
|
"prenom",
|
||||||
|
"telephone",
|
||||||
|
"telephonemobile",
|
||||||
|
"typeadresse",
|
||||||
|
"villedomicile",
|
||||||
|
}
|
||||||
|
|
||||||
|
FORMATION_FIELDS = {
|
||||||
|
"id",
|
||||||
|
"acronyme",
|
||||||
|
"titre_officiel",
|
||||||
|
"formation_code",
|
||||||
|
"code_specialite",
|
||||||
|
"dept_id",
|
||||||
|
"titre",
|
||||||
|
"version",
|
||||||
|
"type_parcours",
|
||||||
|
"referentiel_competence_id",
|
||||||
|
"formation_id",
|
||||||
|
}
|
||||||
|
|
||||||
|
FSEM_FIELDS = {
|
||||||
|
"block_moyennes",
|
||||||
|
"bul_bgcolor",
|
||||||
|
"bul_hide_xml",
|
||||||
|
"date_debut_iso",
|
||||||
|
"date_debut",
|
||||||
|
"date_fin_iso",
|
||||||
|
"date_fin",
|
||||||
|
"dept_id",
|
||||||
|
"elt_annee_apo",
|
||||||
|
"elt_sem_apo",
|
||||||
|
"ens_can_edit_eval",
|
||||||
|
"etat",
|
||||||
|
"formation_id",
|
||||||
|
"formsemestre_id",
|
||||||
|
"gestion_compensation",
|
||||||
|
"gestion_semestrielle",
|
||||||
|
"id",
|
||||||
|
"modalite",
|
||||||
|
"resp_can_change_ens",
|
||||||
|
"resp_can_edit",
|
||||||
|
"responsables",
|
||||||
|
"semestre_id",
|
||||||
|
"titre_formation",
|
||||||
|
"titre_num",
|
||||||
|
"titre",
|
||||||
|
}
|
||||||
|
|
||||||
|
MODIMPL_FIELDS = {
|
||||||
|
"id",
|
||||||
|
"formsemestre_id",
|
||||||
|
"computation_expr",
|
||||||
|
"module_id",
|
||||||
|
"responsable_id",
|
||||||
|
"moduleimpl_id",
|
||||||
|
"ens",
|
||||||
|
"module",
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_FIELDS = {
|
||||||
|
"heures_tp",
|
||||||
|
"code_apogee",
|
||||||
|
"titre",
|
||||||
|
"coefficient",
|
||||||
|
"module_type",
|
||||||
|
"id",
|
||||||
|
"ects",
|
||||||
|
"abbrev",
|
||||||
|
"ue_id",
|
||||||
|
"code",
|
||||||
|
"formation_id",
|
||||||
|
"heures_cours",
|
||||||
|
"matiere_id",
|
||||||
|
"heures_td",
|
||||||
|
"semestre_id",
|
||||||
|
"numero",
|
||||||
|
"module_id",
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_FIELDS = {
|
||||||
|
"semestre_idx",
|
||||||
|
"type",
|
||||||
|
"formation_id",
|
||||||
|
"ue_code",
|
||||||
|
"id",
|
||||||
|
"ects",
|
||||||
|
"acronyme",
|
||||||
|
"is_external",
|
||||||
|
"numero",
|
||||||
|
"code_apogee",
|
||||||
|
"titre",
|
||||||
|
"coefficient",
|
||||||
|
"color",
|
||||||
|
"ue_id",
|
||||||
|
}
|
||||||
|
@ -9,8 +9,16 @@ die() {
|
|||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
[ $# = 1 ] || die "Usage $0 db_name"
|
[ $# = 1 ] || [ $# = 2 ] || die "Usage $0 [--drop] db_name"
|
||||||
db_name="$1"
|
|
||||||
|
if [ "$1" = "--drop" ]
|
||||||
|
then
|
||||||
|
db_name="$2"
|
||||||
|
echo "Dropping database $db_name..."
|
||||||
|
dropdb "$db_name"
|
||||||
|
else
|
||||||
|
db_name="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
# Le répertoire de ce script:
|
# Le répertoire de ce script:
|
||||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||||
|
@ -5,60 +5,59 @@
|
|||||||
Création des départements, formations, semestres, étudiants, groupes...
|
Création des départements, formations, semestres, étudiants, groupes...
|
||||||
|
|
||||||
utilisation:
|
utilisation:
|
||||||
1) modifier le .env pour indiquer
|
1) modifier /opt/scodoc/.env pour indiquer
|
||||||
SCODOC_DATABASE_URI="postgresql:///SCO_TEST_API"
|
FLASK_ENV=test_api
|
||||||
|
FLASK_DEBUG=1
|
||||||
|
|
||||||
2) En tant qu'utilisateur scodoc, lancer:
|
2) En tant qu'utilisateur scodoc, lancer:
|
||||||
tools/create_database.sh SCO_TEST_API
|
tools/create_database.sh SCODOC_TEST_API
|
||||||
flask db upgrade
|
flask db upgrade
|
||||||
flask sco-db-init --erase
|
flask sco-db-init --erase
|
||||||
flask init-test-database
|
flask init-test-database
|
||||||
flask user-role -a Admin -d TAPI test
|
|
||||||
flask user-password test
|
|
||||||
flask create-role APIUserViewer
|
|
||||||
flask edit-role APIUserViewer -a APIView
|
|
||||||
flask user-role test -a APIUserViewer
|
|
||||||
|
|
||||||
3) relancer ScoDoc:
|
3) relancer ScoDoc:
|
||||||
flask run --host 0.0.0.0
|
flask run --host 0.0.0.0
|
||||||
|
|
||||||
4) lancer client de test (ou vérifier dans le navigateur)
|
4) lancer client de test
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
import random
|
import random
|
||||||
|
import sys
|
||||||
|
|
||||||
random.seed(12345678) # tests reproductibles
|
from app.auth.models import Role, User
|
||||||
|
|
||||||
from flask_login import login_user
|
|
||||||
|
|
||||||
from app import auth
|
|
||||||
from app import models
|
from app import models
|
||||||
|
from app.models import Departement, Formation, FormSemestre, Identite
|
||||||
from app import db
|
from app import db
|
||||||
from app.scodoc import (
|
from app.scodoc import (
|
||||||
|
sco_cache,
|
||||||
|
sco_evaluation_db,
|
||||||
sco_formations,
|
sco_formations,
|
||||||
sco_formsemestre,
|
|
||||||
sco_formsemestre_inscriptions,
|
sco_formsemestre_inscriptions,
|
||||||
sco_groups,
|
sco_groups,
|
||||||
)
|
)
|
||||||
|
from app.scodoc.sco_permissions import Permission
|
||||||
from tools.fakeportal.gen_nomprenoms import nomprenom
|
from tools.fakeportal.gen_nomprenoms import nomprenom
|
||||||
|
|
||||||
|
random.seed(12345678) # tests reproductibles
|
||||||
|
|
||||||
# La formation à utiliser:
|
# La formation à utiliser:
|
||||||
FORMATION_XML_FILENAME = "tests/ressources/formations/scodoc_formation_RT_BUT_RT_v1.xml"
|
FORMATION_XML_FILENAME = "tests/ressources/formations/scodoc_formation_RT_BUT_RT_v1.xml"
|
||||||
|
|
||||||
|
|
||||||
def init_departement(acronym):
|
def init_departement(acronym: str) -> Departement:
|
||||||
"Create dept, and switch context into it."
|
"Create dept, and switch context into it."
|
||||||
import app as mapp
|
import app as mapp
|
||||||
|
|
||||||
dept = models.Departement(acronym=acronym)
|
dept = Departement(acronym=acronym)
|
||||||
db.session.add(dept)
|
db.session.add(dept)
|
||||||
mapp.set_sco_dept(acronym)
|
mapp.set_sco_dept(acronym)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return dept
|
return dept
|
||||||
|
|
||||||
|
|
||||||
def import_formation() -> models.Formation:
|
def import_formation() -> Formation:
|
||||||
"""Import formation from XML.
|
"""Import formation from XML.
|
||||||
Returns formation_id
|
Returns formation_id
|
||||||
"""
|
"""
|
||||||
@ -66,28 +65,48 @@ def import_formation() -> models.Formation:
|
|||||||
doc = f.read()
|
doc = f.read()
|
||||||
# --- Création de la formation
|
# --- Création de la formation
|
||||||
f = sco_formations.formation_import_xml(doc)
|
f = sco_formations.formation_import_xml(doc)
|
||||||
return models.Formation.query.get(f[0])
|
return Formation.query.get(f[0])
|
||||||
|
|
||||||
|
|
||||||
def create_user(dept):
|
def create_users(dept: Departement) -> tuple:
|
||||||
"""créé les utilisaterurs nécessaires aux tests"""
|
"""créé les utilisateurs nécessaires aux tests"""
|
||||||
user = auth.models.User(
|
# Un utilisateur "test" (passwd test) pouvant lire l'API
|
||||||
user_name="test", nom="Doe", prenom="John", dept=dept.acronym
|
user = User(user_name="test", nom="Doe", prenom="John", dept=dept.acronym)
|
||||||
)
|
user.set_password("test")
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
|
|
||||||
|
# Le rôle standard LecteurAPI existe déjà
|
||||||
|
role = Role.query.filter_by(name="LecteurAPI").first()
|
||||||
|
if role is None:
|
||||||
|
print("Erreur: rôle LecteurAPI non existant")
|
||||||
|
sys.exit(1)
|
||||||
|
perm_api_view = Permission.get_by_name("APIView")
|
||||||
|
role.add_permission(perm_api_view)
|
||||||
|
db.session.add(role)
|
||||||
|
|
||||||
|
user.add_role(role, None)
|
||||||
|
|
||||||
|
# Un utilisateur "other" n'ayant aucune permission sur l'API
|
||||||
|
other = User(user_name="other", nom="Sans", prenom="Permission", dept=dept.acronym)
|
||||||
|
other.set_password("other")
|
||||||
|
db.session.add(other)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return user
|
return user, other
|
||||||
|
|
||||||
|
|
||||||
def create_fake_etud(dept):
|
def create_fake_etud(dept: Departement) -> Identite:
|
||||||
"""Créé un faux étudiant et l'insère dans la base"""
|
"""Créé un faux étudiant et l'insère dans la base."""
|
||||||
civilite = random.choice(("M", "F", "X"))
|
civilite = random.choice(("M", "F", "X"))
|
||||||
nom, prenom = nomprenom(civilite)
|
nom, prenom = nomprenom(civilite)
|
||||||
etud = models.Identite(civilite=civilite, nom=nom, prenom=prenom, dept_id=dept.id)
|
etud: Identite = Identite(
|
||||||
|
civilite=civilite, nom=nom, prenom=prenom, dept_id=dept.id
|
||||||
|
)
|
||||||
db.session.add(etud)
|
db.session.add(etud)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
etud.code_nip = etud.id
|
# créé un étudiant sur deux avec un NIP et INE alphanumérique
|
||||||
etud.code_ine = etud.id
|
etud.code_nip = f"{etud.id}" if (etud.id % 2) else f"NIP{etud.id}"
|
||||||
|
etud.code_ine = f"INE{etud.id}" if (etud.id % 2) else f"{etud.id}"
|
||||||
db.session.add(etud)
|
db.session.add(etud)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
adresse = models.Adresse(
|
adresse = models.Adresse(
|
||||||
@ -100,14 +119,18 @@ def create_fake_etud(dept):
|
|||||||
return etud
|
return etud
|
||||||
|
|
||||||
|
|
||||||
def create_etuds(dept, nb=16):
|
def create_etuds(dept: Departement, nb=16) -> list:
|
||||||
"create nb etuds"
|
"create nb etuds"
|
||||||
return [create_fake_etud(dept) for _ in range(nb)]
|
return [create_fake_etud(dept) for _ in range(nb)]
|
||||||
|
|
||||||
|
|
||||||
def create_formsemestre(formation, user, semestre_idx=1):
|
def create_formsemestre(
|
||||||
"""Create formsemestre and moduleimpls"""
|
formation: Formation, responsable: User, semestre_idx=1
|
||||||
formsemestre = models.FormSemestre(
|
) -> FormSemestre:
|
||||||
|
"""Create formsemestre and moduleimpls
|
||||||
|
responsable: resp. du formsemestre
|
||||||
|
"""
|
||||||
|
formsemestre = FormSemestre(
|
||||||
dept_id=formation.dept_id,
|
dept_id=formation.dept_id,
|
||||||
semestre_id=semestre_idx,
|
semestre_id=semestre_idx,
|
||||||
titre="Semestre test",
|
titre="Semestre test",
|
||||||
@ -121,7 +144,9 @@ def create_formsemestre(formation, user, semestre_idx=1):
|
|||||||
# Crée un modulimpl par module de ce semestre:
|
# Crée un modulimpl par module de ce semestre:
|
||||||
for module in formation.modules.filter_by(semestre_id=semestre_idx):
|
for module in formation.modules.filter_by(semestre_id=semestre_idx):
|
||||||
modimpl = models.ModuleImpl(
|
modimpl = models.ModuleImpl(
|
||||||
module_id=module.id, formsemestre_id=formsemestre.id, responsable_id=user.id
|
module_id=module.id,
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
responsable_id=responsable.id,
|
||||||
)
|
)
|
||||||
db.session.add(modimpl)
|
db.session.add(modimpl)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -132,7 +157,7 @@ def create_formsemestre(formation, user, semestre_idx=1):
|
|||||||
return formsemestre
|
return formsemestre
|
||||||
|
|
||||||
|
|
||||||
def inscrit_etudiants(etuds, formsemestre):
|
def inscrit_etudiants(etuds: list, formsemestre: FormSemestre):
|
||||||
"""Inscrit les etudiants aux semestres et à tous ses modules"""
|
"""Inscrit les etudiants aux semestres et à tous ses modules"""
|
||||||
for etud in etuds:
|
for etud in etuds:
|
||||||
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
|
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
|
||||||
@ -144,14 +169,38 @@ def inscrit_etudiants(etuds, formsemestre):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def init_test_database():
|
def create_evaluations(formsemestre: FormSemestre):
|
||||||
|
"creation d'une evaluation dans cahque modimpl du semestre"
|
||||||
|
for modimpl in formsemestre.modimpls:
|
||||||
|
args = {
|
||||||
|
"moduleimpl_id": modimpl.id,
|
||||||
|
"jour": None,
|
||||||
|
"heure_debut": "8h00",
|
||||||
|
"heure_fin": "9h00",
|
||||||
|
"description": None,
|
||||||
|
"note_max": 20,
|
||||||
|
"coefficient": 1.0,
|
||||||
|
"visibulletin": True,
|
||||||
|
"publish_incomplete": True,
|
||||||
|
"evaluation_type": None,
|
||||||
|
"numero": None,
|
||||||
|
}
|
||||||
|
evaluation_id = sco_evaluation_db.do_evaluation_create(**args)
|
||||||
|
|
||||||
|
|
||||||
|
def init_test_database():
|
||||||
|
"""Appelé par la commande `flask init-test-database`
|
||||||
|
|
||||||
|
Création d'un département et de son contenu pour les tests
|
||||||
|
"""
|
||||||
dept = init_departement("TAPI")
|
dept = init_departement("TAPI")
|
||||||
user = create_user(dept)
|
user_lecteur, user_autre = create_users(dept)
|
||||||
etuds = create_etuds(dept)
|
with sco_cache.DeferredSemCacheManager():
|
||||||
formation = import_formation()
|
etuds = create_etuds(dept)
|
||||||
formsemestre = create_formsemestre(formation, user)
|
formation = import_formation()
|
||||||
inscrit_etudiants(etuds, formsemestre)
|
formsemestre = create_formsemestre(formation, user_lecteur)
|
||||||
|
create_evaluations(formsemestre)
|
||||||
|
inscrit_etudiants(etuds, formsemestre)
|
||||||
# à compléter
|
# à compléter
|
||||||
# - groupes
|
# - groupes
|
||||||
# - absences
|
# - absences
|
||||||
|
Loading…
Reference in New Issue
Block a user