1
0
forked from ScoDoc/ScoDoc

Merge branch 'new_api_leonard' of https://scodoc.org/git/viennet/ScoDoc

This commit is contained in:
Emmanuel Viennet 2022-07-22 16:49:50 +02:00
commit adc086f83c
38 changed files with 4140 additions and 1160 deletions

View File

@ -1,20 +1,27 @@
#################################################### Absences ######################################################### ##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""ScoDoc 9 API : Absences
"""
from flask import jsonify 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_auth, token_permission_required from app.api.auth import permission_required_api
from app.models import Identite 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"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def absences(etudid: 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é
@ -51,15 +58,14 @@ def absences(etudid: int = None):
) )
# Absences de l'étudiant # Absences de l'étudiant
ndb.open_db_connection() ndb.open_db_connection()
absences = sco_abs.list_abs_date(etud.id) abs_list = sco_abs.list_abs_date(etud.id)
for absence in absences: for absence in abs_list:
absence["jour"] = absence["jour"].isoformat() absence["jour"] = absence["jour"].isoformat()
return jsonify(absences) return jsonify(abs_list)
@bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"]) @bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def absences_just(etudid: 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é
@ -106,40 +112,152 @@ def absences_just(etudid: int = None):
return jsonify(abs_just) return jsonify(abs_just)
# 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"],
)
@permission_required_api(Permission.ScoView, Permission.APIView)
def abs_groupe_etat(group_id: int, date_debut=None, date_fin=None):
"""
Liste des absences d'un groupe (possibilité de choisir entre deux dates)
group_id = l'id du groupe
date_debut = None par défaut, sinon la date ISO du début de notre filtre
date_fin = None par défaut, sinon la date ISO de la fin de notre filtre
Exemple de résultat :
[
{
"etudid": 1,
"list_abs": []
},
{
"etudid": 2,
"list_abs": [
{
"jour": "Fri, 15 Apr 2022 00:00:00 GMT",
"matin": true,
"estabs": true,
"estjust": true,
"description": "",
"begin": "2022-04-15 08:00:00",
"end": "2022-04-15 11:59:59"
},
{
"jour": "Fri, 15 Apr 2022 00:00:00 GMT",
"matin": false,
"estabs": true,
"estjust": false,
"description": "",
"begin": "2022-04-15 12:00:00",
"end": "2022-04-15 17:59:59"
},
]
},
...
]
"""
members = get_group_members(group_id)
data = []
# Filtre entre les deux dates renseignées
for member in members:
absence = {
"etudid": member["etudid"],
"list_abs": sco_abs.list_abs_date(member["etudid"], date_debut, date_fin),
}
data.append(absence)
return jsonify(data)
# XXX TODO EV: A REVOIR (data json dans le POST + modifier les routes)
# @bp.route( # @bp.route(
# "/absences/abs_group_etat/<int:group_id>", # "/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs",
# methods=["GET"], # methods=["POST"],
# defaults={"just_or_not": 0},
# ) # )
# @bp.route( # @bp.route(
# "/absences/abs_group_etat/group_id/<int:group_id>/date_debut/<string:date_debut>/date_fin/<string:date_fin>", # "/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs/only_not_just",
# methods=["GET"], # methods=["POST"],
# defaults={"just_or_not": 1},
# )
# @bp.route(
# "/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs/only_just",
# methods=["POST"],
# defaults={"just_or_not": 2},
# ) # )
# @token_auth.login_required # @token_auth.login_required
# @token_permission_required(Permission.APIView) # @token_permission_required(Permission.APIAbsChange)
# def abs_groupe_etat( # XXX A REVOIR XXX # def reset_etud_abs(etudid: int, list_abs: str, just_or_not: int = 0):
# group_id: int, date_debut, date_fin, with_boursier=True, format="html"
# ):
# """ # """
# Liste des absences d'un ou plusieurs groupes entre deux dates # Set la liste des absences d'un étudiant sur tout un semestre.
# (les absences existant pour cet étudiant sur cette période sont effacées)
# etudid : l'id d'un étudiant
# list_abs : json d'absences
# just_or_not : 0 (pour les absences justifiées et non justifiées),
# 1 (pour les absences justifiées),
# 2 (pour les absences non justifiées)
# """ # """
# return error_response(501, message="Not implemented") # # Toutes les absences
# if just_or_not == 0:
# # suppression des absences et justificatif déjà existant pour éviter les doublons
# for abs in list_abs:
# # Récupération de la date au format iso
# jour = abs["jour"].isoformat()
# if abs["matin"] is True:
# annule_absence(etudid, jour, True)
# annule_justif(etudid, jour, True)
# else:
# annule_absence(etudid, jour, False)
# annule_justif(etudid, jour, False)
# # Fonction utilisée : app.scodoc.sco_groups.get_group_members() et app.scodoc.sco_abs.list_abs_date() # # Ajout de la liste d'absences en base
# add_abslist(list_abs)
# try: # # Uniquement les absences justifiées
# # Utilisation de la fonction get_group_members # elif just_or_not == 1:
# members = get_group_members(group_id) # list_abs_not_just = []
# except ValueError: # # Trie des absences justifiées
# return error_response( # for abs in list_abs:
# 404, message="La requête ne peut être traitée en létat actuel" # if abs["estjust"] is False:
# ) # list_abs_not_just.append(abs)
# # suppression des absences et justificatif déjà existant pour éviter les doublons
# for abs in list_abs:
# # Récupération de la date au format iso
# jour = abs["jour"].isoformat()
# if abs["matin"] is True:
# annule_absence(etudid, jour, True)
# annule_justif(etudid, jour, True)
# else:
# annule_absence(etudid, jour, False)
# annule_justif(etudid, jour, False)
# data = [] # # Ajout de la liste d'absences en base
# # Filtre entre les deux dates renseignées # add_abslist(list_abs_not_just)
# 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) # # Uniquement les absences non justifiées
# return error_response(501, message="Not implemented") # elif just_or_not == 2:
# list_abs_just = []
# # Trie des absences non justifiées
# for abs in list_abs:
# if abs["estjust"] is True:
# list_abs_just.append(abs)
# # suppression des absences et justificatif déjà existant pour éviter les doublons
# for abs in list_abs:
# # Récupération de la date au format iso
# jour = abs["jour"].isoformat()
# if abs["matin"] is True:
# annule_absence(etudid, jour, True)
# annule_justif(etudid, jour, True)
# else:
# annule_absence(etudid, jour, False)
# annule_justif(etudid, jour, False)
# # Ajout de la liste d'absences en base
# add_abslist(list_abs_just)

View File

@ -26,10 +26,9 @@
from functools import wraps from functools import wraps
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 flask_login import current_user
from app import log from app import log
from app.auth.models import User from app.auth.models import User
@ -57,7 +56,10 @@ def basic_auth_error(status):
@token_auth.verify_token @token_auth.verify_token
def verify_token(token) -> User: def verify_token(token) -> User:
"Retrouve l'utilisateur à partir du jeton" """Retrouve l'utilisateur à partir du jeton.
Si la requête n'a pas de jeton, token == "".
"""
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
@ -65,7 +67,7 @@ def verify_token(token) -> User:
@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." "Réponse en cas d'erreur d'auth."
return error_response(status) return error_response(status)
@ -75,7 +77,7 @@ def get_user_roles(user):
def token_permission_required(permission): def token_permission_required(permission):
"Décorateur pour les fontions de l'API ScoDoc" "Décorateur pour les fonctions de l'API ScoDoc"
def decorator(f): def decorator(f):
@wraps(f) @wraps(f)
@ -84,13 +86,39 @@ def token_permission_required(permission):
current_user = basic_auth.current_user() current_user = basic_auth.current_user()
if not current_user or not current_user.has_permission(permission, None): if not current_user or not current_user.has_permission(permission, None):
if current_user: if current_user:
log(f"API permission denied (user {current_user})") message = f"API permission denied (user {current_user})"
else: else:
log("API permission denied (no user supplied)") message = f"API permission denied (no user supplied)"
abort(403) log(message)
# raise werkzeug.exceptions.Forbidden(description=message)
return error_response(403, message=None)
return f(*args, **kwargs) return f(*args, **kwargs)
# return decorated_function(token_auth.login_required()) # return decorated_function(token_auth.login_required())
return decorated_function return decorated_function
return decorator return decorator
def permission_required_api(permission_web, permission_api):
"""Décorateur pour les fonctions de l'API accessibles en mode jeton
ou en mode web.
Si cookie d'authentification web, utilise pour se logger et calculer les
permissions.
Sinon, tente le jeton jwt.
"""
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
scodoc_dept = getattr(g, "scodoc_dept", None)
if not current_user.has_permission(permission_web, scodoc_dept):
# try API
return token_auth.login_required(
token_permission_required(permission_api)(f)
)(*args, **kwargs)
return f(*args, **kwargs)
return decorated_function
return decorator

View File

@ -5,7 +5,7 @@ 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.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.models import Departement, FormSemestre from app.models import Departement, FormSemestre
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -21,20 +21,25 @@ def get_departement(dept_ident: str) -> Departement:
return Departement.query.get_or_404(dept_id) return Departement.query.get_or_404(dept_id)
@bp.route("/departements", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def departements():
"""Liste les départements"""
return jsonify([dept.to_dict() for dept in Departement.query])
@bp.route("/departements_ids", methods=["GET"]) @bp.route("/departements_ids", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def departements_ids(): def departements_ids():
"""Liste des ids de départements""" """Liste des ids de départements"""
return jsonify([dept.id for dept in Departement.query]) return jsonify([dept.id for dept in Departement.query])
@bp.route("/departement/<string:dept_ident>", methods=["GET"]) @bp.route("/departement/<string:acronym>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def departement(acronym: str):
def departement(dept_ident: str):
""" """
Info sur un département. Accès par id ou acronyme. Info sur un département. Accès par acronyme.
Exemple de résultat : Exemple de résultat :
{ {
@ -45,27 +50,27 @@ def departement(dept_ident: str):
"date_creation": "Fri, 15 Apr 2022 12:19:28 GMT" "date_creation": "Fri, 15 Apr 2022 12:19:28 GMT"
} }
""" """
dept = get_departement(dept_ident) dept = Departement.query.filter_by(acronym=acronym).first_or_404()
return jsonify(dept.to_dict()) return jsonify(dept.to_dict())
@bp.route("/departements", methods=["GET"]) @bp.route("/departement/id/<int:dept_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def departement_by_id(dept_id: int):
def departements(): """
"""Liste les départements""" Info sur un département. Accès par id.
return jsonify([dept.to_dict() for dept in Departement.query]) """
dept = Departement.query.get_or_404(dept_id)
return jsonify(dept.to_dict())
@bp.route("/departement/<string:dept_ident>/etudiants", methods=["GET"]) @bp.route("/departement/<string:acronym>/etudiants", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def dept_etudiants(acronym: str):
def list_etudiants(dept_ident: str):
""" """
Retourne la liste des étudiants d'un département Retourne la liste des étudiants d'un département
dept: l'acronym d'un département acronym: l'acronyme d'un département
formsemestre_id: l'id d'un formesemestre
Exemple de résultat : Exemple de résultat :
[ [
@ -83,30 +88,41 @@ def list_etudiants(dept_ident: str):
... ...
] ]
""" """
# Le département, spécifié par un id ou un acronyme dept = Departement.query.filter_by(acronym=acronym).first_or_404()
dept = get_departement(dept_ident)
return jsonify([etud.to_dict_short() for etud in dept.etudiants]) return jsonify([etud.to_dict_short() for etud in dept.etudiants])
@bp.route("/departement/<string:dept_ident>/formsemestres_ids", methods=["GET"]) @bp.route("/departement/id/<int:dept_id>/etudiants", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def dept_etudiants_by_id(dept_id: int):
def formsemestres_ids(dept_ident: str): """
Retourne la liste des étudiants d'un département d'id donné.
"""
dept = Departement.query.get_or_404(dept_id)
return jsonify([etud.to_dict_short() for etud in dept.etudiants])
@bp.route("/departement/<string:acronym>/formsemestres_ids", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def dept_formsemestres_ids(acronym: str):
"""liste des ids formsemestre du département""" """liste des ids formsemestre du département"""
# Le département, spécifié par un id ou un acronyme dept = Departement.query.filter_by(acronym=acronym).first_or_404()
dept = get_departement(dept_ident)
return jsonify([formsemestre.id for formsemestre in dept.formsemestres]) return jsonify([formsemestre.id for formsemestre in dept.formsemestres])
@bp.route("/departement/<string:dept_ident>/formsemestres_courants", methods=["GET"]) @bp.route("/departement/id/<int:dept_id>/formsemestres_ids", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def dept_formsemestres_ids_by_id(dept_id: int):
def liste_semestres_courant(dept_ident: str): """liste des ids formsemestre du département"""
""" dept = Departement.query.get_or_404(dept_id)
Liste des semestres actifs d'un départements donné return jsonify([formsemestre.id for formsemestre in dept.formsemestres])
dept: l'acronym d'un département
@bp.route("/departement/<string:acronym>/formsemestres_courants", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def dept_formsemestres_courants(acronym: str):
"""
Liste des semestres actifs d'un département d'acronyme donné
Exemple de résultat : Exemple de résultat :
[ [
@ -144,7 +160,7 @@ def liste_semestres_courant(dept_ident: str):
] ]
""" """
# Le département, spécifié par un id ou un acronyme # Le département, spécifié par un id ou un acronyme
dept = get_departement(dept_ident) dept = Departement.query.filter_by(acronym=acronym).first_or_404()
# Les semestres en cours de ce département # Les semestres en cours de ce département
formsemestres = models.FormSemestre.query.filter( formsemestres = models.FormSemestre.query.filter(
@ -153,4 +169,23 @@ def liste_semestres_courant(dept_ident: str):
FormSemestre.date_fin >= app.db.func.now(), FormSemestre.date_fin >= app.db.func.now(),
) )
return jsonify([d.to_dict(convert_parcours=True) for d in formsemestres]) return jsonify([d.to_dict(convert_objects=True) for d in formsemestres])
@bp.route("/departement/id/<int:dept_id>/formsemestres_courants", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def dept_formsemestres_courants_by_id(dept_id: int):
"""
Liste des semestres actifs d'un département d'id donné
"""
# Le département, spécifié par un id ou un acronyme
dept = Departement.query.get_or_404(dept_id)
# Les semestres en cours de ce département
formsemestres = models.FormSemestre.query.filter(
FormSemestre.dept_id == dept.id,
FormSemestre.date_debut <= app.db.func.now(),
FormSemestre.date_fin >= app.db.func.now(),
)
return jsonify([d.to_dict(convert_objects=True) for d in formsemestres])

View File

@ -27,6 +27,7 @@ from werkzeug.http import HTTP_STATUS_CODES
def error_response(status_code, message=None): def error_response(status_code, message=None):
"""Réponse sur erreur"""
payload = {"error": HTTP_STATUS_CODES.get(status_code, "Unknown error")} payload = {"error": HTTP_STATUS_CODES.get(status_code, "Unknown error")}
if message: if message:
payload["message"] = message payload["message"] = message
@ -36,4 +37,5 @@ def error_response(status_code, message=None):
def bad_request(message): def bad_request(message):
"400 Bad Request response"
return error_response(400, message) return error_response(400, message)

View File

@ -13,18 +13,19 @@ from flask import jsonify
import app import app
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_auth, token_permission_required from app.api.auth import permission_required_api
from app.api import tools
from app.models import Departement, FormSemestreInscription, FormSemestre, Identite from app.models import Departement, 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_bulletins import do_formsemestre_bulletinetud
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@bp.route("/etudiants/courant", defaults={"long": False}) @bp.route("/etudiants/courants", defaults={"long": False})
@bp.route("/etudiants/courant/long", defaults={"long": True}) @bp.route("/etudiants/courants/long", defaults={"long": True})
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def etudiants_courants(long=False):
def etudiants_courant(long=False):
""" """
Liste des étudiants inscrits dans un formsemestre actuellement en cours. Liste des étudiants inscrits dans un formsemestre actuellement en cours.
@ -63,8 +64,7 @@ def etudiants_courant(long=False):
@bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"]) @bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiant/nip/<string:nip>", methods=["GET"]) @bp.route("/etudiant/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiant/ine/<string:ine>", methods=["GET"]) @bp.route("/etudiant/ine/<string:ine>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etudiant(etudid: int = None, nip: str = None, ine: str = None): def etudiant(etudid: int = None, nip: str = None, ine: str = None):
""" """
Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé. Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé.
@ -104,27 +104,7 @@ def etudiant(etudid: int = None, nip: str = None, ine: str = None):
"description": "" "description": ""
} }
""" """
if etudid is not None: etud = tools.get_etud(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()
if etud is None: if etud is None:
return error_response( return error_response(
@ -138,8 +118,7 @@ def etudiant(etudid: int = None, nip: str = None, ine: str = None):
@bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"]) @bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiants/nip/<string:nip>", methods=["GET"]) @bp.route("/etudiants/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiants/ine/<string:ine>", methods=["GET"]) @bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def etudiants(etudid: int = None, nip: str = None, ine: str = None): def etudiants(etudid: int = None, nip: str = None, ine: str = None):
""" """
Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie
@ -166,41 +145,43 @@ def etudiants(etudid: int = None, nip: str = None, ine: str = None):
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres") @bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
@bp.route("/etudiant/nip/<string:nip>/formsemestres") @bp.route("/etudiant/nip/<string:nip>/formsemestres")
@bp.route("/etudiant/ine/<string:ine>/formsemestres") @bp.route("/etudiant/ine/<string:ine>/formsemestres")
@token_auth.login_required @permission_required_api(Permission.ScoView, 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):
""" """
Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique. Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique.
Attention, si accès via NIP ou INE, les semestres peuvent être de départements différents
(si l'étudiant a changé de département). L'id du département est `dept_id`.
Accès par etudid, nip ou ine Accès par etudid, nip ou ine
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,
"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": [] "responsables": []
"scodoc7_id": null,
"semestre_id": 1,
"titre_num": "Semestre test semestre 1",
"titre": "Semestre test",
}, },
... ...
] ]
@ -231,48 +212,79 @@ def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None)
formsemestres = query.order_by(FormSemestre.date_debut) formsemestres = query.order_by(FormSemestre.date_debut)
return jsonify( return jsonify(
[formsemestre.to_dict(convert_parcours=True) for formsemestre in formsemestres] [formsemestre.to_dict(convert_objects=True) for formsemestre in formsemestres]
) )
@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"}, defaults={"version": "long", "pdf": False},
) )
@bp.route( @bp.route(
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin", "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin",
methods=["GET"], methods=["GET"],
defaults={"version": "long"}, defaults={"version": "long", "pdf": False},
) )
@bp.route( @bp.route(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin", "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin",
methods=["GET"], methods=["GET"],
defaults={"version": "long"}, defaults={"version": "long", "pdf": False},
) )
# Version PDF non fonctionnelle
@bp.route(
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/pdf",
methods=["GET"],
defaults={"version": "long", "pdf": True},
)
# @bp.route(
# "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/pdf",
# methods=["GET"],
# defaults={"version": "long", "pdf": True},
# )
# @bp.route(
# "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/pdf",
# methods=["GET"],
# defaults={"version": "long", "pdf": True},
# )
@bp.route( @bp.route(
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short", "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"], methods=["GET"],
defaults={"version": "short"}, defaults={"version": "short", "pdf": False},
) )
@bp.route( @bp.route(
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short", "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"], methods=["GET"],
defaults={"version": "short"}, defaults={"version": "short", "pdf": False},
) )
@bp.route( @bp.route(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short", "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"], methods=["GET"],
defaults={"version": "short"}, defaults={"version": "short", "pdf": False},
) )
@token_auth.login_required @bp.route(
@token_permission_required(Permission.APIView) "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
def etudiant_bulletin_semestre( methods=["GET"],
defaults={"version": "short", "pdf": True},
)
@bp.route(
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
methods=["GET"],
defaults={"version": "short", "pdf": True},
)
@bp.route(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
methods=["GET"],
defaults={"version": "short", "pdf": True},
)
@permission_required_api(Permission.ScoView, Permission.APIView)
def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner en version pdf
formsemestre_id, formsemestre_id,
etudid: int = None, etudid: int = None,
nip: str = None, nip: str = None,
ine: str = None, ine: str = None,
version="long", version="long",
pdf: bool = False,
): ):
""" """
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é
@ -281,150 +293,8 @@ def etudiant_bulletin_semestre(
etudid : l'etudid d'un étudiant etudid : l'etudid d'un étudiant
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 : voir https://scodoc.org/ScoDoc9API/#bulletin
{
"version": "0",
"type": "BUT",
"date": "2022-04-27T07:18:16.450634Z",
"publie": true,
"etudiant": {
"civilite": "X",
"code_ine": "1",
"code_nip": "1",
"date_naissance": "",
"email": "SACHA.COSTA@example.com",
"emailperso": "",
"etudid": 1,
"nom": "COSTA",
"prenom": "SACHA",
"nomprenom": "Sacha COSTA",
"lieu_naissance": "",
"dept_naissance": "",
"nationalite": "",
"boursier": "",
"fiche_url": "/ScoDoc/TAPI/Scolarite/ficheEtud?etudid=1",
"photo_url": "/ScoDoc/TAPI/Scolarite/get_photo_image?etudid=1&size=small",
"id": 1,
"codepostaldomicile": "",
"paysdomicile": "",
"telephonemobile": "",
"typeadresse": "domicile",
"domicile": "",
"villedomicile": "",
"telephone": "",
"fax": "",
"description": "",
},
"formation": {
"id": 1,
"acronyme": "BUT R&amp;T",
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
"titre": "BUT R&amp;T",
},
"formsemestre_id": 1,
"etat_inscription": "I",
"options": {
"show_abs": true,
"show_abs_modules": false,
"show_ects": true,
"show_codemodules": false,
"show_matieres": false,
"show_rangs": true,
"show_ue_rangs": true,
"show_mod_rangs": true,
"show_moypromo": false,
"show_minmax": false,
"show_minmax_mod": false,
"show_minmax_eval": false,
"show_coef": true,
"show_ue_cap_details": false,
"show_ue_cap_current": true,
"show_temporary": true,
"temporary_txt": "Provisoire",
"show_uevalid": true,
"show_date_inscr": true,
},
"ressources": {
"R101": {
"id": 1,
"titre": "Initiation aux r\u00e9seaux informatiques",
"code_apogee": null,
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=1",
"moyenne": {},
"evaluations": [
{
"id": 1,
"description": "eval1",
"date": "2022-04-20",
"heure_debut": "08:00",
"heure_fin": "09:00",
"coef": "01.00",
"poids": {
"RT1.1": 1.0,
},
"note": {
"value": "12.00",
"min": "00.00",
"max": "18.00",
"moy": "10.88",
},
"url": "/ScoDoc/TAPI/Scolarite/Notes/evaluation_listenotes?evaluation_id=1",
}
],
},
},
"saes": {
"SAE11": {
"id": 2,
"titre": "Se sensibiliser \u00e0 l&apos;hygi\u00e8ne informatique et \u00e0 la cybers\u00e9curit\u00e9",
"code_apogee": null,
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=2",
"moyenne": {},
"evaluations": [],
},
},
"ues": {
"RT1.1": {
"id": 1,
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
"numero": 1,
"type": 0,
"color": "#B80004",
"competence": null,
"moyenne": {
"value": "08.50",
"min": "06.00",
"max": "16.50",
"moy": "11.31",
"rang": "12",
"total": 16,
},
"bonus": "00.00",
"malus": "00.00",
"capitalise": null,
"ressources": {
"R101": {"id": 1, "coef": 12.0, "moyenne": "12.00"},
},
"saes": {
"SAE11": {"id": 2, "coef": 16.0, "moyenne": "~"},
},
"ECTS": {"acquis": 0.0, "total": 12.0},
},
"semestre": {
"etapes": [],
"date_debut": "2021-09-01",
"date_fin": "2022-08-31",
"annee_universitaire": "2021 - 2022",
"numero": 1,
"inscription": "",
"groupes": [],
"absences": {"injustifie": 1, "total": 2},
"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 = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404() formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404() dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
@ -449,6 +319,13 @@ def etudiant_bulletin_semestre(
) )
app.set_sco_dept(dept.acronym) app.set_sco_dept(dept.acronym)
if pdf:
pdf_response, _ = do_formsemestre_bulletinetud(
formsemestre, etudid, version=version, format="pdf"
)
return pdf_response
return sco_bulletins.get_formsemestre_bulletin_etud_json( return sco_bulletins.get_formsemestre_bulletin_etud_json(
formsemestre, etud, version=version formsemestre, etud, version=version
) )
@ -466,8 +343,7 @@ def etudiant_bulletin_semestre(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/groups", "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/groups",
methods=["GET"], methods=["GET"],
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, 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
): ):

View File

@ -1,23 +1,31 @@
############################################### Evaluations ########################################################### ##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""
ScoDoc 9 API : accès aux évaluations
"""
from flask import jsonify 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_auth, token_permission_required from app.api.auth import permission_required_api
from app.api.errors import error_response from app.api.errors import error_response
from app.models import Evaluation
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 @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def evaluations(moduleimpl_id: int): def evaluations(moduleimpl_id: int):
""" """
Retourne la liste des évaluations à partir de l'id d'un moduleimpl Retourne la liste des évaluations d'un moduleimpl
moduleimpl_id : l'id d'un moduleimpl moduleimpl_id : l'id d'un moduleimpl
@ -39,7 +47,7 @@ def evaluations(moduleimpl_id: int):
"evaluation_id": 1, "evaluation_id": 1,
"jouriso": "2022-04-20", "jouriso": "2022-04-20",
"duree": "1h", "duree": "1h",
"descrheure": " de 08h00 \u00e0 09h00", "descrheure": " de 08h00 à 09h00",
"matin": 1, "matin": 1,
"apresmidi": 0 "apresmidi": 0
}, },
@ -56,8 +64,7 @@ def evaluations(moduleimpl_id: int):
@bp.route("/evaluation/eval_notes/<int:evaluation_id>", methods=["GET"]) @bp.route("/evaluation/eval_notes/<int:evaluation_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def evaluation_notes(evaluation_id: int): def evaluation_notes(evaluation_id: int):
""" """
Retourne la liste des notes à partir de l'id d'une évaluation donnée Retourne la liste des notes à partir de l'id d'une évaluation donnée
@ -87,16 +94,11 @@ def evaluation_notes(evaluation_id: int):
... ...
} }
""" """
# Fonction utilisée : app.scodoc.sco_evaluation_db.do_evaluation_get_all_notes()
evaluation = 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( dept = evaluation.moduleimpl.formsemestre.departement
id=evaluation.moduleimpl.formsemestre.dept_id
).first()
app.set_sco_dept(dept.acronym) app.set_sco_dept(dept.acronym)
try: try:
# 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(

View File

@ -1,40 +1,53 @@
##############################################" Formations ############################################################ ##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""
ScoDoc 9 API : accès aux formations
"""
from flask import jsonify 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.errors import error_response
from app.api.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.models.formations import Formation from app.models.formations import Formation
from app.scodoc import sco_formations from app.scodoc import sco_formations
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@bp.route("/formations_ids", methods=["GET"]) @bp.route("/formations", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def formations():
def formations_ids():
""" """
Retourne la liste de toutes les formations (tous départements) Retourne la liste de toutes les formations (tous départements)
"""
data = [d.to_dict() for d in models.Formation.query]
return jsonify(data)
@bp.route("/formations_ids", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def formations_ids():
"""
Retourne la liste de toutes les id de formations (tous départements)
Exemple de résultat : [ 17, 99, 32 ] Exemple de résultat : [ 17, 99, 32 ]
""" """
# Récupération de toutes les formations data = [d.id for d in models.Formation.query]
list_formations = models.Formation.query.all()
# Mise en forme des données
data = [d.id for d in list_formations]
return jsonify(data) return jsonify(data)
@bp.route("/formation/<int:formation_id>", methods=["GET"]) @bp.route("/formation/<int:formation_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formation_by_id(formation_id: int): def formation_by_id(formation_id: int):
""" """
Retourne une formation en fonction d'un id donné La formation d'id donné
formation_id : l'id d'une formation formation_id : l'id d'une formation
@ -42,7 +55,7 @@ def formation_by_id(formation_id: int):
{ {
"id": 1, "id": 1,
"acronyme": "BUT R&amp;T", "acronyme": "BUT R&amp;T",
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications", "titre_officiel": "Bachelor technologique réseaux et télécommunications",
"formation_code": "V1RET", "formation_code": "V1RET",
"code_specialite": null, "code_specialite": null,
"dept_id": 1, "dept_id": 1,
@ -53,13 +66,8 @@ def formation_by_id(formation_id: int):
"formation_id": 1 "formation_id": 1
} }
""" """
# Récupération de la formation formation = models.Formation.query.get_or_404(formation_id)
formation = models.Formation.query.filter_by(id=formation_id).first_or_404() return jsonify(formation.to_dict())
# Mise en forme des données
data = formation.to_dict()
return jsonify(data)
@bp.route( @bp.route(
@ -72,13 +80,13 @@ def formation_by_id(formation_id: int):
methods=["GET"], methods=["GET"],
defaults={"export_ids": True}, defaults={"export_ids": True},
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, 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):
""" """
Retourne la formation, avec UE, matières, modules Retourne la formation, avec UE, matières, modules
formation_id : l'id d'une formation formation_id : l'id d'une formation
export_ids : True ou False, si l'on veut ou non exporter les ids
Exemple de résultat : Exemple de résultat :
{ {
@ -170,10 +178,8 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
} }
""" """
formation = Formation.query.get_or_404(formation_id) formation = Formation.query.get_or_404(formation_id)
dept = models.Departement.query.filter_by(id=formation.dept_id).first() app.set_sco_dept(formation.departement.acronym)
app.set_sco_dept(dept.acronym)
try: try:
# Utilisation de la fonction formation_export
data = sco_formations.formation_export(formation_id, export_ids) data = sco_formations.formation_export(formation_id, export_ids)
except ValueError: except ValueError:
return error_response(500, message="Erreur inconnue") return error_response(500, message="Erreur inconnue")
@ -182,8 +188,7 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
@bp.route("/formation/moduleimpl/<int:moduleimpl_id>", methods=["GET"]) @bp.route("/formation/moduleimpl/<int:moduleimpl_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def moduleimpl(moduleimpl_id: int): def moduleimpl(moduleimpl_id: int):
""" """
Retourne un module moduleimpl en fonction de son id Retourne un module moduleimpl en fonction de son id
@ -219,26 +224,24 @@ def moduleimpl(moduleimpl_id: int):
} }
} }
""" """
modimpl = models.ModuleImpl.query.filter_by(id=moduleimpl_id).first_or_404() modimpl = models.ModuleImpl.query.get_or_404(moduleimpl_id)
data = modimpl.to_dict() return jsonify(modimpl.to_dict())
return jsonify(data)
@bp.route( @bp.route(
"/formation/<int:formation_id>/referentiel_competences", "/formation/<int:formation_id>/referentiel_competences",
methods=["GET"], methods=["GET"],
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def referentiel_competences(formation_id: int): def referentiel_competences(formation_id: int):
""" """
Retourne le référentiel de compétences Retourne le référentiel de compétences
formation_id : l'id d'une formation formation_id : l'id d'une formation
return json, ou null si pas de référentiel associé. return null si pas de référentiel associé.
""" """
formation = models.Formation.query.filter_by(id=formation_id).first_or_404() formation = models.Formation.query.get_or_404(formation_id)
if formation.referentiel_competence is None: if formation.referentiel_competence is None:
return jsonify(None) return jsonify(None)
return jsonify(formation.referentiel_competence.to_dict()) return jsonify(formation.referentiel_competence.to_dict())

View File

@ -1,20 +1,32 @@
########################################## Formsemestres ############################################################## ##############################################################################
from flask import jsonify # ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""
ScoDoc 9 API : accès aux formsemestres
"""
from flask import abort, jsonify, request
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.auth import token_auth, token_permission_required from app.api.auth import permission_required_api
from app.models import Departement, FormSemestre, FormSemestreEtape from app.comp import res_sem
from app.comp.moy_mod import ModuleImplResults
from app.comp.res_compat import NotesTableCompat
from app.models import Evaluation, FormSemestre, FormSemestreEtape, ModuleImpl
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 import sco_groups
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
import app.scodoc.sco_utils as scu
@bp.route("/formsemestre/<int:formsemestre_id>", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def formsemestre_infos(formsemestre_id: int):
def formsemestre(formsemestre_id: int):
""" """
Information sur le formsemestre indiqué. Information sur le formsemestre indiqué.
@ -51,230 +63,67 @@ def formsemestre(formsemestre_id: int):
} }
""" """
formsemestre: FormSemestre = models.FormSemestre.query.filter_by( formsemestre: FormSemestre = models.FormSemestre.query.get_or_404(formsemestre_id)
id=formsemestre_id return jsonify(formsemestre.to_dict_api())
).first_or_404()
data = formsemestre.to_dict(convert_parcours=True)
# 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)
@bp.route("/formsemestre/apo/<string:etape_apo>", methods=["GET"]) @bp.route("/formsemestres/query", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def formsemestres_query():
def formsemestre_apo(etape_apo: str):
""" """
Retourne les informations sur les formsemestres ayant cette étape Apogée Retourne les formsemestres filtrés par
étape Apogée ou année scolaire ou département (acronyme ou id)
etape_apo : un code étape apogée etape_apo : un code étape apogée
annee_scolaire : année de début de l'année scolaire
Exemple de résultat : dept_acronym : acronyme du département (eg "RT")
[ dept_id : id du département
{ ...formsemestre...
}, ...
]
""" """
formsemestres = FormSemestre.query.filter( etape_apo = request.args.get("etape_apo")
FormSemestreEtape.etape_apo == etape_apo, annee_scolaire = request.args.get("annee_scolaire")
FormSemestreEtape.formsemestre_id == FormSemestre.id, dept_acronym = request.args.get("dept_acronym")
dept_id = request.args.get("dept_id")
formsemestres = FormSemestre.query
if etape_apo is not None:
formsemestres = formsemestres.join(FormSemestreEtape).filter(
FormSemestreEtape.etape_apo == etape_apo
) )
if annee_scolaire is not None:
try:
annee_scolaire_int = int(annee_scolaire)
except ValueError:
abort(404, "invalid annee_scolaire: not int")
debut_annee = scu.date_debut_anne_scolaire(annee_scolaire_int)
fin_annee = scu.date_fin_anne_scolaire(annee_scolaire_int)
formsemestres = formsemestres.filter(
FormSemestre.date_fin >= debut_annee, FormSemestre.date_debut <= fin_annee
)
if dept_acronym is not None:
formsemestres = formsemestres.join(models.Departement).filter_by(
acronym=dept_acronym
)
if dept_id is not None:
try:
dept_id = int(dept_id)
except ValueError:
abort(404, "invalid dept_id: not int")
formsemestres = formsemestres.filter_by(dept_id=dept_id)
return jsonify( return jsonify([formsemestre.to_dict_api() for formsemestre in formsemestres])
[formsemestre.to_dict(convert_parcours=True) for formsemestre in formsemestres]
)
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"]) @bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def bulletins(formsemestre_id: int): def bulletins(formsemestre_id: int):
""" """
Retourne les bulletins d'un formsemestre donné Retourne les bulletins d'un formsemestre donné
formsemestre_id : l'id d'un formesemestre formsemestre_id : l'id d'un formesemestre
Exemple de résultat : Exemple de résultat : liste, voir https://scodoc.org/ScoDoc9API/#bulletin
[
{
"version": "0",
"type": "BUT",
"date": "2022-04-27T07:18:16.450634Z",
"publie": true,
"etudiant": {
"civilite": "X",
"code_ine": "1",
"code_nip": "1",
"date_naissance": "",
"email": "SACHA.COSTA@example.com",
"emailperso": "",
"etudid": 1,
"nom": "COSTA",
"prenom": "SACHA",
"nomprenom": "Sacha COSTA",
"lieu_naissance": "",
"dept_naissance": "",
"nationalite": "",
"boursier": "",
"fiche_url": "/ScoDoc/TAPI/Scolarite/ficheEtud?etudid=1",
"photo_url": "/ScoDoc/TAPI/Scolarite/get_photo_image?etudid=1&size=small",
"id": 1,
"codepostaldomicile": "",
"paysdomicile": "",
"telephonemobile": "",
"typeadresse": "domicile",
"domicile": "",
"villedomicile": "",
"telephone": "",
"fax": "",
"description": ""
},
"formation": {
"id": 1,
"acronyme": "BUT R&amp;T",
"titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
"titre": "BUT R&amp;T"
},
"formsemestre_id": 1,
"etat_inscription": "I",
"options": {
"show_abs": true,
"show_abs_modules": false,
"show_ects": true,
"show_codemodules": false,
"show_matieres": false,
"show_rangs": true,
"show_ue_rangs": true,
"show_mod_rangs": true,
"show_moypromo": false,
"show_minmax": false,
"show_minmax_mod": false,
"show_minmax_eval": false,
"show_coef": true,
"show_ue_cap_details": false,
"show_ue_cap_current": true,
"show_temporary": true,
"temporary_txt": "Provisoire",
"show_uevalid": true,
"show_date_inscr": true
},
"ressources": {
"R101": {
"id": 1,
"titre": "Initiation aux r\u00e9seaux informatiques",
"code_apogee": null,
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=1",
"moyenne": {},
"evaluations": [
{
"id": 1,
"description": "eval1",
"date": "2022-04-20",
"heure_debut": "08:00",
"heure_fin": "09:00",
"coef": "01.00",
"poids": {
"RT1.1": 1.0,
},
"note": {
"value": "12.00",
"min": "00.00",
"max": "18.00",
"moy": "10.88"
},
"url": "/ScoDoc/TAPI/Scolarite/Notes/evaluation_listenotes?evaluation_id=1"
}
]
},
},
"saes": {
"SAE11": {
"id": 2,
"titre": "Se sensibiliser \u00e0 l&apos;hygi\u00e8ne informatique et \u00e0 la cybers\u00e9curit\u00e9",
"code_apogee": null,
"url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=2",
"moyenne": {},
"evaluations": []
},
},
"ues": {
"RT1.1": {
"id": 1,
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
"numero": 1,
"type": 0,
"color": "#B80004",
"competence": null,
"moyenne": {
"value": "08.50",
"min": "06.00",
"max": "16.50",
"moy": "11.31",
"rang": "12",
"total": 16
},
"bonus": "00.00",
"malus": "00.00",
"capitalise": null,
"ressources": {
"R101": {
"id": 1,
"coef": 12.0,
"moyenne": "12.00"
},
},
"saes": {
"SAE11": {
"id": 2,
"coef": 16.0,
"moyenne": "~"
},
},
"ECTS": {
"acquis": 0.0,
"total": 12.0
}
},
"semestre": {
"etapes": [],
"date_debut": "2021-09-01",
"date_fin": "2022-08-31",
"annee_universitaire": "2021 - 2022",
"numero": 1,
"inscription": "",
"groupes": [],
"absences": {
"injustifie": 1,
"total": 2
},
"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 = models.FormSemestre.query.get_or_404(formsemestre_id)
id=formsemestre_id app.set_sco_dept(formsemestre.departement.acronym)
).first_or_404()
dept = models.Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
app.set_sco_dept(dept.acronym)
data = [] data = []
for etu in formsemestre.etuds: for etu in formsemestre.etuds:
@ -284,50 +133,11 @@ def bulletins(formsemestre_id: int):
return jsonify(data) return jsonify(data)
# XXX Attendre ScoDoc 9.3
# @bp.route("/formsemestre/<int:formsemestre_id>/jury", methods=["GET"])
# @token_auth.login_required
# @token_permission_required(Permission.APIView)
# def jury(formsemestre_id: int):
# """
# Retourne le récapitulatif des décisions jury
# formsemestre_id : l'id d'un formsemestre
# Exemple de résultat :
# """
# # Fonction utilisée : app.scodoc.sco_pvjury.formsemestre_pvjury()
# 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)
# data = formsemestre_pvjury(formsemestre_id)
# # try:
# # # Utilisation de la fonction formsemestre_pvjury
# # data = formsemestre_pvjury(formsemestre_id)
# # except AttributeError:
# # return error_response(
# # 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)
@bp.route( @bp.route(
"/formsemestre/<int:formsemestre_id>/programme", "/formsemestre/<int:formsemestre_id>/programme",
methods=["GET"], methods=["GET"],
) )
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView)
def formsemestre_programme(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
@ -393,10 +203,7 @@ def formsemestre_programme(formsemestre_id: int):
"modules" : [ ... les modules qui ne sont ni des SAEs ni des ressources ... ] "modules" : [ ... les modules qui ne sont ni des SAEs ni des ressources ... ]
} }
""" """
formsemestre: FormSemestre = models.FormSemestre.query.filter_by( formsemestre: FormSemestre = models.FormSemestre.query.get_or_404(formsemestre_id)
id=formsemestre_id
).first_or_404()
ues = formsemestre.query_ues() ues = formsemestre.query_ues()
m_list = { m_list = {
ModuleType.RESSOURCE: [], ModuleType.RESSOURCE: [],
@ -404,14 +211,179 @@ def formsemestre_programme(formsemestre_id: int):
ModuleType.STANDARD: [], ModuleType.STANDARD: [],
} }
for modimpl in formsemestre.modimpls_sorted: for modimpl in formsemestre.modimpls_sorted:
d = modimpl.to_dict() d = modimpl.to_dict(convert_objects=True)
m_list[modimpl.module.module_type].append(d) m_list[modimpl.module.module_type].append(d)
return jsonify( return jsonify(
{ {
"ues": [ue.to_dict() for ue in ues], "ues": [ue.to_dict(convert_objects=True) for ue in ues],
"ressources": m_list[ModuleType.RESSOURCE], "ressources": m_list[ModuleType.RESSOURCE],
"saes": m_list[ModuleType.SAE], "saes": m_list[ModuleType.SAE],
"modules": m_list[ModuleType.STANDARD], "modules": m_list[ModuleType.STANDARD],
} }
) )
@bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants",
methods=["GET"],
defaults={"etat": scu.INSCRIT},
)
@bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants/demissionnaires",
methods=["GET"],
defaults={"etat": scu.DEMISSION},
)
@bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants/defaillants",
methods=["GET"],
defaults={"etat": scu.DEF},
)
@permission_required_api(Permission.ScoView, Permission.APIView)
def formsemestre_etudiants(formsemestre_id: int, etat: str):
"""
Retourne la liste des étudiants d'un formsemestre
formsemestre_id : l'id d'un formsemestre
"""
formsemestre: FormSemestre = models.FormSemestre.query.filter_by(
id=formsemestre_id
).first_or_404()
inscriptions = [ins for ins in formsemestre.inscriptions if ins.etat == etat]
etuds = [ins.etud.to_dict_short() for ins in inscriptions]
# Ajout des groupes de chaque étudiants
# XXX A REVOIR: trop inefficace !
for etud in etuds:
etud["groups"] = sco_groups.get_etud_groups(etud["id"], formsemestre_id)
return jsonify(etuds)
@bp.route("/formsemestre/<int:formsemestre_id>/etat_evals", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def etat_evals(formsemestre_id: int):
"""
Informations sur l'état des évaluations d'un formsemestre.
formsemestre_id : l'id d'un semestre
Exemple de résultat :
[
{
"id": 1, // moduleimpl_id
"titre": "Initiation aux réseaux informatiques",
"evaluations": [
{
"id": 1,
"description": null,
"datetime_epreuve": null,
"heure_fin": "09:00:00",
"coefficient": "02.00"
"is_complete": true,
"nb_inscrits": 16,
"nb_manquantes": 0,
"ABS": 0,
"ATT": 0,
"EXC": 0,
"saisie_notes": {
"datetime_debut": "2021-09-11T00:00:00+02:00",
"datetime_fin": "2022-08-25T00:00:00+02:00",
"datetime_mediane": "2022-03-19T00:00:00+01:00"
}
},
...
]
},
]
"""
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
app.set_sco_dept(formsemestre.departement.acronym)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
result = []
for modimpl_id in nt.modimpls_results:
modimpl_results: ModuleImplResults = nt.modimpls_results[modimpl_id]
modimpl = ModuleImpl.query.get_or_404(modimpl_id)
modimpl_dict = modimpl.to_dict()
list_eval = []
for evaluation_id in modimpl_results.evaluations_etat:
eval_etat = modimpl_results.evaluations_etat[evaluation_id]
evaluation = Evaluation.query.get_or_404(evaluation_id)
eval_dict = evaluation.to_dict()
eval_dict["etat"] = eval_etat.to_dict()
eval_dict["nb_inscrits"] = modimpl_results.nb_inscrits_module
eval_dict["nb_notes_manquantes"] = len(
modimpl_results.evals_etudids_sans_note[evaluation.id]
)
eval_dict["nb_notes_abs"] = sum(
modimpl_results.evals_notes[evaluation.id] == scu.NOTES_ABSENCE
)
eval_dict["nb_notes_att"] = eval_etat.nb_attente
eval_dict["nb_notes_exc"] = sum(
modimpl_results.evals_notes[evaluation.id] == scu.NOTES_NEUTRALISE
)
# Récupération de toutes les notes de l'évaluation
# eval["notes"] = modimpl_results.get_eval_notes_dict(evaluation_id)
notes = models.NotesNotes.query.filter_by(evaluation_id=evaluation.id).all()
date_debut = None
date_fin = None
date_mediane = None
# Si il y a plus d'une note saisie pour l'évaluation
if len(notes) >= 1:
# Trie des notes en fonction de leurs dates
notes_sorted = sorted(notes, key=lambda note: note.date)
date_debut = notes_sorted[0].date
date_fin = notes_sorted[-1].date
# Récupération de l'id de la note médiane
list_id_notes_sorted = [note.id for note in notes_sorted]
id_mediane = list_id_notes_sorted[len(list_id_notes_sorted) // 2]
date_mediane = notes_sorted[id_mediane].date
eval_dict["saisie_notes"] = {
"datetime_debut": date_debut.isoformat()
if date_debut is not None
else None,
"datetime_fin": date_fin.isoformat() if date_fin is not None else None,
"datetime_mediane": date_mediane.isoformat()
if date_mediane is not None
else None,
}
list_eval.append(eval)
modimpl_dict["evaluations"] = list_eval
result.append(modimpl_dict)
return jsonify(result)
@bp.route("/formsemestre/<int:formsemestre_id>/resultats", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def formsemestre_resultat(formsemestre_id: int):
"""Tableau récapitulatif des résultats
Pour chaque étudiant, son état, ses groupes, ses moyennes d'UE et de modules.
"""
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
app.set_sco_dept(formsemestre.departement.acronym)
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
rows, footer_rows, titles, column_ids = res.get_table_recap(
convert_values=False,
include_evaluations=False,
mode_jury=False,
allow_html=False,
)
# Supprime les champs inutiles (mise en forme)
table = [{k: row[k] for k in row if not k[0] == "_"} for row in rows]
# Ajoute les groupes
etud_groups = sco_groups.get_formsemestre_etuds_groups(formsemestre_id)
for row in table:
row["partitions"] = etud_groups.get(row["etudid"], {})
return jsonify(table)

View File

@ -4,7 +4,7 @@
# 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_auth, token_permission_required # from app.api.auth import permission_required_api
# 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

View File

@ -38,13 +38,12 @@ from app.api.auth import token_auth
from app.api.errors import error_response from app.api.errors import error_response
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 permission_required_api
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@bp.route("/logos", methods=["GET"]) @bp.route("/logos", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, 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):
return error_response(401, message="accès interdit") return error_response(401, message="accès interdit")
@ -56,8 +55,7 @@ def api_get_glob_logos():
@bp.route("/logos/<string:logoname>", methods=["GET"]) @bp.route("/logos/<string:logoname>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, 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):
return error_response(401, message="accès interdit") return error_response(401, message="accès interdit")
@ -73,8 +71,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 @permission_required_api(Permission.ScoView, 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
if not g.current_user.has_permission(Permission.ScoChangePreferences, departement): if not g.current_user.has_permission(Permission.ScoChangePreferences, departement):
@ -84,8 +81,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 @permission_required_api(Permission.ScoView, 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 ?
dept_id = Departement.from_acronym(departement).id dept_id = Departement.from_acronym(departement).id

View File

@ -1,145 +1,320 @@
############################################### Partitions ############################################################ ##############################################################################
from flask import jsonify # ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
from app import models """
ScoDoc 9 API : partitions
"""
from flask import abort, jsonify, request
import app
from app import db, log
from app.api import bp from app.api import bp
from app.api.auth import permission_required_api
from app.api.errors import error_response from app.models import FormSemestre, FormSemestreInscription, Identite
from app.api.auth import token_auth, token_permission_required from app.models import GroupDescr, Partition
from app.scodoc.sco_groups import get_group_members, setGroups, get_partitions_list from app.models.groups import group_membership
from app.scodoc import sco_cache
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc import sco_utils as scu
@bp.route("/partitions/<int:formsemestre_id>", methods=["GET"]) @bp.route("/partition/<int:partition_id>", methods=["GET"])
@token_auth.login_required @permission_required_api(Permission.ScoView, Permission.APIView)
@token_permission_required(Permission.APIView) def partition_info(partition_id: int):
def partition(formsemestre_id: int): """
Exemple de résultat :
```
{
'bul_show_rank': False,
'formsemestre_id': 39,
'groups': [
{'id': 268, 'name': 'A', 'partition_id': 100},
{'id': 269, 'name': 'B', 'partition_id': 100}
],
'groups_editable': True,
'id': 100,
'numero': 100,
'partition_name': 'TD',
'show_in_lists': True
}
```
"""
partition = Partition.query.get_or_404(partition_id)
return jsonify(partition.to_dict(with_groups=True))
@bp.route("/formsemestre/<int:formsemestre_id>/partitions", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def formsemestre_partitions(formsemestre_id: int):
""" """
Retourne la liste de toutes les partitions d'un formsemestre Retourne la liste de toutes les partitions d'un formsemestre
formsemestre_id : l'id d'un formsemestre formsemestre_id : l'id d'un formsemestre
Exemple de résultat :
[
{
"partition_id": 2,
"id": 2,
"formsemestre_id": 1,
"partition_name": "TD",
"numero": 1,
"bul_show_rank": false,
"show_in_lists": true
},
{
"partition_id": 1,
"id": 1,
"formsemestre_id": 1,
"partition_name": null,
"numero": 0,
"bul_show_rank": false,
"show_in_lists": true
}
]
""" """
# # Récupération de toutes les partitions formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
# partitions = models.Partition.query.filter_by(id=formsemestre_id) return jsonify(
# [partition.to_dict(with_groups=True) for partition in formsemestre.partitions]
# # Mise en forme des données )
# data = [partition.to_dict() for partition in partitions]
data = get_partitions_list(formsemestre_id)
return jsonify(data)
@bp.route("/partitions/groups/<int:group_id>", methods=["GET"]) @bp.route("/group/<int:group_id>/etudiants", methods=["GET"])
@bp.route("/partitions/groups/<int:group_id>/etat/<string:etat>", methods=["GET"]) @permission_required_api(Permission.ScoView, Permission.APIView)
@token_auth.login_required def etud_in_group(group_id: int):
@token_permission_required(Permission.APIView)
def etud_in_group(group_id: int, etat=None):
""" """
Retourne la liste des étudiants dans un groupe Retourne la liste des étudiants dans un groupe
group_id : l'id d'un groupe group_id : l'id d'un groupe
etat : état de l'inscription
Exemple de résultat : Exemple de résultat :
[ [
{ {
"etudid": 10, 'civilite': 'M',
"id": 10, 'id': 123456,
"dept_id": 1, 'ine': None,
"nom": "BOUTET", 'nip': '987654321',
"prenom": "Marguerite", 'nom': 'MARTIN',
"nom_usuel": "", 'nom_usuel': null,
"civilite": "F", 'prenom': 'JEAN'}
"date_naissance": null,
"lieu_naissance": null,
"dept_naissance": null,
"nationalite": null,
"statut": null,
"boursier": null,
"photo_filename": null,
"code_nip": "10",
"code_ine": "10",
"scodoc7_id": null,
"email": "MARGUERITE.BOUTET@example.com",
"emailperso": null,
"domicile": null,
"codepostaldomicile": null,
"villedomicile": null,
"paysdomicile": null,
"telephone": null,
"telephonemobile": null,
"fax": null,
"typeadresse": "domicile",
"description": null,
"group_id": 1,
"etat": "I",
"civilite_str": "Mme",
"nom_disp": "BOUTET",
"nomprenom": "Mme Marguerite BOUTET",
"ne": "e",
"email_default": "MARGUERITE.BOUTET@example.com"
}, },
... ...
] ]
""" """
# Fonction utilisée : app.scodoc.sco_groups.get_group_members() group = GroupDescr.query.get_or_404(group_id)
return jsonify([etud.to_dict_short() for etud in group.etuds])
if etat is None:
data = get_group_members(group_id) @bp.route("/group/<int:group_id>/etudiants/query", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def etud_in_group_query(group_id: int):
"""Etudiants du groupe, filtrés par état"""
etat = request.args.get("etat")
if etat not in {scu.INSCRIT, scu.DEMISSION, scu.DEF}:
abort(404, "etat invalid")
group = GroupDescr.query.get_or_404(group_id)
query = (
Identite.query.join(FormSemestreInscription)
.filter_by(formsemestre_id=group.partition.formsemestre_id, etat=etat)
.join(group_membership)
.filter_by(group_id=group_id)
)
return jsonify([etud.to_dict_short() for etud in query])
@bp.route("/group/<int:group_id>/set_etudiant/<int:etudid>", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def set_etud_group(etudid: int, group_id: int):
"""Affecte l'étudiant au groupe indiqué"""
etud = Identite.query.get_or_404(etudid)
group = GroupDescr.query.get_or_404(group_id)
if etud.id not in {e.id for e in group.partition.formsemestre.etuds}:
abort(404, "etud non inscrit au formsemestre du groupe")
groups = (
GroupDescr.query.filter_by(partition_id=group.partition.id)
.join(group_membership)
.filter_by(etudid=etudid)
)
ok = False
for g in groups:
if g.id == group_id:
ok = True
else: else:
data = get_group_members(group_id, etat) g.etuds.remove(etud)
if not ok:
if len(data) == 0: group.etuds.append(etud)
return error_response(404, message="group_id inconnu") db.session.commit()
return jsonify({"group_id": group_id, "etudid": etudid})
return jsonify(data)
@bp.route( @bp.route("/partition/<int:partition_id>/group/create", methods=["POST"])
"/partitions/set_groups/partition/<int:partition_id>/groups/<string:groups_id>/delete/<string:groups_to_delete>" @permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
"/create/<string:groups_to_create>", def group_create(partition_id: int):
methods=["POST"], """Création d'un groupe dans une partition
)
@token_auth.login_required The request content type should be "application/json":
@token_permission_required(Permission.APIEtudChangeGroups) {
def set_groups( "group_name" : nom_du_groupe,
partition_id: int, groups_lists: str, groups_to_delete: str, groups_to_create: str }
):
""" """
Set les groups partition: Partition = Partition.query.get_or_404(partition_id)
if not partition.groups_editable:
abort(404, "partition non editable")
data = request.get_json(force=True) # may raise 400 Bad Request
group_name = data.get("group_name")
if group_name is None:
abort(404, "missing group name or invalid data format")
if not GroupDescr.check_name(partition, group_name):
abort(404, "invalid group_name")
group_name = group_name.strip()
partition_id : l'id d'une partition group = GroupDescr(group_name=group_name, partition_id=partition_id)
groups_lists : membres de chaque groupe existant db.session.add(group)
groups_ti_delete : les groupes à supprimer db.session.commit()
groups_to_create : les groupes à créer log(f"created group {group}")
app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
return jsonify(group.to_dict(with_partition=True))
@bp.route("/group/<int:group_id>/delete", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def group_delete(group_id: int):
"""Suppression d'un groupe"""
group = GroupDescr.query.get_or_404(group_id)
if not group.partition.groups_editable:
abort(404, "partition non editable")
formsemestre_id = group.partition.formsemestre_id
log(f"deleting {group}")
db.session.delete(group)
db.session.commit()
app.set_sco_dept(group.partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(formsemestre_id)
return jsonify({"OK": 1})
@bp.route("/group/<int:group_id>/edit", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def group_edit(group_id: int):
"""Edit a group"""
group: GroupDescr = GroupDescr.query.get_or_404(group_id)
if not group.partition.groups_editable:
abort(404, "partition non editable")
data = request.get_json(force=True) # may raise 400 Bad Request
group_name = data.get("group_name")
if group_name is not None:
if not GroupDescr.check_name(group.partition, group_name, existing=True):
abort(404, "invalid group_name")
group.group_name = group_name.strip()
db.session.add(group)
db.session.commit()
log(f"modified {group}")
app.set_sco_dept(group.partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(group.partition.formsemestre_id)
return jsonify(group.to_dict(with_partition=True))
@bp.route("/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def partition_create(formsemestre_id: int):
"""Création d'une partition dans un semestre
The request content type should be "application/json":
{
"partition_name": str,
"numero":int,
"bul_show_rank":bool,
"show_in_lists":bool,
"groups_editable":bool
}
""" """
# Fonction utilisée : app.scodoc.sco_groups.setGroups() formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
try: data = request.get_json(force=True) # may raise 400 Bad Request
# Utilisation de la fonction setGroups partition_name = data.get("partition_name")
setGroups(partition_id, groups_lists, groups_to_create, groups_to_delete) if partition_name is None:
return error_response(200, message="Groups set") abort(404, "missing partition_name or invalid data format")
except ValueError: if not Partition.check_name(formsemestre, partition_name):
return error_response(404, message="Erreur") abort(404, "invalid partition_name")
numero = data.get("numero", 0)
if not isinstance(numero, int):
abort(404, "invalid type for numero")
args = {
"formsemestre_id": formsemestre_id,
"partition_name": partition_name.strip(),
"numero": numero,
}
for boolean_field in ("bul_show_rank", "show_in_lists", "groups_editable"):
value = data.get(
boolean_field, False if boolean_field != "groups_editable" else True
)
if not isinstance(value, bool):
abort(404, f"invalid type for {boolean_field}")
args[boolean_field] = value
partition = Partition(**args)
db.session.add(partition)
db.session.commit()
log(f"created partition {partition}")
app.set_sco_dept(formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(formsemestre_id)
return jsonify(partition.to_dict(with_groups=True))
@bp.route("/partition/<int:partition_id>/edit", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def partition_edit(partition_id: int):
"""Modification d'une partition dans un semestre
The request content type should be "application/json"
All fields are optional:
{
"partition_name": str,
"numero":int,
"bul_show_rank":bool,
"show_in_lists":bool,
"groups_editable":bool
}
"""
partition = Partition.query.get_or_404(partition_id)
data = request.get_json(force=True) # may raise 400 Bad Request
modified = False
partition_name = data.get("partition_name")
if partition_name is not None and partition_name != partition.partition_name:
if not Partition.check_name(
partition.formsemestre, partition_name, existing=True
):
abort(404, "invalid partition_name")
partition.partition_name = partition_name.strip()
modified = True
numero = data.get("numero")
if numero is not None and numero != partition.numero:
if not isinstance(numero, int):
abort(404, "invalid type for numero")
partition.numero = numero
modified = True
for boolean_field in ("bul_show_rank", "show_in_lists", "groups_editable"):
value = data.get(boolean_field)
if value is not None and value != getattr(partition, boolean_field):
if not isinstance(value, bool):
abort(404, f"invalid type for {boolean_field}")
setattr(partition, boolean_field, value)
modified = True
if modified:
db.session.add(partition)
db.session.commit()
log(f"modified partition {partition}")
app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(partition.formsemestre_id)
return jsonify(partition.to_dict(with_groups=True))
@bp.route("/partition/<int:partition_id>/delete", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def partition_delete(partition_id: int):
"""Suppression d'une partition (et de tous ses groupes).
Note 1: La partition par défaut (tous les étudiants du sem.) ne peut
pas être supprimée.
Note 2: Si la partition de parcours est supprimée, les étudiants
sont désinscrits des parcours.
"""
partition = Partition.query.get_or_404(partition_id)
if not partition.partition_name:
abort(404, "ne peut pas supprimer la partition par défaut")
is_parcours = partition.is_parcours()
formsemestre: FormSemestre = partition.formsemestre
log(f"deleting partition {partition}")
db.session.delete(partition)
db.session.commit()
app.set_sco_dept(partition.formsemestre.departement.acronym)
sco_cache.invalidate_formsemestre(formsemestre.id)
if is_parcours:
formsemestre.update_inscriptions_parcours_from_groups()
return jsonify({"OK": 1})

View File

@ -1,11 +1,22 @@
##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""ScoDoc 9 API : outils
"""
from sqlalchemy import desc
from app import models from app import models
from app.api.errors import error_response
from app.models import Identite, Admission
def get_etud_from_etudid_or_nip_or_ine( def get_etud(etudid=None, nip=None, ine=None) -> models.Identite:
etudid=None, nip=None, ine=None
) -> models.Identite:
""" """
etudiant en fonction de l'etudid, code nip et code ine rentré en paramètres L'instance d'étudiant la plus récente en fonction de l'etudid,
ou du code nip ou code ine.
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
@ -13,12 +24,16 @@ def get_etud_from_etudid_or_nip_or_ine(
Return None si étudiant inexistant. Return None si étudiant inexistant.
""" """
if etudid is None: if etudid is not None:
if nip is None: # si ine return Identite.query.get(etudid)
etud = models.Identite.query.filter_by(code_ine=str(ine)).first()
else: # si nip
etud = models.Identite.query.filter_by(code_nip=str(nip)).first()
else: # si etudid
etud = models.Identite.query.filter_by(id=etudid).first()
return etud 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",
)
return query.join(Admission).order_by(desc(Admission.annee)).first()

View File

@ -33,6 +33,7 @@ Rappel: pour éviter les confusions, on appelera *poids* les coefficients d'une
évaluation dans un module, et *coefficients* ceux utilisés pour le calcul de la évaluation dans un module, et *coefficients* ceux utilisés pour le calcul de la
moyenne générale d'une UE. moyenne générale d'une UE.
""" """
import dataclasses
from dataclasses import dataclass from dataclasses import dataclass
import numpy as np import numpy as np
import pandas as pd import pandas as pd
@ -54,6 +55,10 @@ class EvaluationEtat:
nb_attente: int nb_attente: int
is_complete: bool is_complete: bool
def to_dict(self):
"convert to dict"
return dataclasses.asdict(self)
class ModuleImplResults: class ModuleImplResults:
"""Classe commune à toutes les formations (standard et APC). """Classe commune à toutes les formations (standard et APC).
@ -236,6 +241,16 @@ class ModuleImplResults:
self.evals_notes.values > scu.NOTES_ABSENCE, self.evals_notes.values, 0.0 self.evals_notes.values > scu.NOTES_ABSENCE, self.evals_notes.values, 0.0
) / [e.note_max / 20.0 for e in moduleimpl.evaluations] ) / [e.note_max / 20.0 for e in moduleimpl.evaluations]
def get_eval_notes_dict(self, evaluation_id: int) -> dict:
"""Notes d'une évaulation, brutes, sous forme d'un dict
{ etudid : valeur }
avec les valeurs float, ou "ABS" ou EXC
"""
return {
etudid: scu.fmt_note(x, keep_numeric=True)
for (etudid, x) in self.evals_notes[evaluation_id].items()
}
def get_evaluation_rattrapage(self, moduleimpl: ModuleImpl): def get_evaluation_rattrapage(self, moduleimpl: ModuleImpl):
"""L'évaluation de rattrapage de ce module, ou None s'il n'en a pas. """L'évaluation de rattrapage de ce module, ou None s'il n'en a pas.
Rattrapage: la moyenne du module est la meilleure note entre moyenne Rattrapage: la moyenne du module est la meilleure note entre moyenne

View File

@ -426,9 +426,16 @@ class ResultatsSemestre(ResultatsCache):
# --- TABLEAU RECAP # --- TABLEAU RECAP
def get_table_recap( def get_table_recap(
self, convert_values=False, include_evaluations=False, mode_jury=False self,
convert_values=False,
include_evaluations=False,
mode_jury=False,
allow_html=True,
): ):
"""Result: tuple avec """Table récap. des résultats.
allow_html: si vri, peut-mettre du HTML dans les valeurs
Result: tuple avec
- rows: liste de dicts { column_id : value } - rows: liste de dicts { column_id : value }
- titles: { column_id : title } - titles: { column_id : title }
- columns_ids: (liste des id de colonnes) - columns_ids: (liste des id de colonnes)
@ -591,7 +598,7 @@ class ResultatsSemestre(ResultatsCache):
row, row,
f"bonus_ue_{ue.id}", f"bonus_ue_{ue.id}",
f"Bonus {ue.acronyme}", f"Bonus {ue.acronyme}",
val_fmt_html, val_fmt_html if allow_html else val_fmt,
"col_ue_bonus", "col_ue_bonus",
idx, idx,
) )

View File

@ -1,4 +1,9 @@
# -*- coding: UTF-8 -* # -*- coding: UTF-8 -*
##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""ScoDoc models: formsemestre """ScoDoc models: formsemestre
""" """
@ -141,8 +146,11 @@ class FormSemestre(db.Model):
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__} {self.id} {self.titre_num()}>" return f"<{self.__class__.__name__} {self.id} {self.titre_num()}>"
def to_dict(self, convert_parcours=False): def to_dict(self, convert_objects=False) -> dict:
"dict (compatible ScoDoc7)" """dict (compatible ScoDoc7).
If convert_objects, convert all attributes to native types
(suitable jor json encoding).
"""
d = dict(self.__dict__) d = dict(self.__dict__)
d.pop("_sa_instance_state", None) d.pop("_sa_instance_state", None)
# ScoDoc7 output_formators: (backward compat) # ScoDoc7 output_formators: (backward compat)
@ -160,10 +168,35 @@ class FormSemestre(db.Model):
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() d["titre_formation"] = self.titre_formation()
if convert_parcours: if convert_objects:
d["parcours"] = [p.to_dict() for p in self.parcours] d["parcours"] = [p.to_dict() for p in self.parcours]
return d return d
def to_dict_api(self):
"""
Un dict avec les informations sur le semestre destiné à l'api
"""
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
d["annee_scolaire"] = self.annee_scolaire_str()
d["formsemestre_id"] = self.id
d["titre_num"] = self.titre_num()
if self.date_debut:
d["date_debut"] = self.date_debut.strftime("%d/%m/%Y")
d["date_debut_iso"] = self.date_debut.isoformat()
else:
d["date_debut"] = d["date_debut_iso"] = ""
if self.date_fin:
d["date_fin"] = self.date_fin.strftime("%d/%m/%Y")
d["date_fin_iso"] = self.date_fin.isoformat()
else:
d["date_fin"] = d["date_fin_iso"] = ""
d["responsables"] = [u.id for u in self.responsables]
d["titre_court"] = self.formation.acronyme
d["parcours"] = [p.to_dict() for p in self.parcours]
d["session_id"] = self.session_id()
return d
def get_infos_dict(self) -> dict: def get_infos_dict(self) -> dict:
"""Un dict avec des informations sur le semestre """Un dict avec des informations sur le semestre
pour les bulletins et autres templates pour les bulletins et autres templates

View File

@ -1,12 +1,17 @@
# -*- coding: UTF-8 -* # -*- coding: UTF-8 -*
##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""Groups & partitions """ScoDoc models: Groups & partitions
""" """
from typing import Any
from app import db from app import db
from app.models import SHORT_STR_LEN from app.models import SHORT_STR_LEN
from app.models import GROUPNAME_STR_LEN from app.models import GROUPNAME_STR_LEN
from app.scodoc import sco_utils as scu
class Partition(db.Model): class Partition(db.Model):
@ -41,6 +46,7 @@ class Partition(db.Model):
"GroupDescr", "GroupDescr",
backref=db.backref("partition", lazy=True), backref=db.backref("partition", lazy=True),
lazy="dynamic", lazy="dynamic",
cascade="all, delete-orphan",
) )
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -56,14 +62,33 @@ 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)'}">"""
@classmethod
def check_name(
cls, formsemestre: "FormSemestre", partition_name: str, existing=False
) -> bool:
"""check if a partition named 'partition_name' can be created in the given formsemestre.
If existing is True, allow a partition_name already existing in the formsemestre.
"""
if not isinstance(partition_name, str):
return False
if not len(partition_name.strip()) > 0:
return False
if (not existing) and (
partition_name in [p.partition_name for p in formsemestre.partitions]
):
return False
return True
def is_parcours(self) -> bool:
"Vrai s'il s'agit de la partitoon de parcours"
return self.partition_name == scu.PARTITION_PARCOURS
def to_dict(self, with_groups=False) -> dict: def to_dict(self, with_groups=False) -> dict:
"""as a dict, with or without groups""" """as a dict, with or without groups"""
d = { d = dict(self.__dict__)
"id": self.id, d.pop("_sa_instance_state", None)
"formsemestre_id": self.partition_id, d.pop("formsemestre", None)
"name": self.partition_name,
"numero": self.numero,
}
if with_groups: if with_groups:
d["groups"] = [group.to_dict(with_partition=False) for group in self.groups] d["groups"] = [group.to_dict(with_partition=False) for group in self.groups]
return d return d
@ -107,6 +132,21 @@ class GroupDescr(db.Model):
d["partition"] = self.partition.to_dict(with_groups=False) d["partition"] = self.partition.to_dict(with_groups=False)
return d return d
@classmethod
def check_name(
cls, partition: "Partition", group_name: str, existing=False
) -> bool:
"""check if a group named 'group_name' can be created in the given partition.
If existing is True, allow a group_name already existing in the partition.
"""
if not isinstance(group_name, str):
return False
if not len(group_name.strip()) > 0:
return False
if (not existing) and (group_name in [g.group_name for g in partition.groups]):
return False
return True
group_membership = db.Table( group_membership = db.Table(
"group_membership", "group_membership",

View File

@ -79,17 +79,23 @@ class ModuleImpl(db.Model):
self.module.formation.get_module_coefs(self.module.semestre_id), self.module.formation.get_module_coefs(self.module.semestre_id),
) )
def to_dict(self): def to_dict(self, convert_objects=False, with_module=True):
"""as a dict, with the same conversions as in ScoDoc7, including module""" """as a dict, with the same conversions as in ScoDoc7, including module.
e = dict(self.__dict__) If convert_objects, convert all attributes to native types
e.pop("_sa_instance_state", None) (suitable jor json encoding).
"""
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
# ScoDoc7 output_formators: (backward compat) # ScoDoc7 output_formators: (backward compat)
e["moduleimpl_id"] = self.id d["moduleimpl_id"] = self.id
e["ens"] = [ d["ens"] = [
{"moduleimpl_id": self.id, "ens_id": e.id} for e in self.enseignants {"moduleimpl_id": self.id, "ens_id": e.id} for e in self.enseignants
] ]
e["module"] = self.module.to_dict() if with_module:
return e d["module"] = self.module.to_dict(convert_objects=convert_objects)
else:
d.pop("module", None)
return d
# Enseignants (chargés de TD ou TP) d'un moduleimpl # Enseignants (chargés de TD ou TP) d'un moduleimpl

View File

@ -67,19 +67,33 @@ class Module(db.Model):
def __repr__(self): def __repr__(self):
return f"<Module{ModuleType(self.module_type or ModuleType.STANDARD).name} id={self.id} code={self.code!r}>" return f"<Module{ModuleType(self.module_type or ModuleType.STANDARD).name} id={self.id} code={self.code!r}>"
def to_dict(self): def to_dict(self, convert_objects=False, with_matiere=False, with_ue=False) -> dict:
e = dict(self.__dict__) """If convert_objects, convert all attributes to native types
e.pop("_sa_instance_state", None) (suitable jor json encoding).
"""
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
if convert_objects:
d["parcours"] = [p.to_dict() for p in self.parcours]
if not with_matiere:
d.pop("matiere", None)
if not with_ue:
d.pop("ue", None)
if convert_objects and with_matiere:
d["matiere"] = self.matiere.to_dict(convert_objects=True)
if convert_objects and with_ue:
d["ue"] = self.ue.to_dict(convert_objects=True)
# ScoDoc7 output_formators: (backward compat) # ScoDoc7 output_formators: (backward compat)
e["module_id"] = self.id d["module_id"] = self.id
e["heures_cours"] = 0.0 if self.heures_cours is None else self.heures_cours d["heures_cours"] = 0.0 if self.heures_cours is None else self.heures_cours
e["heures_td"] = 0.0 if self.heures_td is None else self.heures_td d["heures_td"] = 0.0 if self.heures_td is None else self.heures_td
e["heures_tp"] = 0.0 if self.heures_tp is None else self.heures_tp d["heures_tp"] = 0.0 if self.heures_tp is None else self.heures_tp
e["numero"] = 0 if self.numero is None else self.numero d["numero"] = 0 if self.numero is None else self.numero
e["coefficient"] = 0.0 if self.coefficient is None else self.coefficient d["coefficient"] = 0.0 if self.coefficient is None else self.coefficient
e["module_type"] = 0 if self.module_type is None else self.module_type d["module_type"] = 0 if self.module_type is None else self.module_type
e["code_apogee"] = e["code_apogee"] or "" # pas de None d["code_apogee"] = d["code_apogee"] or "" # pas de None
return e return d
def is_apc(self): def is_apc(self):
"True si module SAÉ ou Ressource" "True si module SAÉ ou Ressource"
@ -220,6 +234,14 @@ class ModuleUECoef(db.Model):
), ),
) )
def to_dict(self, convert_objects=False) -> dict:
"""If convert_objects, convert all attributes to native types
(suitable for json encoding).
"""
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
return d
class NotesTag(db.Model): class NotesTag(db.Model):
"""Tag sur un module""" """Tag sur un module"""

View File

@ -47,6 +47,12 @@ class NotesNotes(db.Model):
date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
uid = db.Column(db.Integer, db.ForeignKey("user.id")) uid = db.Column(db.Integer, db.ForeignKey("user.id"))
def to_dict(self) -> dict:
"dict"
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
return d
class NotesNotesLog(db.Model): class NotesNotesLog(db.Model):
"""Historique des modifs sur notes (anciennes entrees de notes_notes)""" """Historique des modifs sur notes (anciennes entrees de notes_notes)"""

View File

@ -59,18 +59,25 @@ class UniteEns(db.Model):
self.semestre_idx} { self.semestre_idx} {
'EXTERNE' if self.is_external else ''})>""" 'EXTERNE' if self.is_external else ''})>"""
def to_dict(self): def to_dict(self, convert_objects=False):
"""as a dict, with the same conversions as in ScoDoc7 """as a dict, with the same conversions as in ScoDoc7
(except ECTS: keep None) (except ECTS: keep None)
If convert_objects, convert all attributes to native types
(suitable jor json encoding).
""" """
e = dict(self.__dict__) e = dict(self.__dict__)
e.pop("_sa_instance_state", None) e.pop("_sa_instance_state", None)
e.pop("evaluation_ue_poids", None)
# ScoDoc7 output_formators # ScoDoc7 output_formators
e["ue_id"] = self.id e["ue_id"] = self.id
e["numero"] = e["numero"] if e["numero"] else 0 e["numero"] = e["numero"] if e["numero"] else 0
e["ects"] = e["ects"] e["ects"] = e["ects"]
e["coefficient"] = e["coefficient"] if e["coefficient"] else 0.0 e["coefficient"] = e["coefficient"] if e["coefficient"] else 0.0
e["code_apogee"] = e["code_apogee"] or "" # pas de None e["code_apogee"] = e["code_apogee"] or "" # pas de None
if convert_objects:
e["module_ue_coefs"] = [
c.to_dict(convert_objects=True) for c in self.module_ue_coefs
]
return e return e
def is_locked(self): def is_locked(self):

View File

@ -131,12 +131,14 @@ class EvaluationCache(ScoDocCache):
@classmethod @classmethod
def invalidate_sem(cls, formsemestre_id): def invalidate_sem(cls, formsemestre_id):
"delete evaluations in this formsemestre from cache" "delete evaluations in this formsemestre from cache"
req = """SELECT e.id from app.models.evaluations import Evaluation
FROM notes_formsemestre s, notes_evaluation e, notes_moduleimpl m from app.models.moduleimpls import ModuleImpl
WHERE s.id = %(formsemestre_id)s and s.id=m.formsemestre_id and e.moduleimpl_id=m.id;
"""
evaluation_ids = [ evaluation_ids = [
x[0] for x in ndb.SimpleQuery(req, {"formsemestre_id": formsemestre_id}) e.id
for e in Evaluation.query.join(ModuleImpl).filter_by(
formsemestre_id=formsemestre_id
)
] ]
cls.delete_many(evaluation_ids) cls.delete_many(evaluation_ids)

View File

@ -1,4 +1,3 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
@ -27,15 +26,15 @@
"""Gestion des cursus (jurys suivant la formation) """Gestion des cursus (jurys suivant la formation)
""" """
from sqlalchemy.sql import text
from app import db
from app.but import cursus_but from app.but import cursus_but
from app.scodoc import sco_cursus_dut from app.scodoc import sco_cursus_dut
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.comp import res_sem from app.comp import res_sem
from app.models import FormSemestre from app.models import FormSemestre
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formations
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
# SituationEtudParcours -> get_situation_etud_cursus # SituationEtudParcours -> get_situation_etud_cursus
@ -111,24 +110,26 @@ def list_formsemestre_utilisateurs_uecap(formsemestre_id):
"""Liste des formsemestres pouvant utiliser une UE capitalisee de ce semestre """Liste des formsemestres pouvant utiliser une UE capitalisee de ce semestre
(et qui doivent donc etre sortis du cache si l'on modifie ce (et qui doivent donc etre sortis du cache si l'on modifie ce
semestre): meme code formation, meme semestre_id, date posterieure""" semestre): meme code formation, meme semestre_id, date posterieure"""
cnx = ndb.GetDBConnexion() formsemestre = FormSemestre.query.get(formsemestre_id)
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] cursor = db.session.execute(
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) text(
cursor.execute( """
"""SELECT sem.id SELECT sem.id
FROM notes_formsemestre sem, notes_formations F FROM notes_formsemestre sem, notes_formations F
WHERE sem.formation_id = F.id WHERE sem.formation_id = F.id
and F.formation_code = %(formation_code)s and F.formation_code = :formation_code
and sem.semestre_id = %(semestre_id)s and sem.semestre_id = :semestre_id
and sem.date_debut >= %(date_debut)s and sem.date_debut >= :date_debut
and sem.id != %(formsemestre_id)s; and sem.id != :formsemestre_id;
""", """
),
{ {
"formation_code": F["formation_code"], "formation_code": formsemestre.formation.formation_code,
"semestre_id": sem["semestre_id"], "semestre_id": formsemestre.semestre_id,
"formsemestre_id": formsemestre_id, "formsemestre_id": formsemestre_id,
"date_debut": ndb.DateDMYtoISO(sem["date_debut"]), "date_debut": formsemestre.date_debut,
}, },
) )
return [x[0] for x in cursor.fetchall()]
return [x[0] for x in cursor]

View File

@ -136,7 +136,7 @@ def get_partition(partition_id):
return r[0] return r[0]
def get_partitions_list(formsemestre_id, with_default=True): def get_partitions_list(formsemestre_id, with_default=True) -> list[dict]:
"""Liste des partitions pour ce semestre (list of dicts)""" """Liste des partitions pour ce semestre (list of dicts)"""
partitions = ndb.SimpleDictFetch( partitions = ndb.SimpleDictFetch(
"""SELECT p.id AS partition_id, p.* """SELECT p.id AS partition_id, p.*
@ -146,9 +146,9 @@ def get_partitions_list(formsemestre_id, with_default=True):
{"formsemestre_id": formsemestre_id}, {"formsemestre_id": formsemestre_id},
) )
# Move 'all' at end of list (for menus) # Move 'all' at end of list (for menus)
R = [p for p in partitions if p["partition_name"] != None] R = [p for p in partitions if p["partition_name"] is not None]
if with_default: if with_default:
R += [p for p in partitions if p["partition_name"] == None] R += [p for p in partitions if p["partition_name"] is None]
return R return R
@ -179,6 +179,24 @@ def get_formsemestre_groups(formsemestre_id, with_default=False):
return partitions, partitions_etud_groups return partitions, partitions_etud_groups
def get_formsemestre_etuds_groups(formsemestre_id: int) -> dict:
"""{ etudid : { partition_id : group_id } }"""
infos = ndb.SimpleDictFetch(
"""SELECT etudid, p.id AS partition_id, gd.id AS group_id
FROM group_descr gd, group_membership gm, partition p
WHERE gd.partition_id = p.id
AND gm.group_id = gd.id
AND p.formsemestre_id = %(formsemestre_id)s
""",
{"formsemestre_id": formsemestre_id},
)
# -> {'etudid': 16483, 'group_id': 5317, 'partition_id': 2264},
d = collections.defaultdict(lambda: {})
for i in infos:
d[i["etudid"]][i["partition_id"]] = i["group_id"]
return d
def get_partition_groups(partition): def get_partition_groups(partition):
"""List of groups in this partition (list of dicts). """List of groups in this partition (list of dicts).
Some groups may be empty.""" Some groups may be empty."""

View File

@ -51,7 +51,7 @@ _SCO_PERMISSIONS = (
# 27 à 39 ... réservé pour "entreprises" # 27 à 39 ... réservé pour "entreprises"
# Api scodoc9 # Api scodoc9
(1 << 40, "APIView", "API: Lecture"), (1 << 40, "APIView", "API: Lecture"),
(1 << 41, "APIEtudChangeGroups", "API: Modifier les groupes"), (1 << 41, "APIEditGroups", "API: Modifier les groupes"),
(1 << 42, "APIEditAllNotes", "API: Modifier toutes les notes"), (1 << 42, "APIEditAllNotes", "API: Modifier toutes les notes"),
(1 << 43, "APIAbsChange", "API: Saisir des absences"), (1 << 43, "APIAbsChange", "API: Saisir des absences"),
) )

View File

@ -1893,6 +1893,8 @@ class BasePreferences(object):
# log(f"loading preferences for dept_id={self.dept_id}") # log(f"loading preferences for dept_id={self.dept_id}")
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
if self.dept_id:
g.scodoc_dept_id = self.dept_id
preflist = self._editor.list(cnx, {"dept_id": self.dept_id}) preflist = self._editor.list(cnx, {"dept_id": self.dept_id})
self.prefs = {None: {}} # { formsemestre_id (or None) : { name : value } } self.prefs = {None: {}} # { formsemestre_id (or None) : { name : value } }
self.default = {} # { name : default_value } self.default = {} # { name : default_value }

View File

@ -65,6 +65,8 @@ class DevConfig(Config):
os.environ.get("SCODOC_DATABASE_URI") or "postgresql:///SCODOC_DEV" os.environ.get("SCODOC_DATABASE_URI") or "postgresql:///SCODOC_DEV"
) )
SECRET_KEY = os.environ.get("DEV_SECRET_KEY") or "bb3faec7d9a34eb68a8e3e710087d87a" SECRET_KEY = os.environ.get("DEV_SECRET_KEY") or "bb3faec7d9a34eb68a8e3e710087d87a"
# pour le avoir url_for dans le shell:
# SERVER_NAME = os.environ.get("SCODOC_TEST_SERVER_NAME") or "localhost"
class TestConfig(DevConfig): class TestConfig(DevConfig):

View File

@ -20,6 +20,7 @@ Travail en cours.
""" """
from dotenv import load_dotenv from dotenv import load_dotenv
import json
import os import os
import requests import requests
import urllib3 import urllib3
@ -35,8 +36,13 @@ load_dotenv(os.path.join(BASEDIR, ".env"))
CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False))) CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
SCODOC_URL = os.environ.get("SCODOC_URL") or "http://localhost:5000" SCODOC_URL = os.environ.get("SCODOC_URL") or "http://localhost:5000"
API_URL = SCODOC_URL + "/ScoDoc/api" API_URL = SCODOC_URL + "/ScoDoc/api"
# Admin:
SCODOC_USER = os.environ["SCODOC_USER"] SCODOC_USER = os.environ["SCODOC_USER"]
SCODOC_PASSWORD = os.environ["SCODOC_PASSWORD"] SCODOC_PASSWORD = os.environ["SCODOC_PASSWORD"]
# Lecteur
SCODOC_USER_API_LECTEUR = os.environ["SCODOC_USER_API_LECTEUR"]
SCODOC_PASSWORD_API_LECTEUR = os.environ["SCODOC_PASSWORD_API_LECTEUR"]
print(f"SCODOC_URL={SCODOC_URL}") print(f"SCODOC_URL={SCODOC_URL}")
print(f"API URL={API_URL}") print(f"API URL={API_URL}")
@ -51,25 +57,48 @@ 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(API_URL + "/" + path, headers=headers or HEADERS, verify=CHK_CERT) r = requests.get(API_URL + path, headers=headers or HEADERS, verify=CHK_CERT)
if r.status_code != 200: if r.status_code != 200:
raise ScoError(errmsg or "erreur !") raise ScoError(errmsg or f"erreur status={r.status_code} !")
return r.json() # decode la reponse JSON return r.json() # decode la reponse JSON
def POST(s, path: str, data: dict, errmsg=None): def POST(path: str, data: dict = {}, headers={}, errmsg=None):
"""Post""" """Post"""
r = s.post(API_URL + "/" + path, data=data, verify=CHK_CERT) r = requests.post(
API_URL + path,
data=data,
headers=headers or HEADERS,
verify=CHK_CERT,
)
if r.status_code != 200: if r.status_code != 200:
raise ScoError(errmsg or "erreur !") raise ScoError(errmsg or f"erreur status={r.status_code} !")
return r.text return r.json() # decode la reponse JSON
# --- Obtention du jeton (token) def POST_JSON(path: str, data: dict = {}, headers={}, errmsg=None):
r = requests.post(API_URL + "/tokens", auth=(SCODOC_USER, SCODOC_PASSWORD)) """Post"""
assert r.status_code == 200 r = requests.post(
token = r.json()["token"] API_URL + path,
HEADERS = {"Authorization": f"Bearer {token}"} json=data,
headers=headers or HEADERS,
verify=CHK_CERT,
)
if r.status_code != 200:
raise ScoError(errmsg or f"erreur status={r.status_code} !")
return r.json() # decode la reponse JSON
def GET_TOKEN(user, password):
"Obtention du jeton (token)"
r = requests.post(API_URL + "/tokens", auth=(user, password))
assert r.status_code == 200
token = r.json()["token"]
return {"Authorization": f"Bearer {token}"}
HEADERS = GET_TOKEN(SCODOC_USER, SCODOC_PASSWORD)
HEADERS_USER = GET_TOKEN(SCODOC_USER_API_LECTEUR, SCODOC_PASSWORD_API_LECTEUR)
r = requests.get(API_URL + "/departements", headers=HEADERS, verify=CHK_CERT) r = requests.get(API_URL + "/departements", headers=HEADERS, verify=CHK_CERT)
if r.status_code != 200: if r.status_code != 200:
@ -113,8 +142,52 @@ print("\n".join([s["titre_num"] for s in sems]))
# Evaluation # Evaluation
evals = GET("/evaluations/1") evals = GET("/evaluations/1")
# # --- Recupere la liste de tous les semestres: # Partitions d'un BUT
# sems = GET(s, "Notes/formsemestre_list?format=json", "Aucun semestre !") formsemestre_id = 1063 # A adapter
partitions = GET(f"/formsemestre/{formsemestre_id}/partitions")
print(partitions)
pid = partitions[1]["id"]
partition = GET(f"/partition/{pid}")
print(partition)
group_id = partition["groups"][0]["id"]
etuds = GET(f"/group/{group_id}/etudiants")
print(f"{len(etuds)} étudiants")
pp(etuds[1])
etuds_dem = GET(f"/group/{group_id}/etudiants/query?etat=D")
print(f"{len(etuds_dem)} étudiants")
etudid = 16650
group_id = 5315
POST(f"/group/{group_id}/set_etudiant/{etudid}")
POST_JSON(f"/partition/{pid}/group/create", data={"group_name": "Omega10"})
partitions = GET(f"/formsemestre/{formsemestre_id}/partitions")
pp(partitions)
POST_JSON(f"/group/5559/delete")
POST_JSON(f"/group/5327/edit", data={"group_name": "TDXXX"})
POST_JSON(
f"/formsemestre/{formsemestre_id}/partition/create",
data={"partition_name": "PXXXXYY"},
)
POST_JSON(
f"/partition/{2379}/edit",
data={"partition_name": "---PPPP", "show_in_lists": True},
)
POST_JSON(f"/partition/{2379}/delete")
# Recherche de formsemestres
sems = GET(f"/formsemestres/query?etape_apo=V1RT&annee_scolaire=2021")
# Table récap:
pp(GET(f"/formsemestre/1063/resultats")[0])
pp(GET(f"/formsemestre/880/resultats")[0])
# # sems est une liste de semestres (dictionnaires) # # sems est une liste de semestres (dictionnaires)
# for sem in sems: # for sem in sems:

View File

@ -21,13 +21,22 @@ import requests
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
# Etudiant pour les tests # Etudiant pour les tests
from tests.api.tools_test_api import (
verify_fields,
ABSENCES_FIELDS,
ABSENCES_GROUP_ETAT_FIELDS,
)
ETUDID = 1 ETUDID = 1
# absences # absences
def test_absences(api_headers): def test_absences(api_headers):
""" """
Route: /absences/etudid/<int:etudid> Test 'absences'
Route :
- /absences/etudid/<int:etudid>
""" """
r = requests.get( r = requests.get(
f"{API_URL}/absences/etudid/{ETUDID}", f"{API_URL}/absences/etudid/{ETUDID}",
@ -35,29 +44,165 @@ def test_absences(api_headers):
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
absences = r.json()
assert isinstance(absences, list)
for abs in absences:
assert verify_fields(abs, ABSENCES_FIELDS) is True
assert isinstance(abs["jour"], str)
assert isinstance(abs["matin"], bool)
assert isinstance(abs["estabs"], bool)
assert isinstance(abs["estjust"], bool)
assert isinstance(abs["description"], str)
assert isinstance(abs["begin"], str)
assert isinstance(abs["end"], str)
assert abs["begin"] < abs["end"]
# absences_justify # absences_justify
def test_absences_justify(api_headers): def test_absences_justify(api_headers):
""" """
Route: /absences/etudid/<etudid:int>/just Test 'absences_just'
Route :
- /absences/etudid/<int:etudid>/just
""" """
r = requests.get( r = requests.get(
API_URL + f"/absences/etudid/{ETUDID}/just", f"{API_URL}/absences/etudid/{ETUDID}/just",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
# TODO vérifier résultat absences = r.json()
assert isinstance(absences, list)
for abs in absences:
assert verify_fields(abs, ABSENCES_FIELDS) is True
assert isinstance(abs["jour"], str)
assert isinstance(abs["matin"], bool)
assert isinstance(abs["estabs"], bool)
assert isinstance(abs["estjust"], bool)
assert isinstance(abs["description"], str)
assert isinstance(abs["begin"], str)
assert isinstance(abs["end"], str)
assert abs["begin"] < abs["end"]
def test_abs_groupe_etat(api_headers):
"""
Test 'abs_groupe_etat'
Routes :
- /absences/abs_group_etat/<int:group_id>
- /absences/abs_group_etat/group_id/<int:group_id>/date_debut/<string:date_debut>/date_fin/<string:date_fin>
"""
group_id = 1
r = requests.get(
f"{API_URL}/absences/abs_group_etat/{group_id}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
list_absences = r.json()
assert isinstance(list_absences, list)
list_id_etu = []
for etu in list_absences:
list_id_etu.append(etu["etudid"])
assert verify_fields(etu, ABSENCES_GROUP_ETAT_FIELDS) is True
assert isinstance(etu["etudid"], int)
assert isinstance(etu["list_abs"], list)
list_abs = etu["list_abs"]
for abs in list_abs:
assert verify_fields(abs, ABSENCES_FIELDS) is True
assert isinstance(abs["jour"], str)
assert isinstance(abs["matin"], bool)
assert isinstance(abs["estabs"], bool)
assert isinstance(abs["estjust"], bool)
assert isinstance(abs["description"], str)
assert isinstance(abs["begin"], str)
assert isinstance(abs["end"], str)
assert abs["begin"] < abs["end"]
all_unique = True
for id in list_id_etu:
if list_id_etu.count(id) > 1:
all_unique = False
assert all_unique is True
date_debut = "Fri, 15 Apr 2021 00:00:00 GMT"
date_fin = "Fri, 18 Apr 2022 00:00:00 GMT"
r1 = requests.get(
f"{API_URL}/absences/abs_group_etat/group_id/{group_id}/date_debut/{date_debut}/date_fin/{date_fin}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r1.status_code == 200
list_absences1 = r.json()
assert isinstance(list_absences1, list)
list_id_etu1 = []
for etu in list_absences1:
list_id_etu1.append(etu["etudid"])
assert verify_fields(etu, ABSENCES_GROUP_ETAT_FIELDS) is True
assert isinstance(etu["etudid"], int)
assert isinstance(etu["list_abs"], list)
list_abs1 = etu["list_abs"]
for abs in list_abs1:
assert verify_fields(abs, ABSENCES_FIELDS) is True
assert isinstance(abs["jour"], str)
assert isinstance(abs["matin"], bool)
assert isinstance(abs["estabs"], bool)
assert isinstance(abs["estjust"], bool)
assert isinstance(abs["description"], str)
assert isinstance(abs["begin"], str)
assert isinstance(abs["end"], str)
assert abs["begin"] < abs["end"]
all_unique1 = True
for id in list_id_etu1:
if list_id_etu1.count(id) > 1:
all_unique1 = False
assert all_unique1 is True
# XXX TODO # XXX TODO
# def test_abs_groupe_etat(api_headers): # def reset_etud_abs(api_headers):
# """ # """
# Route: # Test 'reset_etud_abs'
#
# Routes :
# - /absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs
# - /absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs/only_not_just
# - /absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs/only_just
# """ # """
# list_abs = []
# r = requests.get( # r = requests.get(
# API_URL + "/absences/abs_group_etat/?group_id=<int:group_id>&date_debut=date_debut&date_fin=date_fin", # f"{API_URL}/absences/etudid/{ETUDID}/list_abs/{list_abs}/reset_etud_abs",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
#
# r_only_not_just = requests.get(
# f"{API_URL}/absences/etudid/{ETUDID}/list_abs/{list_abs}/reset_etud_abs/only_not_just",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
#
#
# r_only_just = requests.get(
# f"{API_URL}/absences/etudid/{ETUDID}/list_abs/{list_abs}/reset_etud_abs/only_just",
# headers=api_headers, # headers=api_headers,
# verify=CHECK_CERTIFICATE, # verify=CHECK_CERTIFICATE,
# ) # )

View File

@ -19,29 +19,20 @@ Utilisation :
import requests import requests
from tests.api.setup_test_api import ( from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
API_URL, from tests.api.tools_test_api import (
CHECK_CERTIFICATE, verify_fields,
DEPT_ACRONYM, DEPARTEMENT_FIELDS,
api_headers, FORMSEMESTRE_FIELDS,
verify_occurences_ids_etus,
) )
from tests.api.tools_test_api import verify_fields
DEPARTEMENT_FIELDS = [
"id",
"acronym",
"description",
"visible",
"date_creation",
]
def test_departements(api_headers): def test_departements(api_headers):
""" "
Routes: /departements_ids, /departement, /departement/<string:dept>/formsemestres_ids
""" """
# --- Liste des ids Routes: /departements_ids, /departement, /departement/<string:dept>/formsemestres_ids
"""
# --- departement_ids : liste des ids
r = requests.get( r = requests.get(
API_URL + "/departements_ids", API_URL + "/departements_ids",
headers=api_headers, headers=api_headers,
@ -53,8 +44,17 @@ def test_departements(api_headers):
assert len(departements_ids) > 0 assert len(departements_ids) > 0
assert all(isinstance(x, int) for x in departements_ids) assert all(isinstance(x, int) for x in departements_ids)
all_unique = True
for id in departements_ids:
if departements_ids.count(id) > 1:
all_unique = False
assert all_unique is True
dept_id = departements_ids[0] dept_id = departements_ids[0]
# --- Infos sur un département, accès par id
# --- departement
# Infos sur un département, accès par id
r = requests.get( r = requests.get(
f"{API_URL}/departement/{dept_id}", f"{API_URL}/departement/{dept_id}",
headers=api_headers, headers=api_headers,
@ -62,8 +62,7 @@ def test_departements(api_headers):
) )
assert r.status_code == 200 assert r.status_code == 200
dept_a = r.json() dept_a = r.json()
assert verify_fields(dept_a, DEPARTEMENT_FIELDS) is True # Infos sur un département, accès par acronyme4
# --- Infos sur un département, accès par acronyme4
r = requests.get( r = requests.get(
f"{API_URL}/departement/{dept_a['acronym']}", f"{API_URL}/departement/{dept_a['acronym']}",
headers=api_headers, headers=api_headers,
@ -71,65 +70,131 @@ def test_departements(api_headers):
) )
assert r.status_code == 200 assert r.status_code == 200
dept_b = r.json() dept_b = r.json()
assert dept_a == dept_b
# Liste des formsemestres assert dept_a == dept_b
assert verify_fields(dept_a, DEPARTEMENT_FIELDS) is True
assert isinstance(dept_a["id"], int)
assert isinstance(dept_a["acronym"], str)
assert dept_a["description"] is None or isinstance(dept_a["description"], str)
assert isinstance(dept_a["visible"], bool)
assert dept_a["date_creation"] is None or isinstance(dept_a["date_creation"], str)
# --- departements : Liste des départements
r = requests.get(
API_URL + "/departements",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
# --- formsemestre_ids : listes des ids de formsemestres du département
r = requests.get( r = requests.get(
f"{API_URL}/departement/{dept_a['acronym']}/formsemestres_ids", f"{API_URL}/departement/{dept_a['acronym']}/formsemestres_ids",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
dept_ids = r.json() dept_ids_a = r.json()
assert isinstance(dept_ids, list)
assert all(isinstance(x, int) for x in dept_ids) r = requests.get(
assert len(dept_ids) > 0 f"{API_URL}/departement/{dept_a['id']}/formsemestres_ids",
assert dept_id in dept_ids headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
dept_ids_b = r.json()
assert dept_ids_a == dept_ids_b
assert isinstance(dept_ids_a, list)
assert all(isinstance(id, int) for id in dept_ids_a)
assert len(dept_ids_a) > 0
assert dept_id in dept_ids_a
# Les erreurs
id_inexistant = 50000
r = requests.get(
f"{API_URL}/departement/{id_inexistant}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 404
r = requests.get(
f"{API_URL}/departement/{id_inexistant}/formsemestres_ids",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 404
acronym_inexistant = "AAAAAAAAAAAAAAAAAAA"
r = requests.get(
f"{API_URL}/departement/{acronym_inexistant}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 404
r = requests.get(
f"{API_URL}/departement/{acronym_inexistant}/formsemestres_ids",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 404
def test_list_etudiants(api_headers): def test_list_etudiants(api_headers):
fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"} fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"}
r = requests.get( r = requests.get(
f"{API_URL}/departement/{DEPT_ACRONYM}/etudiants", API_URL + "/departement/TAPI/etudiants",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
etud = r.json()[0] etud_a = r.json()[0]
assert verify_fields(etud, fields) is True
r = requests.get(
API_URL + "/departement/1/etudiants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud_b = r.json()[0]
assert etud_a == etud_b
assert verify_fields(etud_a, fields) is True
assert isinstance(etud_a["id"], int)
assert etud_a["nip"] is None or isinstance(etud_a["nip"], str)
assert etud_a["ine"] is None or isinstance(etud_a["ine"], str)
assert etud_a["nom"] is None or isinstance(etud_a["nom"], str)
assert etud_a["nom_usuel"] is None or isinstance(etud_a["nom_usuel"], str)
assert etud_a["prenom"] is None or isinstance(etud_a["prenom"], str)
assert isinstance(etud_a["civilite"], str)
assert len(etud_a["civilite"]) == 1
all_unique = verify_occurences_ids_etus(r.text)
assert all_unique is True
# Les erreurs
id_inexistant = 50000
r = requests.get(
f"{API_URL}/departement/{id_inexistant}/etudiants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 404
acronym_inexistant = "AAAAAAAAAAAAAAAAAAA"
r = requests.get(
f"{API_URL}/departement/{acronym_inexistant}/etudiants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 404
# liste_semestres_courant # liste_semestres_courant
def test_semestres_courant(api_headers): def test_semestres_courant(api_headers):
fields = [
"titre",
"gestion_semestrielle",
"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",
"titre_formation",
"date_debut_iso",
"date_fin_iso",
"responsables",
]
dept_id = 1 dept_id = 1
r = requests.get( r = requests.get(
f"{API_URL}/departement/{dept_id}", f"{API_URL}/departement/{dept_id}",
@ -139,6 +204,7 @@ def test_semestres_courant(api_headers):
assert r.status_code == 200 assert r.status_code == 200
dept = r.json() dept = r.json()
assert dept["id"] == dept_id assert dept["id"] == dept_id
# Accès via acronyme # Accès via acronyme
r = requests.get( r = requests.get(
f"{API_URL}/departement/{dept['acronym']}/formsemestres_courants", f"{API_URL}/departement/{dept['acronym']}/formsemestres_courants",
@ -147,10 +213,6 @@ def test_semestres_courant(api_headers):
) )
assert r.status_code == 200 assert r.status_code == 200
result_a = r.json() 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 # accès via dept_id
r = requests.get( r = requests.get(
@ -161,3 +223,7 @@ def test_semestres_courant(api_headers):
assert r.status_code == 200 assert r.status_code == 200
result_b = r.json() result_b = r.json()
assert result_a == result_b assert result_a == result_b
assert isinstance(result_a, list) # liste de formsemestres
assert len(result_a) > 0
sem = result_a[0]
assert verify_fields(sem, FORMSEMESTRE_FIELDS) is True

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Test API: accès aux étudiants """Test Logos
Utilisation : Utilisation :
créer les variables d'environnement: (indiquer les valeurs créer les variables d'environnement: (indiquer les valeurs
@ -19,24 +19,54 @@ Utilisation :
import requests import requests
from tests.api.setup_test_api import ( from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
API_URL, from tests.api.tools_test_api import (
CHECK_CERTIFICATE, verify_fields,
DEPT_ACRONYM, verify_occurences_ids_etus,
api_headers, BULLETIN_FIELDS,
BULLETIN_ETUDIANT_FIELDS,
BULLETIN_FORMATION_FIELDS,
BULLETIN_OPTIONS_FIELDS,
BULLETIN_RESSOURCES_FIELDS,
BULLETIN_SAES_FIELDS,
BULLETIN_UES_FIELDS,
BULLETIN_SEMESTRE_FIELDS,
BULLETIN_UES_RT11_RESSOURCES_FIELDS,
BULLETIN_UES_RT11_SAES_FIELDS,
BULLETIN_UES_RT21_RESSOURCES_FIELDS,
BULLETIN_UES_RT31_RESSOURCES_FIELDS,
BULLETIN_UES_RT21_SAES_FIELDS,
BULLETIN_UES_RT31_SAES_FIELDS,
BULLETIN_SEMESTRE_ABSENCES_FIELDS,
BULLETIN_SEMESTRE_ECTS_FIELDS,
BULLETIN_SEMESTRE_NOTES_FIELDS,
BULLETIN_SEMESTRE_RANG_FIELDS,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_FIELDS,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_FIELDS,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_POIDS_FIELDS,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS,
BULLETIN_UES_UE_FIELDS,
BULLETIN_UES_UE_MOYENNE_FIELDS,
BULLETIN_UES_UE_RESSOURCES_RESSOURCE_FIELDS,
BULLETIN_UES_UE_SAES_SAE_FIELDS,
BULLETIN_UES_UE_ECTS_FIELDS,
) )
from tests.api.tools_test_api import verify_fields
from tests.api.tools_test_api import ETUD_FIELDS, FSEM_FIELDS from tests.api.tools_test_api import ETUD_FIELDS, FSEM_FIELDS
ETUDID = 1
NIP = "1"
INE = "1"
def test_etudiants_courant(api_headers): def test_etudiants_courant(api_headers):
""" """
Route: /etudiants/courant Route: /etudiants/courant
""" """
fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"} fields = {"id", "nip", "nom", "prenom", "civilite"}
r = requests.get( r = requests.get(
API_URL + "/etudiants/courant", API_URL + "/etudiants/courants",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -46,10 +76,18 @@ def test_etudiants_courant(api_headers):
etud = etudiants[-1] etud = etudiants[-1]
assert verify_fields(etud, fields) is True assert verify_fields(etud, fields) is True
assert isinstance(etud["id"], int)
assert isinstance(etud["nip"], str)
assert isinstance(etud["nom"], str)
assert isinstance(etud["prenom"], str)
assert isinstance(etud["civilite"], str)
all_unique = verify_occurences_ids_etus(r.text)
assert all_unique is True
########## Version long ################ ########## Version long ################
r = requests.get( r = requests.get(
API_URL + "/etudiants/courant/long", API_URL + "/etudiants/courants/long",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -63,77 +101,119 @@ def test_etudiants_courant(api_headers):
def test_etudiant(api_headers): def test_etudiant(api_headers):
""" """
Routes: /etudiant/etudid, /etudiant/nip, /etudiant/ine Routes : /etudiant/etudid/<int:etudid>, /etudiant/nip/<string:nip>, /etudiant/ine/<string:ine>
""" """
######### Test etudid ######### ######### Test etudid #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/etudid/1", API_URL + "/etudiant/etudid/" + str(ETUDID),
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
etud = r.json() etud = r.json()
assert verify_fields(etud, ETUD_FIELDS) is True assert verify_fields(etud, ETUD_FIELDS) is True
code_nip = r.json()["code_nip"]
code_ine = r.json()["code_ine"]
######### Test code nip ######### ######### Test code nip #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/nip/1", API_URL + "/etudiant/nip/" + code_nip,
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud = r.json()
fields_ok = verify_fields(etud, ETUD_FIELDS)
assert fields_ok is True
assert etud["dept_acronym"] == DEPT_ACRONYM
######### Test code ine #########
r = requests.get(
API_URL + "/etudiant/ine/INE1",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud = r.json()
assert len(etud) == 25
fields_ok = verify_fields(etud, ETUD_FIELDS)
assert fields_ok is True
# Vérifie le requetage des 3 1er étudiants
for etudid in (1, 2, 3):
r = requests.get(
f"{API_URL }/etudiant/etudid/{etudid}",
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, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
etud_nip = r.json() etud_nip = r.json()
# On doit avoir obtenue le même étudiant fields_ok = verify_fields(etud, ETUD_FIELDS)
assert etud_nip == etud assert fields_ok is True
######### Test code ine #########
r = requests.get( r = requests.get(
f"{API_URL }/etudiant/ine/{ine}", API_URL + "/etudiant/ine/" + code_ine,
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
etud_ine = r.json() etud_ine = r.json()
# On doit avoir obtenue le même étudiant assert len(etud) == 26
assert etud_ine == etud fields_ok = verify_fields(etud, ETUD_FIELDS)
assert fields_ok is True
assert etud == etud_nip == etud_ine
def test_etudiants(api_headers):
"""
Route : /etudiants/etudid/<int:etudid>, /etudiants/nip/<string:nip>, /etudiants/ine/<string:ine>
"""
######### Test etudid #########
r = requests.get(
API_URL + "/etudiants/etudid/" + str(ETUDID),
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud = r.json()
code_nip = etud[0]["code_nip"]
code_ine = etud[0]["code_ine"]
assert isinstance(etud, list)
assert len(etud) == 1
fields_ok = verify_fields(etud[0], ETUD_FIELDS)
assert fields_ok is True
######### Test code nip #########
r = requests.get(
API_URL + "/etudiants/nip/" + code_nip,
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud_nip = r.json()
assert isinstance(etud_nip, list)
fields_ok = verify_fields(etud_nip[0], ETUD_FIELDS)
assert fields_ok is True
all_unique = True
list_ids = [etud["id"] for etud in etud_nip]
for id in list_ids:
if list_ids.count(id) > 1:
all_unique = False
assert all_unique is True
######### Test code ine #########
r = requests.get(
API_URL + "/etudiants/ine/" + code_ine,
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud_ine = r.json()
assert isinstance(etud_ine, list)
fields_ok = verify_fields(etud_ine[0], ETUD_FIELDS)
assert fields_ok is True
all_unique = True
list_ids = [etud["id"] for etud in etud_ine]
for id in list_ids:
if list_ids.count(id) > 1:
all_unique = False
assert all_unique is True
####### Erreurs #######
r = requests.get(
API_URL + "/etudiants/etudid/",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 404
def test_etudiant_formsemestres(api_headers): def test_etudiant_formsemestres(api_headers):
@ -144,20 +224,52 @@ def test_etudiant_formsemestres(api_headers):
######### Test etudid ######### ######### Test etudid #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/etudid/1/formsemestres", API_URL + "/etudiant/etudid/" + str(ETUDID) + "/formsemestres",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
formsemestres = r.json() list_formsemestres = r.json()
assert len(formsemestres) == 1 assert len(list_formsemestres) == 1
formsemestre = list_formsemestres[0]
assert isinstance(formsemestre["id"], int)
assert isinstance(formsemestre["bul_bgcolor"], str)
assert isinstance(formsemestre["date_debut"], str)
assert isinstance(formsemestre["date_fin"], str)
assert isinstance(formsemestre["resp_can_edit"], bool)
assert isinstance(formsemestre["dept_id"], int)
assert isinstance(formsemestre["etat"], bool)
assert isinstance(formsemestre["resp_can_change_ens"], bool)
assert isinstance(formsemestre["modalite"], str)
assert isinstance(formsemestre["ens_can_edit_eval"], bool)
assert isinstance(formsemestre["formation_id"], int)
assert isinstance(formsemestre["gestion_compensation"], bool)
assert formsemestre["elt_sem_apo"] is None or isinstance(
formsemestre["elt_sem_apo"], str
)
assert isinstance(formsemestre["semestre_id"], int)
assert isinstance(formsemestre["bul_hide_xml"], bool)
assert formsemestre["elt_annee_apo"] is None or isinstance(
formsemestre["elt_annee_apo"], str
)
assert isinstance(formsemestre["titre"], str)
assert isinstance(formsemestre["block_moyennes"], bool)
assert formsemestre["scodoc7_id"] is None or isinstance(
formsemestre["scodoc7_id"], int
)
assert isinstance(formsemestre["gestion_semestrielle"], bool)
assert isinstance(formsemestre["formsemestre_id"], int)
assert isinstance(formsemestre["titre_num"], str)
assert isinstance(formsemestre["date_debut_iso"], str)
assert isinstance(formsemestre["date_fin_iso"], str)
assert isinstance(formsemestre["responsables"], list)
assert isinstance(formsemestre["titre_formation"], str)
formsemestre = formsemestres[0]
assert verify_fields(formsemestre, FSEM_FIELDS) is True assert verify_fields(formsemestre, FSEM_FIELDS) is True
######### Test code nip ######### ######### Test code nip #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/nip/1/formsemestres", API_URL + "/etudiant/nip/" + str(NIP) + "/formsemestres",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -170,7 +282,7 @@ def test_etudiant_formsemestres(api_headers):
######### Test code ine ######### ######### Test code ine #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/ine/INE1/formsemestres", API_URL + "/etudiant/ine/" + str(INE) + "/formsemestres",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -186,10 +298,391 @@ def test_etudiant_bulletin_semestre(api_headers):
""" """
Route: /etudiant/etudid/<etudid>/formsemestre/<formsemestre_id>/bulletin Route: /etudiant/etudid/<etudid>/formsemestre/<formsemestre_id>/bulletin
""" """
##################### LONG ########################
######### Test etudid ######### ######### Test etudid #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/etudid/1/formsemestre/1/bulletin", API_URL + "/etudiant/etudid/" + str(ETUDID) + "/formsemestre/1/bulletin",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
bulletin = r.json()
assert len(bulletin) == 13 # HARDCODED
assert verify_fields(bulletin, BULLETIN_FIELDS) is True
assert isinstance(bulletin["version"], str)
assert isinstance(bulletin["type"], str)
assert isinstance(bulletin["date"], str)
assert isinstance(bulletin["publie"], bool)
assert isinstance(bulletin["etudiant"], dict)
assert isinstance(bulletin["formation"], dict)
assert isinstance(bulletin["formsemestre_id"], int)
assert isinstance(bulletin["etat_inscription"], str)
assert isinstance(bulletin["options"], dict)
assert isinstance(bulletin["ressources"], dict)
assert isinstance(bulletin["saes"], dict)
assert isinstance(bulletin["ues"], dict)
assert isinstance(bulletin["semestre"], dict)
bulletin_etud = bulletin["etudiant"]
assert verify_fields(bulletin_etud, BULLETIN_ETUDIANT_FIELDS) is True
assert isinstance(bulletin_etud["civilite"], str)
assert isinstance(bulletin_etud["code_ine"], str)
assert isinstance(bulletin_etud["code_nip"], str)
assert isinstance(bulletin_etud["date_naissance"], str)
assert isinstance(bulletin_etud["dept_id"], int)
assert isinstance(bulletin_etud["dept_acronym"], str)
assert isinstance(bulletin_etud["email"], str)
assert isinstance(bulletin_etud["emailperso"], str)
assert isinstance(bulletin_etud["etudid"], int)
assert isinstance(bulletin_etud["nom"], str)
assert isinstance(bulletin_etud["prenom"], str)
assert isinstance(bulletin_etud["nomprenom"], str)
assert isinstance(bulletin_etud["lieu_naissance"], str)
assert isinstance(bulletin_etud["dept_naissance"], str)
assert isinstance(bulletin_etud["nationalite"], str)
assert isinstance(bulletin_etud["fiche_url"], str)
assert isinstance(bulletin_etud["photo_url"], str)
assert isinstance(bulletin_etud["id"], int)
assert isinstance(bulletin_etud["domicile"], str)
assert isinstance(bulletin_etud["villedomicile"], str)
assert isinstance(bulletin_etud["telephone"], str)
assert isinstance(bulletin_etud["fax"], str)
assert isinstance(bulletin_etud["description"], str)
assert isinstance(bulletin_etud["codepostaldomicile"], str)
assert isinstance(bulletin_etud["paysdomicile"], str)
assert isinstance(bulletin_etud["telephonemobile"], str)
assert isinstance(bulletin_etud["typeadresse"], str)
bulletin_formation = bulletin["formation"]
assert verify_fields(bulletin_formation, BULLETIN_FORMATION_FIELDS) is True
assert isinstance(bulletin_formation["id"], int)
assert isinstance(bulletin_formation["acronyme"], str)
assert isinstance(bulletin_formation["titre_officiel"], str)
assert isinstance(bulletin_formation["titre"], str)
bulletin_options = bulletin["options"]
assert verify_fields(bulletin_options, BULLETIN_OPTIONS_FIELDS) is True
assert isinstance(bulletin_options["show_abs"], bool)
assert isinstance(bulletin_options["show_abs_modules"], bool)
assert isinstance(bulletin_options["show_ects"], bool)
assert isinstance(bulletin_options["show_codemodules"], bool)
assert isinstance(bulletin_options["show_matieres"], bool)
assert isinstance(bulletin_options["show_rangs"], bool)
assert isinstance(bulletin_options["show_ue_rangs"], bool)
assert isinstance(bulletin_options["show_mod_rangs"], bool)
assert isinstance(bulletin_options["show_moypromo"], bool)
assert isinstance(bulletin_options["show_minmax"], bool)
assert isinstance(bulletin_options["show_minmax_mod"], bool)
assert isinstance(bulletin_options["show_minmax_eval"], bool)
assert isinstance(bulletin_options["show_coef"], bool)
assert isinstance(bulletin_options["show_ue_cap_details"], bool)
assert isinstance(bulletin_options["show_ue_cap_current"], bool)
assert isinstance(bulletin_options["show_temporary"], bool)
assert isinstance(bulletin_options["temporary_txt"], str)
assert isinstance(bulletin_options["show_uevalid"], bool)
assert isinstance(bulletin_options["show_date_inscr"], bool)
bulletin_ressources = bulletin["ressources"]
assert verify_fields(bulletin_ressources, BULLETIN_RESSOURCES_FIELDS) is True
assert isinstance(bulletin_ressources, dict)
for ressource in bulletin_ressources.values():
assert (
verify_fields(
ressource, BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_FIELDS
)
is True
)
assert isinstance(ressource, dict)
assert isinstance(ressource["evaluations"], list)
for evaluation in ressource["evaluations"]:
assert (
verify_fields(
evaluation,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_FIELDS,
)
is True
)
assert isinstance(evaluation["id"], int)
assert evaluation["description"] is None or isinstance(
evaluation["description"], str
)
assert evaluation["date"] is None or isinstance(evaluation["date"], str)
assert isinstance(evaluation["heure_debut"], str)
assert isinstance(evaluation["heure_fin"], str)
assert isinstance(evaluation["coef"], str)
assert isinstance(evaluation["poids"], dict)
assert isinstance(evaluation["note"], dict)
assert isinstance(evaluation["url"], str)
assert (
verify_fields(
evaluation["poids"],
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_POIDS_FIELDS,
)
is True
)
assert isinstance(evaluation["poids"]["RT1.1"], float)
assert isinstance(evaluation["poids"]["RT2.1"], float)
assert isinstance(evaluation["poids"]["RT3.1"], float)
assert (
verify_fields(
evaluation["note"],
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS,
)
is True
)
assert isinstance(evaluation["note"]["value"], str)
assert isinstance(evaluation["note"]["min"], str)
assert isinstance(evaluation["note"]["max"], str)
assert isinstance(evaluation["note"]["moy"], str)
bulletin_saes = bulletin["saes"]
assert verify_fields(bulletin_saes, BULLETIN_SAES_FIELDS) is True
assert isinstance(bulletin_saes, dict)
for sae in bulletin_saes.values():
assert (
verify_fields(sae, BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_FIELDS)
is True
)
assert isinstance(sae, dict)
assert isinstance(sae["evaluations"], list)
for evaluation in sae["evaluations"]:
assert (
verify_fields(
evaluation,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_FIELDS,
)
is True
)
assert isinstance(evaluation["id"], int)
assert evaluation["description"] is None or isinstance(
evaluation["description"], str
)
assert evaluation["date"] is None or isinstance(evaluation["date"], str)
assert isinstance(evaluation["heure_debut"], str)
assert isinstance(evaluation["heure_fin"], str)
assert isinstance(evaluation["coef"], str)
assert isinstance(evaluation["poids"], dict)
assert isinstance(evaluation["note"], dict)
assert isinstance(evaluation["url"], str)
assert (
verify_fields(
evaluation["poids"],
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_POIDS_FIELDS,
)
is True
)
assert isinstance(evaluation["poids"]["RT1.1"], float)
assert isinstance(evaluation["poids"]["RT2.1"], float)
assert isinstance(evaluation["poids"]["RT3.1"], float)
assert (
verify_fields(
evaluation["note"],
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS,
)
is True
)
assert isinstance(evaluation["note"]["value"], str)
assert isinstance(evaluation["note"]["min"], str)
assert isinstance(evaluation["note"]["max"], str)
assert isinstance(evaluation["note"]["moy"], str)
bulletin_ues = bulletin["ues"]
assert verify_fields(bulletin_ues, BULLETIN_UES_FIELDS) is True
assert isinstance(bulletin_ues, dict)
for (key_ue, value_ue) in bulletin_ues.items():
assert verify_fields(value_ue, BULLETIN_UES_UE_FIELDS) is True
assert isinstance(value_ue["id"], int)
assert isinstance(value_ue["titre"], str)
assert isinstance(value_ue["numero"], int)
assert isinstance(value_ue["type"], int)
assert isinstance(value_ue["color"], str)
assert value_ue["competence"] is None or isinstance(value_ue["competence"], str)
assert isinstance(value_ue["moyenne"], dict)
assert isinstance(value_ue["bonus"], str)
assert isinstance(value_ue["malus"], str)
assert value_ue["capitalise"] is None or isinstance(value_ue["capitalise"], str)
assert isinstance(value_ue["ressources"], dict)
assert isinstance(value_ue["saes"], dict)
assert isinstance(value_ue["ECTS"], dict)
assert (
verify_fields(value_ue["moyenne"], BULLETIN_UES_UE_MOYENNE_FIELDS) is True
)
assert isinstance(value_ue["moyenne"]["value"], str)
assert isinstance(value_ue["moyenne"]["min"], str)
assert isinstance(value_ue["moyenne"]["max"], str)
assert isinstance(value_ue["moyenne"]["moy"], str)
assert isinstance(value_ue["moyenne"]["rang"], str)
assert isinstance(value_ue["moyenne"]["total"], int)
if key_ue == "RT1.1":
assert (
verify_fields(
bulletin_ues[key_ue]["ressources"],
BULLETIN_UES_RT11_RESSOURCES_FIELDS,
)
is True
)
assert (
verify_fields(
bulletin_ues[key_ue]["saes"], BULLETIN_UES_RT11_SAES_FIELDS
)
is True
)
elif key_ue == "RT2.1":
assert (
verify_fields(
bulletin_ues[key_ue]["ressources"],
BULLETIN_UES_RT21_RESSOURCES_FIELDS,
)
is True
)
assert (
verify_fields(
bulletin_ues[key_ue]["saes"], BULLETIN_UES_RT21_SAES_FIELDS
)
is True
)
elif key_ue == "RT3.1":
assert (
verify_fields(
bulletin_ues[key_ue]["ressources"],
BULLETIN_UES_RT31_RESSOURCES_FIELDS,
)
is True
)
assert (
verify_fields(
bulletin_ues[key_ue]["saes"], BULLETIN_UES_RT31_SAES_FIELDS
)
is True
)
for ressource in value_ue["ressources"].values():
assert (
verify_fields(ressource, BULLETIN_UES_UE_RESSOURCES_RESSOURCE_FIELDS)
is True
)
assert isinstance(ressource["id"], int)
assert isinstance(ressource["coef"], float)
assert isinstance(ressource["moyenne"], str)
for sae in value_ue["saes"].values():
assert verify_fields(sae, BULLETIN_UES_UE_SAES_SAE_FIELDS) is True
assert isinstance(sae["id"], int)
assert isinstance(sae["coef"], float)
assert isinstance(sae["moyenne"], str)
assert verify_fields(value_ue["ECTS"], BULLETIN_UES_UE_ECTS_FIELDS) is True
assert isinstance(value_ue["ECTS"]["acquis"], float)
assert isinstance(value_ue["ECTS"]["total"], float)
bulletin_semestre = bulletin["semestre"]
assert verify_fields(bulletin_semestre, BULLETIN_SEMESTRE_FIELDS) is True
assert isinstance(bulletin_semestre["etapes"], list)
assert isinstance(bulletin_semestre["date_debut"], str)
assert isinstance(bulletin_semestre["date_fin"], str)
assert isinstance(bulletin_semestre["annee_universitaire"], str)
assert isinstance(bulletin_semestre["numero"], int)
assert isinstance(bulletin_semestre["inscription"], str)
assert isinstance(bulletin_semestre["groupes"], list)
assert isinstance(bulletin_semestre["absences"], dict)
assert isinstance(bulletin_semestre["ECTS"], dict)
assert isinstance(bulletin_semestre["notes"], dict)
assert isinstance(bulletin_semestre["rang"], dict)
assert (
verify_fields(bulletin_semestre["absences"], BULLETIN_SEMESTRE_ABSENCES_FIELDS)
is True
)
assert isinstance(bulletin_semestre["absences"]["injustifie"], int)
assert isinstance(bulletin_semestre["absences"]["total"], int)
assert (
verify_fields(bulletin_semestre["ECTS"], BULLETIN_SEMESTRE_ECTS_FIELDS) is True
)
assert isinstance(bulletin_semestre["ECTS"]["acquis"], int)
assert isinstance(bulletin_semestre["ECTS"]["total"], float)
assert (
verify_fields(bulletin_semestre["notes"], BULLETIN_SEMESTRE_NOTES_FIELDS)
is True
)
assert isinstance(bulletin_semestre["notes"]["value"], str)
assert isinstance(bulletin_semestre["notes"]["min"], str)
assert isinstance(bulletin_semestre["notes"]["max"], str)
assert isinstance(bulletin_semestre["notes"]["moy"], str)
assert (
verify_fields(bulletin_semestre["rang"], BULLETIN_SEMESTRE_RANG_FIELDS) is True
)
assert isinstance(bulletin_semestre["rang"]["value"], str)
assert isinstance(bulletin_semestre["rang"]["total"], int)
######### Test code nip #########
r = requests.get(
API_URL + "/etudiant/nip/" + str(NIP) + "/formsemestre/1/bulletin",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
bul = r.json()
assert len(bul) == 13 # HARDCODED
######### Test code ine #########
r = requests.get(
API_URL + "/etudiant/ine/" + str(INE) + "/formsemestre/1/bulletin",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
bul = r.json()
assert len(bul) == 13 # HARDCODED
################### LONG + PDF #####################
# ######### Test etudid #########
#
# r = requests.get(
# API_URL + "/etudiant/etudid/" + str(ETUDID) + "/formsemestre/1/bulletin/pdf",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
#
# ######### Test code nip #########
#
# r = requests.get(
# API_URL + "/etudiant/nip/" + str(NIP) + "/formsemestre/1/bulletin/pdf",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
#
# ######### Test code ine #########
# r = requests.get(
# API_URL + "/etudiant/ine/" + str(INE) + "/formsemestre/1/bulletin/pdf",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
################### SHORT #####################
######### Test etudid #########
r = requests.get(
API_URL + "/etudiant/etudid/" + str(ETUDID) + "/formsemestre/1/bulletin/short",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -200,7 +693,7 @@ def test_etudiant_bulletin_semestre(api_headers):
######### Test code nip ######### ######### Test code nip #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/nip/1/formsemestre/1/bulletin", API_URL + "/etudiant/nip/" + str(NIP) + "/formsemestre/1/bulletin/short",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -210,7 +703,7 @@ def test_etudiant_bulletin_semestre(api_headers):
######### Test code ine ######### ######### Test code ine #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/ine/INE1/formsemestre/1/bulletin", API_URL + "/etudiant/ine/" + str(INE) + "/formsemestre/1/bulletin/short",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -218,6 +711,33 @@ def test_etudiant_bulletin_semestre(api_headers):
bul = r.json() bul = r.json()
assert len(bul) == 13 # HARDCODED assert len(bul) == 13 # HARDCODED
################### SHORT + PDF #####################
# ######### Test etudid #########
# r = requests.get(
# API_URL + "/etudiant/etudid/" + str(ETUDID) + "/formsemestre/1/bulletin/short/pdf",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
#
# ######### Test code nip #########
#
# r = requests.get(
# API_URL + "/etudiant/nip/" + str(NIP) + "/formsemestre/1/bulletin/short/pdf",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
#
# ######### Test code ine #########
# r = requests.get(
# API_URL + "/etudiant/ine/" + str(INE) + "/formsemestre/1/bulletin/short/pdf",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
### --- Test étudiant inexistant ### --- Test étudiant inexistant
r = requests.get( r = requests.get(
API_URL + "/etudiant/ine/189919919119191/formsemestre/1/bulletin", API_URL + "/etudiant/ine/189919919119191/formsemestre/1/bulletin",
@ -260,7 +780,7 @@ def test_etudiant_groups(api_headers):
######### Test code nip ######### ######### Test code nip #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/nip/1/formsemestre/1/groups", API_URL + "/etudiant/nip/" + str(NIP) + "/formsemestre/1/groups",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
@ -273,7 +793,7 @@ def test_etudiant_groups(api_headers):
######### Test code ine ######### ######### Test code ine #########
r = requests.get( r = requests.get(
API_URL + "/etudiant/ine/INE1/formsemestre/1/groups", API_URL + "/etudiant/ine/" + str(INE) + "/formsemestre/1/groups",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )

View File

@ -20,30 +20,79 @@ Utilisation :
import requests import requests
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
from tests.api.tools_test_api import (
verify_fields,
EVALUATIONS_FIELDS,
EVALUATION_FIELDS,
)
def test_evaluations(api_headers): def test_evaluations(api_headers):
""" """
Route: /evaluation/<int:moduleimpl_id> Test 'evaluations'
Route :
- /evaluations/<int:moduleimpl_id>
""" """
moduleimpl_id = 1
r = requests.get( r = requests.get(
API_URL + "/evaluations/1", f"{API_URL}/evaluations/{moduleimpl_id}",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
# TODO list_eval = r.json()
assert isinstance(list_eval, list)
for eval in list_eval:
assert verify_fields(eval, EVALUATIONS_FIELDS) is True
assert isinstance(eval["id"], int)
assert isinstance(eval["jour"], str)
assert isinstance(eval["heure_fin"], str)
assert isinstance(eval["note_max"], float)
assert isinstance(eval["visibulletin"], bool)
assert isinstance(eval["evaluation_type"], int)
assert isinstance(eval["moduleimpl_id"], int)
assert isinstance(eval["heure_debut"], str)
assert eval["description"] is None or isinstance(eval["description"], str)
assert isinstance(eval["coefficient"], float)
assert isinstance(eval["publish_incomplete"], bool)
assert isinstance(eval["numero"], int)
assert isinstance(eval["evaluation_id"], int)
assert eval["date_debut"] is None or isinstance(eval["date_debut"], str)
assert eval["date_fin"] is None or isinstance(eval["date_fin"], str)
assert isinstance(eval["poids"], dict)
assert eval["jouriso"] is None or isinstance(eval["jouriso"], str)
assert isinstance(eval["duree"], str)
assert isinstance(eval["descrheure"], str)
assert isinstance(eval["matin"], int)
assert isinstance(eval["apresmidi"], int)
assert eval["moduleimpl_id"] == moduleimpl_id
# TODO car pas d'évaluations créées à ce stade def test_evaluation_notes(api_headers): # XXX TODO changer la boucle pour parcourir le dict sans les indices
# def test_evaluation_notes(api_headers): """
# """ Test 'evaluation_notes'
# Route: /evaluation/eval_notes/<int:evaluation_id>
# """ Route :
# r = requests.get( - /evaluation/eval_notes/<int:evaluation_id>
# API_URL + "/evaluation/eval_notes/1", """
# headers=api_headers, eval_id = 1
# verify=CHECK_CERTIFICATE, r = requests.get(
# ) f"{API_URL}/evaluation/eval_notes/{eval_id}",
# assert r.status_code == 200 headers=api_headers,
# # TODO verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
eval_notes = r.json()
for i in range(1, len(eval_notes)):
assert verify_fields(eval_notes[f"{i}"], EVALUATION_FIELDS)
assert isinstance(eval_notes[f"{i}"]["id"], int)
assert isinstance(eval_notes[f"{i}"]["etudid"], int)
assert isinstance(eval_notes[f"{i}"]["evaluation_id"], int)
assert isinstance(eval_notes[f"{i}"]["value"], float)
assert isinstance(eval_notes[f"{i}"]["comment"], str)
assert isinstance(eval_notes[f"{i}"]["date"], str)
assert isinstance(eval_notes[f"{i}"]["uid"], int)
assert eval_id == eval_notes[f"{i}"]["evaluation_id"]

View File

@ -20,7 +20,16 @@ Utilisation :
import requests import requests
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_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,
FORMATION_EXPORT_FIELDS,
FORMATION_EXPORT_UE_FIELDS,
FORMATION_EXPORT_UE_MATIERE_FIELDS,
FORMATION_EXPORT_UE_MATIERE_MODULE_FIELDS,
FORMATION_EXPORT_UE_MATIERE_MODULE_COEF_FIELDS,
MODULE_FIELDS,
REF_COMP_FIELDS,
)
from tests.api.tools_test_api import FORMATION_FIELDS, MODIMPL_FIELDS from tests.api.tools_test_api import FORMATION_FIELDS, MODIMPL_FIELDS
@ -45,15 +54,52 @@ def test_formations_by_id(api_headers):
""" """
Route: /formation/<int:formation_id> Route: /formation/<int:formation_id>
""" """
id_formation = 1
r = requests.get( r = requests.get(
API_URL + "/formation/1", f"{API_URL}/formation/{id_formation}",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
formation = r.json() formation = r.json()
assert verify_fields(formation, FORMATION_FIELDS) is True assert verify_fields(formation, FORMATION_FIELDS) is True
# TODO tester le contenu de certains champs assert isinstance(formation["dept_id"], int)
assert isinstance(formation["acronyme"], str)
assert isinstance(formation["titre_officiel"], str)
assert isinstance(formation["formation_code"], str)
assert formation["code_specialite"] is None or isinstance(
formation["code_specialite"], str
)
assert isinstance(formation["id"], int)
assert isinstance(formation["titre"], str)
assert isinstance(formation["version"], int)
assert isinstance(formation["type_parcours"], int)
assert formation["referentiel_competence_id"] is None or isinstance(
formation["referentiel_competence_id"], int
)
assert isinstance(formation["formation_id"], int)
assert id_formation == formation["formation_id"]
assert id_formation == formation["id"]
r1 = requests.get(
f"{API_URL}/formation/{formation['formation_id']}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r1.status_code == 200
formation1 = r1.json()
assert formation == formation1
# ERROR
id_formation_inexistant = 1516476846861656351
r_error = requests.get(
f"{API_URL}/formation/{id_formation_inexistant}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_error.status_code == 404
def test_formation_export(api_headers): def test_formation_export(api_headers):
@ -67,33 +113,150 @@ def test_formation_export(api_headers):
) )
assert r.status_code == 200 assert r.status_code == 200
export_formation = r.json() export_formation = r.json()
assert verify_fields(export_formation, FORMATION_FIELDS) is True assert verify_fields(export_formation, FORMATION_EXPORT_FIELDS) is True
# TODO tester le contenu de certains champs assert isinstance(export_formation["dept_id"], int)
assert isinstance(export_formation["acronyme"], str)
assert isinstance(export_formation["titre_officiel"], str)
assert isinstance(export_formation["formation_code"], str)
assert export_formation["code_specialite"] is None or isinstance(
export_formation["code_specialite"], str
)
assert isinstance(export_formation["id"], int)
assert isinstance(export_formation["titre"], str)
assert isinstance(export_formation["version"], int)
assert isinstance(export_formation["type_parcours"], int)
assert export_formation["referentiel_competence_id"] is None or isinstance(
export_formation["referentiel_competence_id"], int
)
assert isinstance(export_formation["formation_id"], int)
assert isinstance(export_formation["ue"], list)
ues = export_formation["ue"]
# TODO for ue in ues:
# def test_formsemestre_apo(api_headers): assert verify_fields(ue, FORMATION_EXPORT_UE_FIELDS) is True
# r = requests.get( assert isinstance(ue["acronyme"], str)
# API_URL + "/formation/apo/<string:etape_apo>", assert isinstance(ue["numero"], int)
# headers=api_headers, assert isinstance(ue["titre"], str)
# verify=CHECK_CERTIFICATE, assert isinstance(ue["type"], int)
# ) assert isinstance(ue["ue_code"], str)
# assert r.status_code == 200 assert isinstance(ue["ects"], float)
assert isinstance(ue["is_external"], bool)
assert isinstance(ue["code_apogee"], str)
assert isinstance(ue["coefficient"], float)
assert isinstance(ue["semestre_idx"], int)
assert isinstance(ue["color"], str)
assert isinstance(ue["reference"], int)
assert isinstance(ue["matiere"], list)
matieres = ue["matiere"]
for matiere in matieres:
assert verify_fields(matiere, FORMATION_EXPORT_UE_MATIERE_FIELDS)
assert isinstance(matiere["titre"], str)
assert isinstance(matiere["numero"], int)
assert isinstance(matiere["module"], list)
modules = matiere["module"]
for module in modules:
assert verify_fields(module, FORMATION_EXPORT_UE_MATIERE_MODULE_FIELDS)
assert isinstance(module["titre"], str)
assert isinstance(module["abbrev"], str)
assert isinstance(module["code"], str)
assert isinstance(module["heures_cours"], float)
assert isinstance(module["heures_td"], float)
assert isinstance(module["heures_tp"], float)
assert isinstance(module["coefficient"], float)
assert isinstance(module["ects"], str)
assert isinstance(module["semestre_id"], int)
assert isinstance(module["numero"], int)
assert isinstance(module["code_apogee"], str)
assert isinstance(module["module_type"], int)
assert isinstance(module["coefficients"], list)
coefficients = module["coefficients"]
for coef in coefficients:
assert verify_fields(
coef, FORMATION_EXPORT_UE_MATIERE_MODULE_COEF_FIELDS
)
assert isinstance(coef["ue_reference"], str)
assert isinstance(coef["coef"], str)
# ERROR
id_formation_inexistant = 1516476846861656351
r_error = requests.get(
f"{API_URL}/formation/formation_export/{id_formation_inexistant}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_error.status_code == 404
def test_moduleimpl(api_headers): def test_moduleimpl(api_headers):
""" """
Route: /formation/moduleimpl/<int:moduleimpl_id> Route: /formation/moduleimpl/<int:moduleimpl_id>
""" """
moduleimpl_id = 1
r = requests.get( r = requests.get(
API_URL + "/formation/moduleimpl/1", f"{API_URL}/formation/moduleimpl/{moduleimpl_id}",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
moduleimpl = r.json() moduleimpl = r.json()
assert verify_fields(moduleimpl, MODIMPL_FIELDS) is True assert verify_fields(moduleimpl, MODIMPL_FIELDS) is True
# TODO tester le contenu de certains champs assert isinstance(moduleimpl["id"], int)
assert isinstance(moduleimpl["responsable_id"], int)
assert isinstance(moduleimpl["module_id"], int)
assert isinstance(moduleimpl["formsemestre_id"], int)
assert moduleimpl["computation_expr"] is None or isinstance(
moduleimpl["computation_expr"], str
)
assert isinstance(moduleimpl["moduleimpl_id"], int)
assert isinstance(moduleimpl["ens"], list)
assert isinstance(moduleimpl["module"], dict)
module = moduleimpl["module"]
assert verify_fields(module, MODULE_FIELDS)
assert isinstance(module["heures_cours"], float)
assert isinstance(module["semestre_id"], int)
assert isinstance(module["heures_td"], float)
assert isinstance(module["numero"], int)
assert isinstance(module["heures_tp"], float)
assert isinstance(module["code_apogee"], str)
assert isinstance(module["titre"], str)
assert isinstance(module["coefficient"], float)
assert isinstance(module["module_type"], int)
assert isinstance(module["id"], int)
assert module["ects"] is None or isinstance(module["ects"], str)
assert isinstance(module["abbrev"], str)
assert isinstance(module["ue_id"], int)
assert isinstance(module["code"], str)
assert isinstance(module["formation_id"], int)
assert isinstance(module["matiere_id"], int)
assert isinstance(module["module_id"], int)
assert moduleimpl_id == moduleimpl["id"]
assert moduleimpl_id == moduleimpl["moduleimpl_id"]
r1 = requests.get(
f"{API_URL}/formation/moduleimpl/{moduleimpl['moduleimpl_id']}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r1.status_code == 200
moduleimpl1 = r1.json()
assert moduleimpl == moduleimpl1
# ERROR
id_formation_inexistant = 1516476846861656351
r_error = requests.get(
f"{API_URL}/formation/moduleimpl/{id_formation_inexistant}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_error.status_code == 404
def test_referentiel_competences(api_headers): def test_referentiel_competences(api_headers):
@ -101,9 +264,32 @@ def test_referentiel_competences(api_headers):
Route: "/formation/<int:formation_id>/referentiel_competences", Route: "/formation/<int:formation_id>/referentiel_competences",
""" """
r = requests.get( r = requests.get(
API_URL + "/formation/1/referentiel_competences", f"{API_URL}/formation/1/referentiel_competences",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
# XXX A compléter
ref_comp = r.json()
assert verify_fields(ref_comp, REF_COMP_FIELDS) is True
assert isinstance(ref_comp["dept_id"], int)
assert isinstance(ref_comp["annexe"], str)
assert isinstance(ref_comp["specialite"], str)
assert isinstance(ref_comp["specialite_long"], str)
assert isinstance(ref_comp["type_structure"], str)
assert isinstance(ref_comp["type_departement"], str)
assert isinstance(ref_comp["type_titre"], str)
assert isinstance(ref_comp["version_orebut"], str)
assert isinstance(ref_comp["scodoc_date_loaded"], str)
assert isinstance(ref_comp["scodoc_orig_filename"], str)
assert isinstance(ref_comp["competences"], dict)
assert isinstance(ref_comp["parcours"], dict)
# ERROR
id_formation_inexistant = 1516476846861656351
r_error = requests.get(
f"{API_URL}/formation/{id_formation_inexistant}/referentiel_competences",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_error.status_code == 404

View File

@ -21,8 +21,36 @@ import requests
from app.api.formsemestres import formsemestre from app.api.formsemestres import formsemestre
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
from tests.api.tools_test_api import MODIMPL_FIELDS, verify_fields
from tests.api.tools_test_api import FSEM_FIELDS, UE_FIELDS, MODULE_FIELDS from tests.api.tools_test_api import (
verify_fields,
MODIMPL_FIELDS,
EVAL_FIELDS,
SAISIE_NOTES_FIELDS,
FORMSEMESTRE_ETUS_FIELDS,
FSEM_FIELDS,
FSEM_FIELDS,
UE_FIELDS,
MODULE_FIELDS,
FORMSEMESTRE_BULLETINS_FIELDS,
FORMSEMESTRE_BULLETINS_ETU_FIELDS,
FORMSEMESTRE_BULLETINS_FORMATION_FIELDS,
FORMSEMESTRE_BULLETINS_OPT_FIELDS,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_FIELDS,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_FIELDS,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_POIDS_FIELDS,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS,
BULLETIN_UES_UE_FIELDS,
BULLETIN_UES_UE_MOYENNE_FIELDS,
BULLETIN_UES_UE_RESSOURCES_RESSOURCE_FIELDS,
BULLETIN_UES_UE_SAES_SAE_FIELDS,
BULLETIN_UES_UE_ECTS_FIELDS,
BULLETIN_SEMESTRE_FIELDS,
BULLETIN_SEMESTRE_ABSENCES_FIELDS,
BULLETIN_SEMESTRE_ECTS_FIELDS,
BULLETIN_SEMESTRE_NOTES_FIELDS,
BULLETIN_SEMESTRE_RANG_FIELDS,
)
# Etudiant pour les tests # Etudiant pour les tests
ETUDID = 1 ETUDID = 1
@ -34,62 +62,421 @@ def test_formsemestre(api_headers):
""" """
Route: /formsemestre/<id> Route: /formsemestre/<id>
""" """
formsemestre_id = 1
r = requests.get( r = requests.get(
API_URL + "/formsemestre/1", f"{API_URL}/formsemestre/{formsemestre_id}",
headers=api_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) assert verify_fields(formsemestre, FSEM_FIELDS)
assert isinstance(formsemestre["block_moyennes"], bool)
assert isinstance(formsemestre["bul_bgcolor"], str)
assert isinstance(formsemestre["bul_hide_xml"], bool)
assert isinstance(formsemestre["date_debut_iso"], str)
assert isinstance(formsemestre["date_debut"], str)
assert isinstance(formsemestre["date_fin_iso"], str)
assert isinstance(formsemestre["date_fin"], str)
assert isinstance(formsemestre["dept_id"], int)
assert formsemestre["elt_annee_apo"] is None or isinstance(
formsemestre["elt_annee_apo"], str
)
assert formsemestre["elt_sem_apo"] is None or isinstance(
formsemestre["elt_sem_apo"], str
)
assert isinstance(formsemestre["ens_can_edit_eval"], bool)
assert isinstance(formsemestre["etat"], bool)
assert isinstance(formsemestre["formation_id"], int)
assert isinstance(formsemestre["formsemestre_id"], int)
assert isinstance(formsemestre["gestion_compensation"], bool)
assert isinstance(formsemestre["gestion_semestrielle"], bool)
assert isinstance(formsemestre["id"], int)
assert isinstance(formsemestre["modalite"], str)
assert isinstance(formsemestre["resp_can_change_ens"], bool)
assert isinstance(formsemestre["resp_can_edit"], bool)
assert isinstance(formsemestre["responsables"], list)
assert formsemestre["scodoc7_id"] is None or isinstance(
formsemestre["scodoc7_id"], int
)
assert isinstance(formsemestre["semestre_id"], int)
assert isinstance(formsemestre["titre_formation"], str)
assert isinstance(formsemestre["titre_num"], str)
assert isinstance(formsemestre["titre"], str)
### ERROR ###
formsemestre_id_inexistant = 165456165165136513510351
r = requests.get(
f"{API_URL}/formsemestre/{formsemestre_id_inexistant}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 404
def test_etudiant_bulletin(api_headers): def test_formsemestre_apo(api_headers):
""" """
Route: Route: /formsemestre/apo/<string:etape_apo>
""" """
formsemestre_id = 1 etape_apo = "A1"
r = requests.get( r = requests.get(
f"{API_URL}/etudiant/etudid/1/formsemestre/{formsemestre_id}/bulletin", f"{API_URL}/formsemestre/apo/{etape_apo}",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
bull_a = r.json() list_formsemestre = r.json()
assert isinstance(list_formsemestre, list)
r = requests.get( for formsemestre in list_formsemestre:
f"{API_URL}/etudiant/nip/{NIP}/formsemestre/{formsemestre_id}/bulletin", assert isinstance(formsemestre, dict)
headers=api_headers, assert verify_fields(formsemestre, FSEM_FIELDS)
verify=CHECK_CERTIFICATE, assert isinstance(formsemestre["block_moyennes"], bool)
assert isinstance(formsemestre["bul_bgcolor"], str)
assert isinstance(formsemestre["bul_hide_xml"], bool)
assert isinstance(formsemestre["date_debut_iso"], str)
assert isinstance(formsemestre["date_debut"], str)
assert isinstance(formsemestre["date_fin_iso"], str)
assert isinstance(formsemestre["date_fin"], str)
assert isinstance(formsemestre["dept_id"], int)
assert formsemestre["elt_annee_apo"] is None or isinstance(
formsemestre["elt_annee_apo"], str
) )
assert r.status_code == 200 assert formsemestre["elt_sem_apo"] is None or isinstance(
bull_b = r.json() formsemestre["elt_sem_apo"], str
)
assert isinstance(formsemestre["ens_can_edit_eval"], bool)
assert isinstance(formsemestre["etat"], bool)
assert isinstance(formsemestre["formation_id"], int)
assert isinstance(formsemestre["formsemestre_id"], int)
assert isinstance(formsemestre["gestion_compensation"], bool)
assert isinstance(formsemestre["gestion_semestrielle"], bool)
assert isinstance(formsemestre["id"], int)
assert isinstance(formsemestre["modalite"], str)
assert isinstance(formsemestre["resp_can_change_ens"], bool)
assert isinstance(formsemestre["resp_can_edit"], bool)
assert isinstance(formsemestre["responsables"], list)
assert formsemestre["scodoc7_id"] is None or isinstance(
formsemestre["scodoc7_id"], int
)
assert isinstance(formsemestre["semestre_id"], int)
assert isinstance(formsemestre["titre_formation"], str)
assert isinstance(formsemestre["titre_num"], str)
assert isinstance(formsemestre["titre"], str)
r = requests.get( ### ERROR ###
f"{API_URL}/etudiant/ine/{INE}/formsemestre/{formsemestre_id}/bulletin", # etape_apo_inexistante = "aoefiaozidaoẑidjnoaiznjd"
headers=api_headers, # r_error = requests.get(
verify=CHECK_CERTIFICATE, # f"{API_URL}/formsemestre/apo/{etape_apo_inexistante}",
) # headers=api_headers,
assert r.status_code == 200 # verify=CHECK_CERTIFICATE,
bull_c = r.json() # )
# elimine les dates de publication pour comparer les autres champs # assert r_error.status_code == 404
del bull_a["date"]
del bull_b["date"]
del bull_c["date"]
assert bull_a == bull_b == bull_c
def test_bulletins(api_headers): def test_bulletins(api_headers):
""" """
Route: Route: /formsemestre/<int:formsemestre_id>/bulletins
""" """
formsemestre_id = 1
r = requests.get( r = requests.get(
API_URL + "/formsemestre/1/bulletins", f"{API_URL}/formsemestre/{formsemestre_id}/bulletins",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
bulletins = r.json()
assert isinstance(bulletins, list)
for bul in bulletins:
assert verify_fields(bul, FORMSEMESTRE_BULLETINS_FIELDS) is True
assert isinstance(bul["version"], str)
assert isinstance(bul["type"], str)
assert isinstance(bul["date"], str)
assert isinstance(bul["publie"], bool)
assert isinstance(bul["etudiant"], dict)
assert isinstance(bul["formation"], dict)
assert isinstance(bul["formsemestre_id"], int)
assert isinstance(bul["etat_inscription"], str)
assert isinstance(bul["options"], dict)
assert isinstance(bul["ressources"], dict)
assert isinstance(bul["saes"], dict)
assert isinstance(bul["ues"], dict)
assert isinstance(bul["semestre"], dict)
formsemestre_id_bul = bul["formsemestre_id"]
assert formsemestre_id == formsemestre_id_bul
etudiant = bul["etudiant"]
assert verify_fields(etudiant, FORMSEMESTRE_BULLETINS_ETU_FIELDS) is True
assert isinstance(etudiant["civilite"], str)
assert isinstance(etudiant["code_ine"], str)
assert isinstance(etudiant["code_nip"], str)
assert isinstance(etudiant["date_naissance"], str)
assert isinstance(etudiant["dept_id"], int)
assert isinstance(etudiant["dept_acronym"], str)
assert isinstance(etudiant["email"], str)
assert isinstance(etudiant["emailperso"], str)
assert isinstance(etudiant["etudid"], int)
assert isinstance(etudiant["nom"], str)
assert isinstance(etudiant["prenom"], str)
assert isinstance(etudiant["nomprenom"], str)
assert isinstance(etudiant["lieu_naissance"], str)
assert isinstance(etudiant["dept_naissance"], str)
assert isinstance(etudiant["nationalite"], str)
assert isinstance(etudiant["boursier"], str)
assert isinstance(etudiant["fiche_url"], str)
assert isinstance(etudiant["photo_url"], str)
assert isinstance(etudiant["id"], int)
assert isinstance(etudiant["codepostaldomicile"], str)
assert isinstance(etudiant["paysdomicile"], str)
assert isinstance(etudiant["telephonemobile"], str)
assert isinstance(etudiant["typeadresse"], str)
assert isinstance(etudiant["domicile"], str)
assert isinstance(etudiant["villedomicile"], str)
assert isinstance(etudiant["telephone"], str)
assert isinstance(etudiant["fax"], str)
assert isinstance(etudiant["description"], str)
formation = bul["formation"]
assert verify_fields(formation, FORMSEMESTRE_BULLETINS_FORMATION_FIELDS) is True
assert isinstance(formation["id"], int)
assert isinstance(formation["acronyme"], str)
assert isinstance(formation["titre_officiel"], str)
assert isinstance(formation["titre"], str)
options = bul["options"]
assert verify_fields(options, FORMSEMESTRE_BULLETINS_OPT_FIELDS) is True
assert isinstance(options["show_abs"], bool)
assert isinstance(options["show_abs_modules"], bool)
assert isinstance(options["show_ects"], bool)
assert isinstance(options["show_codemodules"], bool)
assert isinstance(options["show_matieres"], bool)
assert isinstance(options["show_rangs"], bool)
assert isinstance(options["show_ue_rangs"], bool)
assert isinstance(options["show_mod_rangs"], bool)
assert isinstance(options["show_moypromo"], bool)
assert isinstance(options["show_minmax"], bool)
assert isinstance(options["show_minmax_mod"], bool)
assert isinstance(options["show_minmax_eval"], bool)
assert isinstance(options["show_coef"], bool)
assert isinstance(options["show_ue_cap_details"], bool)
assert isinstance(options["show_ue_cap_current"], bool)
assert isinstance(options["show_temporary"], bool)
assert isinstance(options["temporary_txt"], str)
assert isinstance(options["show_uevalid"], bool)
assert isinstance(options["show_date_inscr"], bool)
bulletin_ressources = bul["ressources"]
assert isinstance(bulletin_ressources, dict)
for ressource in bulletin_ressources.values():
assert (
verify_fields(
ressource, BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_FIELDS
)
is True
)
assert isinstance(ressource, dict)
assert isinstance(ressource["evaluations"], list)
for evaluation in ressource["evaluations"]:
assert (
verify_fields(
evaluation,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_FIELDS,
)
is True
)
assert isinstance(evaluation["id"], int)
assert evaluation["description"] is None or isinstance(
evaluation["description"], str
)
assert evaluation["date"] is None or isinstance(evaluation["date"], str)
assert isinstance(evaluation["heure_debut"], str)
assert isinstance(evaluation["heure_fin"], str)
assert isinstance(evaluation["coef"], str)
assert isinstance(evaluation["poids"], dict)
assert isinstance(evaluation["note"], dict)
assert isinstance(evaluation["url"], str)
assert (
verify_fields(
evaluation["poids"],
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_POIDS_FIELDS,
)
is True
)
assert isinstance(evaluation["poids"]["RT1.1"], float)
assert isinstance(evaluation["poids"]["RT2.1"], float)
assert isinstance(evaluation["poids"]["RT3.1"], float)
assert (
verify_fields(
evaluation["note"],
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS,
)
is True
)
assert isinstance(evaluation["note"]["value"], str)
assert isinstance(evaluation["note"]["min"], str)
assert isinstance(evaluation["note"]["max"], str)
assert isinstance(evaluation["note"]["moy"], str)
bulletin_saes = bul["saes"]
assert isinstance(bulletin_saes, dict)
for sae in bulletin_saes.values():
assert (
verify_fields(sae, BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_FIELDS)
is True
)
assert isinstance(sae, dict)
assert isinstance(sae["evaluations"], list)
for evaluation in sae["evaluations"]:
assert (
verify_fields(
evaluation,
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_FIELDS,
)
is True
)
assert isinstance(evaluation["id"], int)
assert evaluation["description"] is None or isinstance(
evaluation["description"], str
)
assert evaluation["date"] is None or isinstance(evaluation["date"], str)
assert isinstance(evaluation["heure_debut"], str)
assert isinstance(evaluation["heure_fin"], str)
assert isinstance(evaluation["coef"], str)
assert isinstance(evaluation["poids"], dict)
assert isinstance(evaluation["note"], dict)
assert isinstance(evaluation["url"], str)
assert (
verify_fields(
evaluation["poids"],
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_POIDS_FIELDS,
)
is True
)
assert isinstance(evaluation["poids"]["RT1.1"], float)
assert isinstance(evaluation["poids"]["RT2.1"], float)
assert isinstance(evaluation["poids"]["RT3.1"], float)
assert (
verify_fields(
evaluation["note"],
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS,
)
is True
)
assert isinstance(evaluation["note"]["value"], str)
assert isinstance(evaluation["note"]["min"], str)
assert isinstance(evaluation["note"]["max"], str)
assert isinstance(evaluation["note"]["moy"], str)
bulletin_ues = bul["ues"]
assert isinstance(bulletin_ues, dict)
for (key_ue, value_ue) in bulletin_ues.items():
assert verify_fields(value_ue, BULLETIN_UES_UE_FIELDS) is True
assert isinstance(value_ue["id"], int)
assert isinstance(value_ue["titre"], str)
assert isinstance(value_ue["numero"], int)
assert isinstance(value_ue["type"], int)
assert isinstance(value_ue["color"], str)
assert value_ue["competence"] is None or isinstance(
value_ue["competence"], str
)
assert isinstance(value_ue["moyenne"], dict)
assert isinstance(value_ue["bonus"], str)
assert isinstance(value_ue["malus"], str)
assert value_ue["capitalise"] is None or isinstance(
value_ue["capitalise"], str
)
assert isinstance(value_ue["ressources"], dict)
assert isinstance(value_ue["saes"], dict)
assert isinstance(value_ue["ECTS"], dict)
assert (
verify_fields(value_ue["moyenne"], BULLETIN_UES_UE_MOYENNE_FIELDS)
is True
)
assert isinstance(value_ue["moyenne"]["value"], str)
assert isinstance(value_ue["moyenne"]["min"], str)
assert isinstance(value_ue["moyenne"]["max"], str)
assert isinstance(value_ue["moyenne"]["moy"], str)
assert isinstance(value_ue["moyenne"]["rang"], str)
assert isinstance(value_ue["moyenne"]["total"], int)
for ressource in value_ue["ressources"].values():
assert (
verify_fields(
ressource, BULLETIN_UES_UE_RESSOURCES_RESSOURCE_FIELDS
)
is True
)
assert isinstance(ressource["id"], int)
assert isinstance(ressource["coef"], float)
assert isinstance(ressource["moyenne"], str)
for sae in value_ue["saes"].values():
assert verify_fields(sae, BULLETIN_UES_UE_SAES_SAE_FIELDS) is True
assert isinstance(sae["id"], int)
assert isinstance(sae["coef"], float)
assert isinstance(sae["moyenne"], str)
assert verify_fields(value_ue["ECTS"], BULLETIN_UES_UE_ECTS_FIELDS) is True
assert isinstance(value_ue["ECTS"]["acquis"], float)
assert isinstance(value_ue["ECTS"]["total"], float)
bulletin_semestre = bul["semestre"]
assert verify_fields(bulletin_semestre, BULLETIN_SEMESTRE_FIELDS) is True
assert isinstance(bulletin_semestre["etapes"], list)
assert isinstance(bulletin_semestre["date_debut"], str)
assert isinstance(bulletin_semestre["date_fin"], str)
assert isinstance(bulletin_semestre["annee_universitaire"], str)
assert isinstance(bulletin_semestre["numero"], int)
assert isinstance(bulletin_semestre["inscription"], str)
assert isinstance(bulletin_semestre["groupes"], list)
assert isinstance(bulletin_semestre["absences"], dict)
assert isinstance(bulletin_semestre["ECTS"], dict)
assert isinstance(bulletin_semestre["notes"], dict)
assert isinstance(bulletin_semestre["rang"], dict)
assert (
verify_fields(
bulletin_semestre["absences"], BULLETIN_SEMESTRE_ABSENCES_FIELDS
)
is True
)
assert isinstance(bulletin_semestre["absences"]["injustifie"], int)
assert isinstance(bulletin_semestre["absences"]["total"], int)
assert (
verify_fields(bulletin_semestre["ECTS"], BULLETIN_SEMESTRE_ECTS_FIELDS)
is True
)
assert isinstance(bulletin_semestre["ECTS"]["acquis"], int)
assert isinstance(bulletin_semestre["ECTS"]["total"], float)
assert (
verify_fields(bulletin_semestre["notes"], BULLETIN_SEMESTRE_NOTES_FIELDS)
is True
)
assert isinstance(bulletin_semestre["notes"]["value"], str)
assert isinstance(bulletin_semestre["notes"]["min"], str)
assert isinstance(bulletin_semestre["notes"]["max"], str)
assert isinstance(bulletin_semestre["notes"]["moy"], str)
assert (
verify_fields(bulletin_semestre["rang"], BULLETIN_SEMESTRE_RANG_FIELDS)
is True
)
assert isinstance(bulletin_semestre["rang"]["value"], str)
assert isinstance(bulletin_semestre["rang"]["total"], int)
# # jury # # jury
# def test_jury(): # def test_jury():
@ -101,6 +488,141 @@ def test_bulletins(api_headers):
# assert r.status_code == 200 # assert r.status_code == 200
def test_formsemestre_etudiants(api_headers):
"""
Route: /formsemestre/<int:formsemestre_id>/etudiants,
/formsemestre/<int:formsemestre_id>/etudiants/demissionnaires,
/formsemestre/<int:formsemestre_id>/etudiants/defaillants
"""
formsemestre_id = 1
r = requests.get(
f"{API_URL}/formsemestre/{formsemestre_id}/etudiants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
formsemestre_etus = r.json()
assert isinstance(formsemestre_etus, list)
for etu in formsemestre_etus:
assert verify_fields(etu, FORMSEMESTRE_ETUS_FIELDS) is True
assert isinstance(etu["id"], int)
assert isinstance(etu["nip"], str)
assert isinstance(etu["ine"], str)
assert isinstance(etu["nom"], str)
assert etu["nom_usuel"] is None or isinstance(etu["nom_usuel"], str)
assert isinstance(etu["prenom"], str)
assert isinstance(etu["civilite"], str)
assert isinstance(etu["groups"], list)
etu_groups = etu["groups"]
for group in etu_groups:
assert isinstance(group["partition_id"], int)
assert isinstance(group["id"], int)
assert isinstance(group["formsemestre_id"], int)
assert group["partition_name"] is None or isinstance(
group["partition_name"], str
)
assert isinstance(group["numero"], int)
assert isinstance(group["bul_show_rank"], bool)
assert isinstance(group["show_in_lists"], bool)
assert isinstance(group["group_id"], int)
assert group["group_name"] is None or isinstance(group["group_name"], int)
### demissionnaires ###
r_demissionnaires = requests.get(
f"{API_URL}/formsemestre/{formsemestre_id}/etudiants/demissionnaires",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_demissionnaires.status_code == 200
formsemestre_etus = r_demissionnaires.json()
assert isinstance(formsemestre_etus, list)
for etu in formsemestre_etus:
assert verify_fields(etu, FORMSEMESTRE_ETUS_FIELDS) is True
assert isinstance(etu["id"], int)
assert isinstance(etu["nip"], str)
assert isinstance(etu["ine"], str)
assert isinstance(etu["nom"], str)
assert etu["nom_usuel"] is None or isinstance(etu["nom_usuel"], str)
assert isinstance(etu["prenom"], str)
assert isinstance(etu["civilite"], str)
assert isinstance(etu["groups"], list)
etu_groups = etu["groups"]
for group in etu_groups:
assert isinstance(group["partition_id"], int)
assert isinstance(group["id"], int)
assert isinstance(group["formsemestre_id"], int)
assert group["partition_name"] is None or isinstance(
group["partition_name"], str
)
assert isinstance(group["numero"], int)
assert isinstance(group["bul_show_rank"], bool)
assert isinstance(group["show_in_lists"], bool)
assert isinstance(group["group_id"], int)
assert group["group_name"] is None or isinstance(group["group_name"], int)
### defaillants ###
r_defaillants = requests.get(
f"{API_URL}/formsemestre/{formsemestre_id}/etudiants/defaillants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_defaillants.status_code == 200
formsemestre_etus = r_defaillants.json()
assert isinstance(formsemestre_etus, list)
for etu in formsemestre_etus:
assert verify_fields(etu, FORMSEMESTRE_ETUS_FIELDS) is True
assert isinstance(etu["id"], int)
assert isinstance(etu["nip"], str)
assert isinstance(etu["ine"], str)
assert isinstance(etu["nom"], str)
assert etu["nom_usuel"] is None or isinstance(etu["nom_usuel"], str)
assert isinstance(etu["prenom"], str)
assert isinstance(etu["civilite"], str)
assert isinstance(etu["groups"], list)
etu_groups = etu["groups"]
for group in etu_groups:
assert isinstance(group["partition_id"], int)
assert isinstance(group["id"], int)
assert isinstance(group["formsemestre_id"], int)
assert group["partition_name"] is None or isinstance(
group["partition_name"], str
)
assert isinstance(group["numero"], int)
assert isinstance(group["bul_show_rank"], bool)
assert isinstance(group["show_in_lists"], bool)
assert isinstance(group["group_id"], int)
assert group["group_name"] is None or isinstance(group["group_name"], int)
assert r.json() != r_demissionnaires.json()
assert r.json() != r_defaillants.json()
assert r_demissionnaires.json() != r_defaillants.json()
### ERROR ###
id_formsemestre_inexistant = 265165689619851621685
r_error = requests.get(
f"{API_URL}/formsemestre/{id_formsemestre_inexistant}/etudiants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_error.status_code == 404
r_error_demissionnaires = requests.get(
f"{API_URL}/formsemestre/{id_formsemestre_inexistant}/etudiants/demissionnaires",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_error_demissionnaires.status_code == 404
r_error_defaillants = requests.get(
f"{API_URL}/formsemestre/{id_formsemestre_inexistant}/etudiants/defaillants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_error_defaillants.status_code == 404
def test_formsemestre_programme(api_headers): def test_formsemestre_programme(api_headers):
""" """
Route: /formsemestre/1/programme Route: /formsemestre/1/programme
@ -131,3 +653,122 @@ def test_formsemestre_programme(api_headers):
assert verify_fields(modules[0], MODIMPL_FIELDS) assert verify_fields(modules[0], MODIMPL_FIELDS)
assert verify_fields(ressource, MODIMPL_FIELDS) assert verify_fields(ressource, MODIMPL_FIELDS)
assert verify_fields(sae, MODIMPL_FIELDS) assert verify_fields(sae, MODIMPL_FIELDS)
def test_etat_evals(
api_headers,
):
"""
Route : /formsemestre/<int:formsemestre_id>/etat_evals
"""
r = requests.get(
API_URL + "/formsemestre/1/etat_evals",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etat_evals = r.json()
assert len(etat_evals) == 3
for ue in etat_evals.values():
for module in ue:
assert isinstance(module["id"], int)
assert isinstance(module["titre"], str)
assert isinstance(module["evaluations"], list)
for eval in module["evaluations"]:
assert verify_fields(eval, EVAL_FIELDS)
assert isinstance(eval["id"], int)
assert eval["description"] is None or isinstance(
eval["description"], str
)
assert eval["datetime_epreuve"] is None or isinstance(
eval["datetime_epreuve"], str
)
assert isinstance(eval["heure_fin"], str)
assert isinstance(eval["coefficient"], float)
assert isinstance(eval["comptee"], str)
assert isinstance(eval["inscrits"], int)
assert isinstance(eval["manquantes"], int)
assert isinstance(eval["ABS"], int)
assert isinstance(eval["ATT"], int)
assert isinstance(eval["EXC"], int)
assert isinstance(eval["saisie_notes"], dict)
list_eval_id = [e["id"] for e in module["evaluations"]]
all_unique = True
for id in list_eval_id:
if list_eval_id.count(id) > 1:
all_unique = False
assert all_unique is True
saisie_notes = eval["saisie_notes"]
assert verify_fields(saisie_notes, SAISIE_NOTES_FIELDS)
assert eval["saisie_notes"]["datetime_debut"] is None or isinstance(
eval["saisie_notes"]["datetime_debut"], str
)
assert eval["saisie_notes"]["datetime_debut"] is None or isinstance(
eval["saisie_notes"]["datetime_fin"], str
)
assert eval["saisie_notes"]["datetime_debut"] is None or isinstance(
eval["saisie_notes"]["datetime_mediane"], str
)
if (
eval["saisie_notes"]["datetime_fin"] is not None
and eval["saisie_notes"]["datetime_mediane"] is not None
and eval["saisie_notes"]["datetime_debut"] is not None
):
assert (
eval["saisie_notes"]["datetime_fin"]
> eval["saisie_notes"]["datetime_mediane"]
)
assert (
eval["saisie_notes"]["datetime_fin"]
> eval["saisie_notes"]["datetime_debut"]
)
assert (
eval["saisie_notes"]["datetime_mediane"]
> eval["saisie_notes"]["datetime_debut"]
)
list_id_ue1 = []
list_titre_ue1 = []
list_id_ue2 = []
list_titre_ue2 = []
list_id_ue3 = []
list_titre_ue3 = []
i = 0
for ue in etat_evals.values():
i += 1
for module in ue:
if i == 1:
list_id_ue1.append(module["id"])
list_titre_ue1.append(module["id"])
elif i == 2:
list_id_ue2.append(module["id"])
list_titre_ue2.append(module["id"])
elif i == 3:
list_id_ue3.append(module["id"])
list_titre_ue3.append(module["id"])
assert list_id_ue1 != list_id_ue2
assert list_id_ue1 != list_titre_ue3
assert list_id_ue2 != list_titre_ue3
assert list_titre_ue1 != list_titre_ue2
assert list_titre_ue1 != list_titre_ue3
assert list_titre_ue2 != list_titre_ue3
##### ERROR #####
fake_eval_id = 153165161656849846516511321651651
r = requests.get(
f"{API_URL}/formsemestre/{fake_eval_id}/etat_evals",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 404

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Test API Jurys XXX TODO A ECRIRE """Test Logos
Utilisation : Utilisation :
créer les variables d'environnement: (indiquer les valeurs créer les variables d'environnement: (indiquer les valeurs
@ -24,10 +24,13 @@ from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
def test_jury_preparation(api_headers): def test_jury_preparation(api_headers):
""" """
Route: Test 'jury_preparation'
Route :
- /jury/formsemestre/<int:formsemestre_id>/preparation_jury
""" """
r = requests.get( r = requests.get(
SCODOC_URL API_URL
+ "/ScoDoc/api/jury/formsemestre/<int:formsemestre_id>/preparation_jury", + "/ScoDoc/api/jury/formsemestre/<int:formsemestre_id>/preparation_jury",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
@ -37,7 +40,10 @@ def test_jury_preparation(api_headers):
def test_jury_decisions(api_headers): def test_jury_decisions(api_headers):
""" """
Route: Test 'jury_decisions'
Route :
- /jury/formsemestre/<int:formsemestre_id>/decisions_jury
""" """
r = requests.get( r = requests.get(
API_URL + "/jury/formsemestre/<int:formsemestre_id>/decisions_jury", API_URL + "/jury/formsemestre/<int:formsemestre_id>/decisions_jury",
@ -45,62 +51,3 @@ def test_jury_decisions(api_headers):
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
# set_decision_jury
def test_set_decision_jury(api_headers):
r = requests.get(
SCODOC_URL
+ "/ScoDoc/api/jury/set_decision/etudid?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
r = requests.get(
SCODOC_URL
+ "/ScoDoc/api/jury/set_decision/nip?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
r = requests.get(
SCODOC_URL
+ "/ScoDoc/api/jury/set_decision/ine?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
# def test_annule_decision_jury(api_headers):
# """
# Route:
# """
# r = requests.get(
# SCODOC_URL
# + "/ScoDoc/api/jury/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/annule_decision",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
# r = requests.get(
# SCODOC_URL
# + "/ScoDoc/api/jury/nip/<int:nip>/formsemestre/<int:formsemestre_id>/annule_decision",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
# r = requests.get(
# SCODOC_URL
# + "/ScoDoc/api/jury/ine/<int:ine>/formsemestre/<int:formsemestre_id>/annule_decision",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200

View File

@ -20,103 +20,164 @@ Utilisation :
import requests import requests
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_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,
PARTITIONS_FIELDS,
PARTITIONS_GROUPS_ETU_FIELDS,
)
def test_partition(api_headers): def test_partition(api_headers):
""" """
Route: Test 'partition'
"""
fields = [
"partition_id",
"id",
"formsemestre_id",
"partition_name",
"numero",
"bul_show_rank",
"show_in_lists",
]
Route :
- /partitions/<int:formsemestre_id>
"""
partition_id = 1
r = requests.get( r = requests.get(
API_URL + "/partitions/1", f"{API_URL}/partitions/{partition_id}",
headers=api_headers, headers=api_headers,
verify=CHECK_CERTIFICATE, verify=CHECK_CERTIFICATE,
) )
assert r.status_code == 200 assert r.status_code == 200
partitions = r.json() partitions = r.json()
assert len(partitions) == 1 assert len(partitions) == 1
assert isinstance(partitions, list)
partition = partitions[0] partition = partitions[0]
fields_ok = verify_fields(partition, fields) assert isinstance(partition, dict)
assert fields_ok is True assert verify_fields(partition, PARTITIONS_FIELDS) is True
assert partition_id == partition["partition_id"]
assert isinstance(partition["partition_id"], int)
assert isinstance(partition["id"], int)
assert isinstance(partition["formsemestre_id"], int)
assert partition["partition_name"] is None or isinstance(
partition["partition_name"], str
)
assert isinstance(partition["numero"], int)
assert isinstance(partition["bul_show_rank"], bool)
assert isinstance(partition["show_in_lists"], bool)
def test_etud_in_group(api_headers): def test_etud_in_group(api_headers):
""" """
Route: Test 'etud_in_group'
"""
fields = [
"etudid",
"id",
"dept_id",
"nom",
"prenom",
"nom_usuel",
"civilite",
"date_naissance",
"lieu_naissance",
"dept_naissance",
"nationalite",
"statut",
"boursier",
"photo_filename",
"code_nip",
"code_ine",
"scodoc7_id",
"email",
"emailperso",
"domicile",
"codepostaldomicile",
"villedomicile",
"paysdomicile",
"telephone",
"telephonemobile",
"fax",
"typeadresse",
"description",
"group_id",
"etat",
"civilite_str",
"nom_disp",
"nomprenom",
"ne",
"email_default",
]
Routes :
- /partition/group/<int:group_id>
- /partition/group/<int:group_id>/etat/<string:etat>
"""
group_id = 1
r = requests.get( r = requests.get(
API_URL + "/partitions/groups/1", f"{API_URL}/partition/group/{group_id}",
headers=api_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 assert isinstance(r.json(), list)
assert fields_ok is True
# r = requests.get( for etu in r.json():
# API_URL + "/partitions/groups/1/etat/<string:etat>", assert verify_fields(etu, PARTITIONS_GROUPS_ETU_FIELDS)
# headers=api_headers, assert isinstance(etu["etudid"], int)
# verify=CHECK_CERTIFICATE, assert isinstance(etu["id"], int)
# ) assert isinstance(etu["dept_id"], int)
# assert r.status_code == 200 assert isinstance(etu["nom"], str)
assert isinstance(etu["prenom"], str)
assert isinstance(etu["nom_usuel"], str)
assert isinstance(etu["civilite"], str)
assert etu["date_naissance"] is None or isinstance(etu["date_naissance"], str)
assert etu["lieu_naissance"] is None or isinstance(etu["lieu_naissance"], str)
assert etu["dept_naissance"] is None or isinstance(etu["dept_naissance"], str)
assert etu["nationalite"] is None or isinstance(etu["nationalite"], str)
assert etu["statut"] is None or isinstance(etu["statut"], str)
assert etu["boursier"] is None or isinstance(etu["boursier"], bool)
assert etu["photo_filename"] is None or isinstance(etu["photo_filename"], str)
assert isinstance(etu["code_nip"], str)
assert isinstance(etu["code_ine"], str)
assert etu["scodoc7_id"] is None or isinstance(etu["scodoc7_id"], int)
assert isinstance(etu["email"], str)
assert etu["emailperso"] is None or isinstance(etu["emailperso"], str)
assert etu["domicile"] is None or isinstance(etu["domicile"], str)
assert etu["codepostaldomicile"] is None or isinstance(
etu["codepostaldomicile"], str
)
assert etu["villedomicile"] is None or isinstance(etu["villedomicile"], str)
assert etu["paysdomicile"] is None or isinstance(etu["paysdomicile"], str)
assert etu["telephone"] is None or isinstance(etu["telephone"], str)
assert etu["telephonemobile"] is None or isinstance(etu["telephonemobile"], str)
assert etu["fax"] is None or isinstance(etu["fax"], str)
assert isinstance(etu["typeadresse"], str)
assert etu["description"] is None or isinstance(etu["description"], int)
assert isinstance(etu["group_id"], int)
assert isinstance(etu["etat"], str)
assert isinstance(etu["civilite_str"], str)
assert isinstance(etu["nom_disp"], str)
assert isinstance(etu["nomprenom"], str)
assert isinstance(etu["ne"], str)
assert isinstance(etu["email_default"], str)
etat = "I"
r_etat = requests.get(
f"{API_URL}/partition/group/{group_id}/etat/{etat}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r_etat.status_code == 200
assert isinstance(r_etat.json(), list)
for etu in r_etat.json():
assert verify_fields(etu, PARTITIONS_GROUPS_ETU_FIELDS)
assert isinstance(etu["etudid"], int)
assert isinstance(etu["id"], int)
assert isinstance(etu["dept_id"], int)
assert isinstance(etu["nom"], str)
assert isinstance(etu["prenom"], str)
assert isinstance(etu["nom_usuel"], str)
assert isinstance(etu["civilite"], str)
assert etu["date_naissance"] is None or isinstance(etu["date_naissance"], str)
assert etu["lieu_naissance"] is None or isinstance(etu["lieu_naissance"], str)
assert etu["dept_naissance"] is None or isinstance(etu["dept_naissance"], str)
assert etu["nationalite"] is None or isinstance(etu["nationalite"], str)
assert etu["statut"] is None or isinstance(etu["statut"], str)
assert etu["boursier"] is None or isinstance(etu["boursier"], bool)
assert etu["photo_filename"] is None or isinstance(etu["photo_filename"], str)
assert isinstance(etu["code_nip"], str)
assert isinstance(etu["code_ine"], str)
assert etu["scodoc7_id"] is None or isinstance(etu["scodoc7_id"], int)
assert isinstance(etu["email"], str)
assert etu["emailperso"] is None or isinstance(etu["emailperso"], str)
assert etu["domicile"] is None or isinstance(etu["domicile"], str)
assert etu["codepostaldomicile"] is None or isinstance(
etu["codepostaldomicile"], str
)
assert etu["villedomicile"] is None or isinstance(etu["villedomicile"], str)
assert etu["paysdomicile"] is None or isinstance(etu["paysdomicile"], str)
assert etu["telephone"] is None or isinstance(etu["telephone"], str)
assert etu["telephonemobile"] is None or isinstance(etu["telephonemobile"], str)
assert etu["fax"] is None or isinstance(etu["fax"], str)
assert isinstance(etu["typeadresse"], str)
assert etu["description"] is None or isinstance(etu["description"], int)
assert isinstance(etu["group_id"], int)
assert isinstance(etu["etat"], str)
assert isinstance(etu["civilite_str"], str)
assert isinstance(etu["nom_disp"], str)
assert isinstance(etu["nomprenom"], str)
assert isinstance(etu["ne"], str)
assert isinstance(etu["email_default"], str)
assert etat == etu["etat"]
# # set_groups # # set_groups
# def test_set_groups(api_headers): # def test_set_groups(api_headers):
# """ # """
# Route: # Test 'set_groups'
#
# Routes :
# - /partitions/set_groups/partition/<int:partition_id>/groups/<string:groups_id>/delete/<string:groups_to_delete>"
# "/create/<string:groups_to_create>
# """ # """
# r = requests.get( # r = requests.get(
# SCODOC_URL # SCODOC_URL

View File

@ -29,7 +29,7 @@ def test_permissions(api_headers):
# Ce test va récupérer toutes les routes de l'API # Ce test va récupérer toutes les routes de l'API
app = create_app(RunningConfig) app = create_app(RunningConfig)
assert app assert app
# Les routes de l'API avec GET, excluant les logos pour le momeent XXX # Les routes de l'API avec GET, excluant les logos pour le moment XXX
api_rules = [ api_rules = [
r r
for r in app.url_map.iter_rules() for r in app.url_map.iter_rules()

View File

@ -1,5 +1,6 @@
"""Utilitaires pour les tests de l'API """Utilitaires pour les tests de l'API
""" """
import json
def verify_fields(json_response: dict, expected_fields: set) -> bool: def verify_fields(json_response: dict, expected_fields: set) -> bool:
@ -14,6 +15,40 @@ def verify_fields(json_response: dict, expected_fields: set) -> bool:
return all(field in json_response for field in expected_fields) return all(field in json_response for field in expected_fields)
def verify_occurences_ids_etus(json_response) -> bool:
"""
Vérifie si il n'y a pas deux fois le même id dans la liste d'étudiant donnée en paramètres
json_response : la réponse de la requête
Retourne True ou False
"""
list_etu = json.loads(json_response)
list_ids = [etu["id"] for etu in list_etu]
list_nip = [etu["nip"] for etu in list_etu]
list_ine = [etu["ine"] for etu in list_etu]
for id in list_ids:
if list_ids.count(id) > 1:
return False
for nip in list_nip:
if list_nip.count(nip) > 1:
return False
for ine in list_ine:
if list_ine.count(ine) > 1:
return False
return True
DEPARTEMENT_FIELDS = [
"id",
"acronym",
"description",
"visible",
"date_creation",
]
ETUD_FIELDS = { ETUD_FIELDS = {
"boursier", "boursier",
"civilite", "civilite",
@ -43,12 +78,12 @@ ETUD_FIELDS = {
} }
FORMATION_FIELDS = { FORMATION_FIELDS = {
"id", "dept_id",
"acronyme", "acronyme",
"titre_officiel", "titre_officiel",
"formation_code", "formation_code",
"code_specialite", "code_specialite",
"dept_id", "id",
"titre", "titre",
"version", "version",
"type_parcours", "type_parcours",
@ -56,6 +91,92 @@ FORMATION_FIELDS = {
"formation_id", "formation_id",
} }
FORMATION_EXPORT_FIELDS = {
"dept_id",
"acronyme",
"titre_officiel",
"formation_code",
"code_specialite",
"id",
"titre",
"version",
"type_parcours",
"referentiel_competence_id",
"formation_id",
"ue",
}
FORMATION_EXPORT_UE_FIELDS = {
"acronyme",
"numero",
"titre",
"type",
"ue_code",
"ects",
"is_external",
"code_apogee",
"coefficient",
"semestre_idx",
"color",
"reference",
"matiere",
}
FORMATION_EXPORT_UE_MATIERE_FIELDS = {
"titre",
"numero",
"module",
}
FORMATION_EXPORT_UE_MATIERE_MODULE_FIELDS = {
"titre",
"abbrev",
"code",
"heures_cours",
"heures_td",
"coefficient",
"ects",
"semestre_id",
"numero",
"code_apogee",
"module_type",
"coefficients",
}
FORMATION_EXPORT_UE_MATIERE_MODULE_COEF_FIELDS = {
"ue_reference",
"coef",
}
FORMSEMESTRE_FIELDS = [
"titre",
"gestion_semestrielle",
"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",
"titre_formation",
"date_debut_iso",
"date_fin_iso",
"responsables",
]
FSEM_FIELDS = { FSEM_FIELDS = {
"block_moyennes", "block_moyennes",
"bul_bgcolor", "bul_bgcolor",
@ -131,3 +252,468 @@ UE_FIELDS = {
"color", "color",
"ue_id", "ue_id",
} }
BULLETIN_FIELDS = {
"version",
"type",
"date",
"publie",
"etudiant",
"formation",
"formsemestre_id",
"etat_inscription",
"options",
"ressources",
"saes",
"ues",
"semestre",
}
BULLETIN_ETUDIANT_FIELDS = {
"civilite",
"code_ine",
"code_nip",
"date_naissance",
"dept_id",
"dept_acronym",
"email",
"emailperso",
"etudid",
"nom",
"prenom",
"nomprenom",
"lieu_naissance",
"dept_naissance",
"nationalite",
"boursier",
"fiche_url",
"photo_url",
"id",
"domicile",
"villedomicile",
"telephone",
"fax",
"description",
"codepostaldomicile",
"paysdomicile",
"telephonemobile",
"typeadresse",
}
BULLETIN_FORMATION_FIELDS = {"id", "acronyme", "titre_officiel", "titre"}
BULLETIN_OPTIONS_FIELDS = {
"show_abs",
"show_abs_modules",
"show_ects",
"show_codemodules",
"show_matieres",
"show_rangs",
"show_ue_rangs",
"show_mod_rangs",
"show_moypromo",
"show_minmax",
"show_minmax_mod",
"show_minmax_eval",
"show_coef",
"show_ue_cap_details",
"show_ue_cap_current",
"show_temporary",
"temporary_txt",
"show_uevalid",
"show_date_inscr",
}
BULLETIN_RESSOURCES_FIELDS = {
"R101",
"R102",
"R103",
"R104",
"R105",
"R106",
"R107",
"R108",
"R109",
"R110",
"R111",
"R112",
"R113",
"R114",
"R115",
}
BULLETIN_SAES_FIELDS = {
"SAE11",
"SAE12",
"SAE13",
"SAE14",
"SAE15",
"SAE16",
}
########### RESSOURCES ET SAES ###########
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_FIELDS = {
"id",
"titre",
"code_apogee",
"url",
"moyenne",
"evaluations",
}
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_FIELDS = {
"id",
"description",
"date",
"heure_debut",
"heure_fin",
"coef",
"poids",
"note",
"url",
}
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_POIDS_FIELDS = {
"RT1.1",
"RT2.1",
"RT3.1",
}
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS = {
"value",
"min",
"max",
"moy",
}
########### UES ###########
BULLETIN_UES_FIELDS = {"RT1.1", "RT2.1", "RT3.1"}
BULLETIN_UES_UE_FIELDS = {
"id",
"titre",
"numero",
"type",
"color",
"competence",
"moyenne",
"bonus",
"malus",
"capitalise",
"ressources",
"saes",
"ECTS",
}
BULLETIN_UES_UE_MOYENNE_FIELDS = {"value", "min", "max", "moy", "rang", "total"}
BULLETIN_UES_RT11_RESSOURCES_FIELDS = {
"R101",
"R102",
"R103",
"R104",
"R106",
"R108",
"R110",
"R111",
"R112",
"R113",
"R114",
}
BULLETIN_UES_RT21_RESSOURCES_FIELDS = {
"R101",
"R103",
"R104",
"R105",
"R110",
"R111",
"R112",
"R113",
"R114",
"R115",
}
BULLETIN_UES_RT31_RESSOURCES_FIELDS = {
"R101",
"R107",
"R108",
"R109",
"R110",
"R111",
"R112",
"R115",
}
BULLETIN_UES_UE_RESSOURCES_RESSOURCE_FIELDS = {"id", "coef", "moyenne"}
BULLETIN_UES_RT11_SAES_FIELDS = {
"SAE11",
"SAE12",
}
BULLETIN_UES_RT21_SAES_FIELDS = {"SAE13"}
BULLETIN_UES_RT31_SAES_FIELDS = {
"SAE14",
"SAE15",
}
BULLETIN_UES_UE_SAES_SAE_FIELDS = {"id", "coef", "moyenne"}
BULLETIN_UES_UE_ECTS_FIELDS = {"acquis", "total"}
########### SEMESTRE ###########
BULLETIN_SEMESTRE_FIELDS = {
"etapes",
"date_debut",
"date_fin",
"annee_universitaire",
"numero",
"inscription",
"groupes",
"absences",
"ECTS",
"notes",
"rang",
}
BULLETIN_SEMESTRE_ABSENCES_FIELDS = {"injustifie", "total"}
BULLETIN_SEMESTRE_ECTS_FIELDS = {"acquis", "total"}
BULLETIN_SEMESTRE_NOTES_FIELDS = {"value", "min", "moy", "max"}
BULLETIN_SEMESTRE_RANG_FIELDS = {"value", "total"}
EVAL_FIELDS = {
"id",
"description",
"datetime_epreuve",
"heure_fin",
"coefficient",
"comptee",
"inscrits",
"manquantes",
"ABS",
"ATT",
"EXC",
"saisie_notes",
}
SAISIE_NOTES_FIELDS = {"datetime_debut", "datetime_fin", "datetime_mediane"}
REF_COMP_FIELDS = {
"dept_id",
"annexe",
"specialite",
"specialite_long",
"type_structure",
"type_departement",
"type_titre",
"version_orebut",
"scodoc_date_loaded",
"scodoc_orig_filename",
"competences",
"parcours",
}
ABSENCES_FIELDS = {
"jour",
"matin",
"estabs",
"estjust",
"description",
"begin",
"end",
}
ABSENCES_GROUP_ETAT_FIELDS = {"etudid", "list_abs"}
FORMSEMESTRE_ETUS_FIELDS = {
"id",
"nip",
"ine",
"nom",
"nom_usuel",
"prenom",
"civilite",
"groups",
}
FORMSEMESTRE_ETUS_GROUPS_FIELDS = {
"partition_id",
"id",
"formsemestre_id",
"partition_name",
"numero",
"bul_show_rank",
"show_in_lists",
"group_id",
"group_name",
}
EVALUATIONS_FIELDS = {
"id",
"jour",
"heure_fin",
"note_max",
"visibulletin",
"evaluation_type",
"moduleimpl_id",
"heure_debut",
"description",
"coefficient",
"publish_incomplete",
"numero",
"evaluation_id",
"date_debut",
"date_fin",
"poids",
"jouriso",
"duree",
"descrheure",
"matin",
"apresmidi",
}
EVALUATION_FIELDS = {
"id",
"etudid",
"evaluation_id",
"value",
"comment",
"date",
"uid",
}
PARTITIONS_FIELDS = {
"partition_id",
"id",
"formsemestre_id",
"partition_name",
"numero",
"bul_show_rank",
"show_in_lists",
}
PARTITIONS_GROUPS_ETU_FIELDS = {
"etudid",
"id",
"dept_id",
"nom",
"prenom",
"nom_usuel",
"civilite",
"date_naissance",
"lieu_naissance",
"dept_naissance",
"nationalite",
"statut",
"boursier",
"photo_filename",
"code_nip",
"code_ine",
"scodoc7_id",
"email",
"emailperso",
"domicile",
"codepostaldomicile",
"villedomicile",
"paysdomicile",
"telephone",
"telephonemobile",
"fax",
"typeadresse",
"description",
"group_id",
"etat",
"civilite_str",
"nom_disp",
"nomprenom",
"ne",
"email_default",
}
FORMSEMESTRE_BULLETINS_FIELDS = {
"version",
"type",
"date",
"publie",
"etudiant",
"formation",
"formsemestre_id",
"etat_inscription",
"options",
"ressources",
"saes",
"ues",
"semestre",
}
FORMSEMESTRE_BULLETINS_ETU_FIELDS = {
"civilite",
"code_ine",
"code_nip",
"date_naissance",
"dept_id",
"dept_acronym",
"email",
"emailperso",
"etudid",
"nom",
"prenom",
"nomprenom",
"lieu_naissance",
"dept_naissance",
"nationalite",
"boursier",
"fiche_url",
"photo_url",
"id",
"codepostaldomicile",
"paysdomicile",
"telephonemobile",
"typeadresse",
"domicile",
"villedomicile",
"telephone",
"fax",
"description",
}
FORMSEMESTRE_BULLETINS_FORMATION_FIELDS = {
"id",
"acronyme",
"titre_officiel",
"titre",
}
FORMSEMESTRE_BULLETINS_OPT_FIELDS = {
"show_abs",
"show_abs_modules",
"show_ects",
"show_codemodules",
"show_matieres",
"show_rangs",
"show_ue_rangs",
"show_mod_rangs",
"show_moypromo",
"show_minmax",
"show_minmax_mod",
"show_minmax_eval",
"show_coef",
"show_ue_cap_details",
"show_ue_cap_current",
"show_temporary",
"temporary_txt",
"show_uevalid",
"show_date_inscr",
}

View File

@ -10,7 +10,7 @@
FLASK_DEBUG=1 FLASK_DEBUG=1
2) En tant qu'utilisateur scodoc, lancer: 2) En tant qu'utilisateur scodoc, lancer:
tools/create_database.sh SCODOC_TEST_API tools/create_database.sh
flask db upgrade flask db upgrade
flask sco-db-init --erase flask sco-db-init --erase
flask init-test-database flask init-test-database
@ -24,12 +24,32 @@
""" """
import datetime import datetime
import random import random
import time
import sys import sys
from app.auth.models import Role, User from app.auth.models import Role, User
from app import models from app import models
from app.models import Departement, Formation, FormSemestre, Identite from app.models import (
Departement,
Formation,
FormSemestre,
Identite,
ModuleImpl,
NotesNotes,
ApcReferentielCompetences,
ApcCompetence,
Absence,
FormSemestreEtape,
)
from app import db from app import db
from app.models.but_refcomp import (
ApcParcours,
ApcAnneeParcours,
ApcSituationPro,
ApcComposanteEssentielle,
ApcNiveau,
ApcAppCritique,
)
from app.scodoc import ( from app.scodoc import (
sco_cache, sco_cache,
sco_evaluation_db, sco_evaluation_db,
@ -38,6 +58,7 @@ from app.scodoc import (
sco_groups, sco_groups,
) )
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_saisie_notes import notes_add
from tools.fakeportal.gen_nomprenoms import nomprenom from tools.fakeportal.gen_nomprenoms import nomprenom
random.seed(12345678) # tests reproductibles random.seed(12345678) # tests reproductibles
@ -160,6 +181,8 @@ def create_formsemestre(
def inscrit_etudiants(etuds: list, formsemestre: 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:
aleatoire = random.randint(0, 10)
if aleatoire <= 3:
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules( sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
formsemestre.id, formsemestre.id,
etud.id, etud.id,
@ -167,6 +190,22 @@ def inscrit_etudiants(etuds: list, formsemestre: FormSemestre):
etat="I", etat="I",
method="init db test", method="init db test",
) )
elif 3 < aleatoire <= 6:
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
formsemestre.id,
etud.id,
group_ids=[],
etat="D",
method="init db test",
)
else:
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
formsemestre.id,
etud.id,
group_ids=[],
etat="DEF",
method="init db test",
)
def create_evaluations(formsemestre: FormSemestre): def create_evaluations(formsemestre: FormSemestre):
@ -188,6 +227,286 @@ def create_evaluations(formsemestre: FormSemestre):
evaluation_id = sco_evaluation_db.do_evaluation_create(**args) evaluation_id = sco_evaluation_db.do_evaluation_create(**args)
def saisie_notes_evaluations(formsemestre: FormSemestre, user: User):
"""
Saisie les notes des evaluations d'un semestre
"""
etuds = formsemestre.etuds
list_etuds = []
for etu in etuds:
list_etuds.append(etu)
date_debut = formsemestre.date_debut
date_fin = formsemestre.date_fin
list_ues = formsemestre.query_ues()
def saisir_notes(evaluation_id: int, condition: int):
"""
Permet de saisir les notes de manière aléatoire suivant une condition
Définition des valeurs de condition :
0 : all_notes_saisies
1 : all_notes_manquantes
2 : some_notes_manquantes
"""
if condition == 0 or condition == 2:
if condition == 0:
for etu in list_etuds:
note = NotesNotes(
etu.id,
evaluation_id,
random.uniform(0, 20),
"",
date_debut + random.random() * (date_fin - date_debut),
user.id,
)
db.session.add(note)
db.session.commit()
else:
percent = 80 / 100
len_etuds = len(list_etuds)
new_list_etuds = random.sample(list_etuds, k=int(percent * len_etuds))
for etu in new_list_etuds:
note = NotesNotes(
etu.id,
evaluation_id,
random.uniform(0, 20),
"",
date_debut + random.random() * (date_fin - date_debut),
user.id,
)
db.session.add(note)
db.session.commit()
for ue in list_ues:
mods = ue.modules
for mod in mods:
moduleimpl = ModuleImpl.query.get_or_404(mod.id)
for evaluation in moduleimpl.evaluations:
condition_saisie_notes = random.randint(0, 2)
saisir_notes(evaluation.id, condition_saisie_notes)
def create_ref_comp(formation: Formation):
"""
Créer un referentiel de competences
"""
# ### ApcSituationPro ###
# apc_situation_pro_id = 1
# apc_situation_pro_competence_id = 1
# apc_situation_pro_libelle = ""
#
# apc_situation_pro = ApcSituationPro(
# apc_situation_pro_id, apc_situation_pro_competence_id, apc_situation_pro_libelle
# )
# db.session.add(apc_situation_pro)
# db.session.commit()
#
# ### ApcComposanteEssentielle ###
# apc_composante_essentielle_id = 1
# apc_composante_essentielle_competence_id = 1
# apc_composante_essentielle_libelle = ""
#
# apc_composante_essentielle = ApcComposanteEssentielle(
# apc_composante_essentielle_id,
# apc_composante_essentielle_competence_id,
# apc_composante_essentielle_libelle,
# )
# db.session.add(apc_composante_essentielle)
# db.session.commit()
#
# ### ApcAppCritique ###
# apc_app_critique_id = 1
# apc_app_critique_niveau_id = 1
# apc_app_critique_code = ""
# apc_app_critique_libelle = ""
# apc_app_critique_modules = formation.modules
#
# apc_app_critique = ApcAppCritique(
# apc_app_critique_id,
# apc_app_critique_niveau_id,
# apc_app_critique_code,
# apc_app_critique_libelle,
# apc_app_critique_modules,
# )
# db.session.add(apc_app_critique)
# db.session.commit()
#
# ### ApcNiveau ###
# apc_niveau_id = 1
# apc_niveau_competence_id = 1
# apc_niveau_libelle = ""
# apc_niveau_annee = ""
# apc_niveau_ordre = 1
# apc_niveau_app_critiques = apc_app_critique
#
# apc_niveau = ApcNiveau(
# apc_niveau_id,
# apc_niveau_competence_id,
# apc_niveau_libelle,
# apc_niveau_annee,
# apc_niveau_ordre,
# apc_niveau_app_critiques,
# )
# db.session.add(apc_niveau)
# db.session.commit()
#
# ### ApcCompetence ###
# apc_competence_id = 1
# apc_competence_referentiel_id = 1
# apc_competence_id_orebut = ""
# apc_competence_titre = ""
# apc_competence_titre_long = ""
# apc_competence_couleur = ""
# apc_competence_numero = 1
# apc_competence_xml_attribs = { # xml_attrib : attribute
# "id": "id_orebut",
# "nom_court": "titre", # was name
# "libelle_long": "titre_long",
# }
# apc_competence_situations = apc_situation_pro
# apc_competence_composantes_essentielles = apc_composante_essentielle
# apc_competence_niveaux = apc_niveau
#
# apc_competence = ApcCompetence(
# apc_competence_id,
# apc_competence_referentiel_id,
# apc_competence_id_orebut,
# apc_competence_titre,
# apc_competence_titre_long,
# apc_competence_couleur,
# apc_competence_numero,
# apc_competence_xml_attribs,
# apc_competence_situations,
# apc_competence_composantes_essentielles,
# apc_competence_niveaux,
# )
# db.session.add(apc_competence)
# db.session.commit()
#
# ### ApcAnneeParcours ###
# apc_annee_parcours_id = 1
# apc_annee_parcours_parcours_id = 1
# apc_annee_parcours_ordre = 1
#
# ap_annee_parcours = ApcAnneeParcours(
# apc_annee_parcours_id, apc_annee_parcours_parcours_id, apc_annee_parcours_ordre
# )
#
# ### ApcParcours ###
# apc_parcours_id = 1
# apc_parcours_referentiel_id = 1
# apc_parcours_numero = 1
# apc_parcours_code = ""
# apc_parcours_libelle = ""
# apc_parcours_annees = ap_annee_parcours
#
# apc_parcours = ApcParcours(
# apc_parcours_id,
# apc_parcours_referentiel_id,
# apc_parcours_numero,
# apc_parcours_code,
# apc_parcours_libelle,
# apc_parcours_annees,
# )
# db.session.add(apc_parcours)
# db.session.commit()
### ApcReferentielCompetences ###
apc_referentiel_competences_id = 1
apc_referentiel_competences_dept_id = 1
apc_referentiel_competences_annexe = ""
apc_referentiel_competences_specialite = ""
apc_referentiel_competences_specialite_long = ""
apc_referentiel_competences_type_titre = ""
apc_referentiel_competences_type_structure = ""
apc_referentiel_competences_type_departement = ""
apc_referentiel_competences_version_orebut = ""
apc_referentiel_competences_xml_attribs = {
"type": "type_titre",
"version": "version_orebut",
}
apc_referentiel_competences_scodoc_date_loaded = ""
apc_referentiel_competences_scodoc_orig_filename = ""
# apc_referentiel_competences_competences = apc_competence
# apc_referentiel_competences_parcours = apc_parcours
# apc_referentiel_competences_formations = formation
apc_referentiel_competences = ApcReferentielCompetences(
apc_referentiel_competences_id,
apc_referentiel_competences_dept_id,
apc_referentiel_competences_annexe,
apc_referentiel_competences_specialite,
apc_referentiel_competences_specialite_long,
apc_referentiel_competences_type_titre,
apc_referentiel_competences_type_structure,
apc_referentiel_competences_type_departement,
apc_referentiel_competences_version_orebut,
apc_referentiel_competences_xml_attribs,
# apc_referentiel_competences_scodoc_date_loaded,
apc_referentiel_competences_scodoc_orig_filename,
# apc_referentiel_competences_competences,
# apc_referentiel_competences_parcours,
# apc_referentiel_competences_formations,
)
db.session.add(apc_referentiel_competences)
db.session.commit()
formation.referentiel_competence_id = apc_referentiel_competences.id
db.session.commit()
def add_absences(formsemestre: FormSemestre):
"""
Ajoute des absences en base
"""
date_debut = formsemestre.date_debut
date_fin = formsemestre.date_fin
etuds = formsemestre.etuds
id_db = 1
for etu in etuds:
aleatoire = random.randint(0, 1)
if aleatoire == 1:
nb_absences = random.randint(1, 5)
for absence in range(0, nb_absences):
id = id_db
etudid = etu.id
jour = date_debut + random.random() * (date_fin - date_debut)
estabs = True
estjust = True if random.randint(0, 1) == 1 else False
matin = True if random.randint(0, 1) == 1 else False
description = ""
abs = Absence(id, etudid, jour, estabs, estjust, matin, description)
db.session.add(abs)
db.session.commit()
id_db += 1
def create_etape_apo(formsemestre: FormSemestre):
"""
Ajoute étape apoge au formsemestre
"""
etape_apo1 = FormSemestreEtape(
id=1, formsemestre_id=formsemestre.id, etape_apo="A1"
)
db.session.add(etape_apo1)
etape_apo2 = FormSemestreEtape(
id=2, formsemestre_id=formsemestre.id, etape_apo="A2"
)
db.session.add(etape_apo2)
etape_apo3 = FormSemestreEtape(
id=3, formsemestre_id=formsemestre.id, etape_apo="A3"
)
db.session.add(etape_apo3)
list_etapes = [etape_apo1, etape_apo2, etape_apo3]
formsemestre.etapes = list_etapes
db.session.commit()
def init_test_database(): def init_test_database():
"""Appelé par la commande `flask init-test-database` """Appelé par la commande `flask init-test-database`
@ -201,6 +520,10 @@ def init_test_database():
formsemestre = create_formsemestre(formation, user_lecteur) formsemestre = create_formsemestre(formation, user_lecteur)
create_evaluations(formsemestre) create_evaluations(formsemestre)
inscrit_etudiants(etuds, formsemestre) inscrit_etudiants(etuds, formsemestre)
saisie_notes_evaluations(formsemestre, user_lecteur)
create_ref_comp(formation)
add_absences(formsemestre)
create_etape_apo(formsemestre)
# à compléter # à compléter
# - groupes # - groupes
# - absences # - absences