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 app.api import bp
from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required
from app.api.auth import permission_required_api
from app.models import Identite
from app.scodoc import notesdb as ndb
from app.scodoc import sco_abs
from app.scodoc.sco_groups import get_group_members
from app.scodoc.sco_permissions import Permission
@bp.route("/absences/etudid/<int:etudid>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def absences(etudid: int = None):
"""
Retourne la liste des absences d'un étudiant donné
@ -51,15 +58,14 @@ def absences(etudid: int = None):
)
# Absences de l'étudiant
ndb.open_db_connection()
absences = sco_abs.list_abs_date(etud.id)
for absence in absences:
abs_list = sco_abs.list_abs_date(etud.id)
for absence in abs_list:
absence["jour"] = absence["jour"].isoformat()
return jsonify(absences)
return jsonify(abs_list)
@bp.route("/absences/etudid/<int:etudid>/just", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def absences_just(etudid: int = None):
"""
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)
# 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(
# "/absences/abs_group_etat/<int:group_id>",
# methods=["GET"],
# "/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs",
# methods=["POST"],
# defaults={"just_or_not": 0},
# )
# @bp.route(
# "/absences/abs_group_etat/group_id/<int:group_id>/date_debut/<string:date_debut>/date_fin/<string:date_fin>",
# methods=["GET"],
# "/absences/etudid/<int:etudid>/list_abs/<string:list_abs>/reset_etud_abs/only_not_just",
# 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_permission_required(Permission.APIView)
# def abs_groupe_etat( # XXX A REVOIR XXX
# group_id: int, date_debut, date_fin, with_boursier=True, format="html"
# ):
# @token_permission_required(Permission.APIAbsChange)
# def reset_etud_abs(etudid: int, list_abs: str, just_or_not: int = 0):
# """
# 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:
# # Utilisation de la fonction get_group_members
# members = get_group_members(group_id)
# except ValueError:
# return error_response(
# 404, message="La requête ne peut être traitée en létat actuel"
# )
# # Uniquement les absences justifiées
# elif just_or_not == 1:
# list_abs_not_just = []
# # Trie des absences justifiées
# for abs in list_abs:
# 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 = []
# # Filtre entre les deux dates renseignées
# for member in members:
# abs = sco_abs.list_abs_date(member.id, date_debut, date_fin)
# data.append(abs)
# # Ajout de la liste d'absences en base
# add_abslist(list_abs_not_just)
# # return jsonify(data) # XXX TODO faire en sorte de pouvoir renvoyer sa (ex to_dict() dans absences)
# return error_response(501, message="Not implemented")
# # Uniquement les absences non justifiées
# 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 flask import abort
from flask import g
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth
from flask_login import current_user
from app import log
from app.auth.models import User
@ -57,7 +56,10 @@ def basic_auth_error(status):
@token_auth.verify_token
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
g.current_user = user
return user
@ -65,7 +67,7 @@ def verify_token(token) -> User:
@token_auth.error_handler
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)
@ -75,7 +77,7 @@ def get_user_roles(user):
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):
@wraps(f)
@ -84,13 +86,39 @@ def token_permission_required(permission):
current_user = basic_auth.current_user()
if not current_user or not current_user.has_permission(permission, None):
if current_user:
log(f"API permission denied (user {current_user})")
message = f"API permission denied (user {current_user})"
else:
log("API permission denied (no user supplied)")
abort(403)
message = f"API permission denied (no user supplied)"
log(message)
# raise werkzeug.exceptions.Forbidden(description=message)
return error_response(403, message=None)
return f(*args, **kwargs)
# return decorated_function(token_auth.login_required())
return decorated_function
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
from app import models
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.scodoc.sco_permissions import Permission
@ -21,20 +21,25 @@ def get_departement(dept_ident: str) -> Departement:
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"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def departements_ids():
"""Liste des ids de départements"""
return jsonify([dept.id for dept in Departement.query])
@bp.route("/departement/<string:dept_ident>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def departement(dept_ident: str):
@bp.route("/departement/<string:acronym>", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def departement(acronym: 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 :
{
@ -45,27 +50,27 @@ def departement(dept_ident: str):
"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())
@bp.route("/departements", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def departements():
"""Liste les départements"""
return jsonify([dept.to_dict() for dept in Departement.query])
@bp.route("/departement/id/<int:dept_id>", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def departement_by_id(dept_id: int):
"""
Info sur un département. Accès par id.
"""
dept = Departement.query.get_or_404(dept_id)
return jsonify(dept.to_dict())
@bp.route("/departement/<string:dept_ident>/etudiants", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def list_etudiants(dept_ident: str):
@bp.route("/departement/<string:acronym>/etudiants", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def dept_etudiants(acronym: str):
"""
Retourne la liste des étudiants d'un département
dept: l'acronym d'un département
formsemestre_id: l'id d'un formesemestre
acronym: l'acronyme d'un département
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 = get_departement(dept_ident)
dept = Departement.query.filter_by(acronym=acronym).first_or_404()
return jsonify([etud.to_dict_short() for etud in dept.etudiants])
@bp.route("/departement/<string:dept_ident>/formsemestres_ids", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def formsemestres_ids(dept_ident: str):
@bp.route("/departement/id/<int:dept_id>/etudiants", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def dept_etudiants_by_id(dept_id: int):
"""
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"""
# 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()
return jsonify([formsemestre.id for formsemestre in dept.formsemestres])
@bp.route("/departement/<string:dept_ident>/formsemestres_courants", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def liste_semestres_courant(dept_ident: str):
"""
Liste des semestres actifs d'un départements donné
@bp.route("/departement/id/<int:dept_id>/formsemestres_ids", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def dept_formsemestres_ids_by_id(dept_id: int):
"""liste des ids formsemestre du département"""
dept = Departement.query.get_or_404(dept_id)
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 :
[
@ -144,7 +160,7 @@ def liste_semestres_courant(dept_ident: str):
]
"""
# 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
formsemestres = models.FormSemestre.query.filter(
@ -153,4 +169,23 @@ def liste_semestres_courant(dept_ident: str):
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):
"""Réponse sur erreur"""
payload = {"error": HTTP_STATUS_CODES.get(status_code, "Unknown error")}
if message:
payload["message"] = message
@ -36,4 +37,5 @@ def error_response(status_code, message=None):
def bad_request(message):
"400 Bad Request response"
return error_response(400, message)

View File

@ -13,18 +13,19 @@ from flask import jsonify
import app
from app.api import bp
from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required
from app.api.auth import permission_required_api
from app.api import tools
from app.models import Departement, FormSemestreInscription, FormSemestre, Identite
from app.scodoc import sco_bulletins
from app.scodoc import sco_groups
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
from app.scodoc.sco_permissions import Permission
@bp.route("/etudiants/courant", defaults={"long": False})
@bp.route("/etudiants/courant/long", defaults={"long": True})
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etudiants_courant(long=False):
@bp.route("/etudiants/courants", defaults={"long": False})
@bp.route("/etudiants/courants/long", defaults={"long": True})
@permission_required_api(Permission.ScoView, Permission.APIView)
def etudiants_courants(long=False):
"""
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/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiant/ine/<string:ine>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def etudiant(etudid: int = None, nip: str = None, ine: str = None):
"""
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": ""
}
"""
if etudid is not None:
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()
etud = tools.get_etud(etudid, nip, ine)
if etud is None:
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/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def etudiants(etudid: int = None, nip: str = None, ine: str = None):
"""
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/nip/<string:nip>/formsemestres")
@bp.route("/etudiant/ine/<string:ine>/formsemestres")
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
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.
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
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",
"resp_can_edit": false,
"dept_id": 1,
"elt_annee_apo": null,
"elt_sem_apo": null,
"ens_can_edit_eval": false,
"etat": true,
"resp_can_change_ens": true,
"formation_id": 1,
"formsemestre_id": 1,
"gestion_compensation": false,
"gestion_semestrielle": false,
"id": 1,
"modalite": "FI",
"ens_can_edit_eval": false,
"formation_id": 1,
"gestion_compensation": false,
"elt_sem_apo": null,
"semestre_id": 1,
"bul_hide_xml": false,
"elt_annee_apo": null,
"titre": "Semestre test",
"block_moyennes": false,
"scodoc7_id": null,
"date_debut": "01/09/2021",
"gestion_semestrielle": false,
"bul_bgcolor": "white",
"formsemestre_id": 1,
"titre_num": "Semestre test semestre 1",
"date_debut_iso": "2021-09-01",
"date_fin_iso": "2022-08-31",
"resp_can_change_ens": true,
"resp_can_edit": false,
"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)
return jsonify(
[formsemestre.to_dict(convert_parcours=True) for formsemestre in formsemestres]
[formsemestre.to_dict(convert_objects=True) for formsemestre in formsemestres]
)
@bp.route(
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin",
methods=["GET"],
defaults={"version": "long"},
defaults={"version": "long", "pdf": False},
)
@bp.route(
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin",
methods=["GET"],
defaults={"version": "long"},
defaults={"version": "long", "pdf": False},
)
@bp.route(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin",
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(
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"],
defaults={"version": "short"},
defaults={"version": "short", "pdf": False},
)
@bp.route(
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"],
defaults={"version": "short"},
defaults={"version": "short", "pdf": False},
)
@bp.route(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"],
defaults={"version": "short"},
defaults={"version": "short", "pdf": False},
)
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etudiant_bulletin_semestre(
@bp.route(
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
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,
etudid: int = None,
nip: str = None,
ine: str = None,
version="long",
pdf: bool = False,
):
"""
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
nip : le code nip d'un étudiant
ine : le code ine d'un étudiant
Exemple de résultat :
{
"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},
},
},
}
Exemple de résultat : voir https://scodoc.org/ScoDoc9API/#bulletin
"""
formsemestre = FormSemestre.query.filter_by(id=formsemestre_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)
if pdf:
pdf_response, _ = do_formsemestre_bulletinetud(
formsemestre, etudid, version=version, format="pdf"
)
return pdf_response
return sco_bulletins.get_formsemestre_bulletin_etud_json(
formsemestre, etud, version=version
)
@ -466,8 +343,7 @@ def etudiant_bulletin_semestre(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/groups",
methods=["GET"],
)
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def etudiant_groups(
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
import app
from app import models
from app.models import Evaluation
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.models import Evaluation
from app.scodoc.sco_evaluation_db import do_evaluation_get_all_notes
from app.scodoc.sco_permissions import Permission
@bp.route("/evaluations/<int:moduleimpl_id>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
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
@ -39,7 +47,7 @@ def evaluations(moduleimpl_id: int):
"evaluation_id": 1,
"jouriso": "2022-04-20",
"duree": "1h",
"descrheure": " de 08h00 \u00e0 09h00",
"descrheure": " de 08h00 à 09h00",
"matin": 1,
"apresmidi": 0
},
@ -56,8 +64,7 @@ def evaluations(moduleimpl_id: int):
@bp.route("/evaluation/eval_notes/<int:evaluation_id>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def evaluation_notes(evaluation_id: int):
"""
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()
dept = models.Departement.query.filter_by(
id=evaluation.moduleimpl.formsemestre.dept_id
).first()
dept = evaluation.moduleimpl.formsemestre.departement
app.set_sco_dept(dept.acronym)
try:
# Utilisation de la fonction do_evaluation_get_all_notes
data = do_evaluation_get_all_notes(evaluation_id)
except AttributeError: # ???
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
import app
from app import models
from app.api import bp
from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required
from app.api.auth import permission_required_api
from app.models.formations import Formation
from app.scodoc import sco_formations
from app.scodoc.sco_permissions import Permission
@bp.route("/formations_ids", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def formations_ids():
@bp.route("/formations", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def formations():
"""
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 ]
"""
# Récupération de toutes les formations
list_formations = models.Formation.query.all()
# Mise en forme des données
data = [d.id for d in list_formations]
data = [d.id for d in models.Formation.query]
return jsonify(data)
@bp.route("/formation/<int:formation_id>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
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
@ -42,7 +55,7 @@ def formation_by_id(formation_id: int):
{
"id": 1,
"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",
"code_specialite": null,
"dept_id": 1,
@ -53,13 +66,8 @@ def formation_by_id(formation_id: int):
"formation_id": 1
}
"""
# Récupération de la formation
formation = models.Formation.query.filter_by(id=formation_id).first_or_404()
# Mise en forme des données
data = formation.to_dict()
return jsonify(data)
formation = models.Formation.query.get_or_404(formation_id)
return jsonify(formation.to_dict())
@bp.route(
@ -72,13 +80,13 @@ def formation_by_id(formation_id: int):
methods=["GET"],
defaults={"export_ids": True},
)
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def formation_export_by_formation_id(formation_id: int, export_ids=False):
"""
Retourne la formation, avec UE, matières, modules
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 :
{
@ -170,10 +178,8 @@ def formation_export_by_formation_id(formation_id: int, export_ids=False):
}
"""
formation = Formation.query.get_or_404(formation_id)
dept = models.Departement.query.filter_by(id=formation.dept_id).first()
app.set_sco_dept(dept.acronym)
app.set_sco_dept(formation.departement.acronym)
try:
# Utilisation de la fonction formation_export
data = sco_formations.formation_export(formation_id, export_ids)
except ValueError:
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"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def moduleimpl(moduleimpl_id: int):
"""
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()
data = modimpl.to_dict()
return jsonify(data)
modimpl = models.ModuleImpl.query.get_or_404(moduleimpl_id)
return jsonify(modimpl.to_dict())
@bp.route(
"/formation/<int:formation_id>/referentiel_competences",
methods=["GET"],
)
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def referentiel_competences(formation_id: int):
"""
Retourne le référentiel de compétences
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:
return jsonify(None)
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
from app import models
from app.api import bp
from app.api.auth import token_auth, token_permission_required
from app.models import Departement, FormSemestre, FormSemestreEtape
from app.api.auth import permission_required_api
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 import sco_groups
from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_utils import ModuleType
import app.scodoc.sco_utils as scu
@bp.route("/formsemestre/<int:formsemestre_id>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def formsemestre(formsemestre_id: int):
@permission_required_api(Permission.ScoView, Permission.APIView)
def formsemestre_infos(formsemestre_id: int):
"""
Information sur le formsemestre indiqué.
@ -51,230 +63,67 @@ def formsemestre(formsemestre_id: int):
}
"""
formsemestre: FormSemestre = models.FormSemestre.query.filter_by(
id=formsemestre_id
).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)
formsemestre: FormSemestre = models.FormSemestre.query.get_or_404(formsemestre_id)
return jsonify(formsemestre.to_dict_api())
@bp.route("/formsemestre/apo/<string:etape_apo>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def formsemestre_apo(etape_apo: str):
@bp.route("/formsemestres/query", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def formsemestres_query():
"""
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
Exemple de résultat :
[
{ ...formsemestre...
}, ...
]
annee_scolaire : année de début de l'année scolaire
dept_acronym : acronyme du département (eg "RT")
dept_id : id du département
"""
formsemestres = FormSemestre.query.filter(
FormSemestreEtape.etape_apo == etape_apo,
FormSemestreEtape.formsemestre_id == FormSemestre.id,
)
etape_apo = request.args.get("etape_apo")
annee_scolaire = request.args.get("annee_scolaire")
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(
[formsemestre.to_dict(convert_parcours=True) for formsemestre in formsemestres]
)
return jsonify([formsemestre.to_dict_api() for formsemestre in formsemestres])
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def bulletins(formsemestre_id: int):
"""
Retourne les bulletins d'un formsemestre donné
formsemestre_id : l'id d'un formesemestre
Exemple de résultat :
[
{
"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
}
}
},
...
]
Exemple de résultat : liste, voir https://scodoc.org/ScoDoc9API/#bulletin
"""
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)
formsemestre = models.FormSemestre.query.get_or_404(formsemestre_id)
app.set_sco_dept(formsemestre.departement.acronym)
data = []
for etu in formsemestre.etuds:
@ -284,50 +133,11 @@ def bulletins(formsemestre_id: int):
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(
"/formsemestre/<int:formsemestre_id>/programme",
methods=["GET"],
)
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def formsemestre_programme(formsemestre_id: int):
"""
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 ... ]
}
"""
formsemestre: FormSemestre = models.FormSemestre.query.filter_by(
id=formsemestre_id
).first_or_404()
formsemestre: FormSemestre = models.FormSemestre.query.get_or_404(formsemestre_id)
ues = formsemestre.query_ues()
m_list = {
ModuleType.RESSOURCE: [],
@ -404,14 +211,179 @@ def formsemestre_programme(formsemestre_id: int):
ModuleType.STANDARD: [],
}
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)
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],
"saes": m_list[ModuleType.SAE],
"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.api import bp
# from app.api.errors import error_response
# from app.api.auth import token_auth, token_permission_required
# from app.api.auth import permission_required_api
# from app.scodoc.sco_prepajury import feuille_preparation_jury
# 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.models import Departement
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
@bp.route("/logos", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def api_get_glob_logos():
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
return error_response(401, message="accès interdit")
@ -56,8 +55,7 @@ def api_get_glob_logos():
@bp.route("/logos/<string:logoname>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def api_get_glob_logo(logoname):
if not g.current_user.has_permission(Permission.ScoSuperAdmin, None):
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"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def api_get_local_logos(departement):
dept_id = Departement.from_acronym(departement).id
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"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
@permission_required_api(Permission.ScoView, Permission.APIView)
def api_get_local_logo(departement, logoname):
# format = requested_format("jpg", ['png', 'jpg']) XXX ?
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.errors import error_response
from app.api.auth import token_auth, token_permission_required
from app.scodoc.sco_groups import get_group_members, setGroups, get_partitions_list
from app.api.auth import permission_required_api
from app.models import FormSemestre, FormSemestreInscription, Identite
from app.models import GroupDescr, Partition
from app.models.groups import group_membership
from app.scodoc import sco_cache
from app.scodoc.sco_permissions import Permission
from app.scodoc import sco_utils as scu
@bp.route("/partitions/<int:formsemestre_id>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def partition(formsemestre_id: int):
@bp.route("/partition/<int:partition_id>", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def partition_info(partition_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
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
# partitions = models.Partition.query.filter_by(id=formsemestre_id)
#
# # Mise en forme des données
# data = [partition.to_dict() for partition in partitions]
data = get_partitions_list(formsemestre_id)
return jsonify(data)
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
return jsonify(
[partition.to_dict(with_groups=True) for partition in formsemestre.partitions]
)
@bp.route("/partitions/groups/<int:group_id>", methods=["GET"])
@bp.route("/partitions/groups/<int:group_id>/etat/<string:etat>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etud_in_group(group_id: int, etat=None):
@bp.route("/group/<int:group_id>/etudiants", methods=["GET"])
@permission_required_api(Permission.ScoView, Permission.APIView)
def etud_in_group(group_id: int):
"""
Retourne la liste des étudiants dans un groupe
group_id : l'id d'un groupe
etat : état de l'inscription
Exemple de résultat :
[
{
"etudid": 10,
"id": 10,
"dept_id": 1,
"nom": "BOUTET",
"prenom": "Marguerite",
"nom_usuel": "",
"civilite": "F",
"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"
},
...
]
[
{
'civilite': 'M',
'id': 123456,
'ine': None,
'nip': '987654321',
'nom': 'MARTIN',
'nom_usuel': null,
'prenom': 'JEAN'}
},
...
]
"""
# Fonction utilisée : app.scodoc.sco_groups.get_group_members()
if etat is None:
data = get_group_members(group_id)
else:
data = get_group_members(group_id, etat)
if len(data) == 0:
return error_response(404, message="group_id inconnu")
return jsonify(data)
group = GroupDescr.query.get_or_404(group_id)
return jsonify([etud.to_dict_short() for etud in group.etuds])
@bp.route(
"/partitions/set_groups/partition/<int:partition_id>/groups/<string:groups_id>/delete/<string:groups_to_delete>"
"/create/<string:groups_to_create>",
methods=["POST"],
)
@token_auth.login_required
@token_permission_required(Permission.APIEtudChangeGroups)
def set_groups(
partition_id: int, groups_lists: str, groups_to_delete: str, groups_to_create: str
):
@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:
g.etuds.remove(etud)
if not ok:
group.etuds.append(etud)
db.session.commit()
return jsonify({"group_id": group_id, "etudid": etudid})
@bp.route("/partition/<int:partition_id>/group/create", methods=["POST"])
@permission_required_api(Permission.ScoEtudChangeGroups, Permission.APIEditGroups)
def group_create(partition_id: int):
"""Création d'un groupe dans une partition
The request content type should be "application/json":
{
"group_name" : nom_du_groupe,
}
"""
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
groups_lists : membres de chaque groupe existant
groups_ti_delete : les groupes à supprimer
groups_to_create : les groupes à créer
group = GroupDescr(group_name=group_name, partition_id=partition_id)
db.session.add(group)
db.session.commit()
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()
try:
# Utilisation de la fonction setGroups
setGroups(partition_id, groups_lists, groups_to_create, groups_to_delete)
return error_response(200, message="Groups set")
except ValueError:
return error_response(404, message="Erreur")
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
data = request.get_json(force=True) # may raise 400 Bad Request
partition_name = data.get("partition_name")
if partition_name is None:
abort(404, "missing partition_name or invalid data format")
if not Partition.check_name(formsemestre, partition_name):
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.api.errors import error_response
from app.models import Identite, Admission
def get_etud_from_etudid_or_nip_or_ine(
etudid=None, nip=None, ine=None
) -> models.Identite:
def get_etud(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
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.
"""
if etudid is None:
if nip is None: # si ine
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()
if etudid is not None:
return Identite.query.get(etudid)
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
moyenne générale d'une UE.
"""
import dataclasses
from dataclasses import dataclass
import numpy as np
import pandas as pd
@ -54,6 +55,10 @@ class EvaluationEtat:
nb_attente: int
is_complete: bool
def to_dict(self):
"convert to dict"
return dataclasses.asdict(self)
class ModuleImplResults:
"""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
) / [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):
"""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

View File

@ -426,9 +426,16 @@ class ResultatsSemestre(ResultatsCache):
# --- TABLEAU 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 }
- titles: { column_id : title }
- columns_ids: (liste des id de colonnes)
@ -591,7 +598,7 @@ class ResultatsSemestre(ResultatsCache):
row,
f"bonus_ue_{ue.id}",
f"Bonus {ue.acronyme}",
val_fmt_html,
val_fmt_html if allow_html else val_fmt,
"col_ue_bonus",
idx,
)

View File

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

View File

@ -1,12 +1,17 @@
# -*- 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.models import SHORT_STR_LEN
from app.models import GROUPNAME_STR_LEN
from app.scodoc import sco_utils as scu
class Partition(db.Model):
@ -41,6 +46,7 @@ class Partition(db.Model):
"GroupDescr",
backref=db.backref("partition", lazy=True),
lazy="dynamic",
cascade="all, delete-orphan",
)
def __init__(self, **kwargs):
@ -56,14 +62,33 @@ class Partition(db.Model):
def __repr__(self):
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:
"""as a dict, with or without groups"""
d = {
"id": self.id,
"formsemestre_id": self.partition_id,
"name": self.partition_name,
"numero": self.numero,
}
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
d.pop("formsemestre", None)
if with_groups:
d["groups"] = [group.to_dict(with_partition=False) for group in self.groups]
return d
@ -107,6 +132,21 @@ class GroupDescr(db.Model):
d["partition"] = self.partition.to_dict(with_groups=False)
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",

View File

@ -79,17 +79,23 @@ class ModuleImpl(db.Model):
self.module.formation.get_module_coefs(self.module.semestre_id),
)
def to_dict(self):
"""as a dict, with the same conversions as in ScoDoc7, including module"""
e = dict(self.__dict__)
e.pop("_sa_instance_state", None)
def to_dict(self, convert_objects=False, with_module=True):
"""as a dict, with the same conversions as in ScoDoc7, including module.
If convert_objects, convert all attributes to native types
(suitable jor json encoding).
"""
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
# ScoDoc7 output_formators: (backward compat)
e["moduleimpl_id"] = self.id
e["ens"] = [
d["moduleimpl_id"] = self.id
d["ens"] = [
{"moduleimpl_id": self.id, "ens_id": e.id} for e in self.enseignants
]
e["module"] = self.module.to_dict()
return e
if with_module:
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

View File

@ -67,19 +67,33 @@ class Module(db.Model):
def __repr__(self):
return f"<Module{ModuleType(self.module_type or ModuleType.STANDARD).name} id={self.id} code={self.code!r}>"
def to_dict(self):
e = dict(self.__dict__)
e.pop("_sa_instance_state", None)
def to_dict(self, convert_objects=False, with_matiere=False, with_ue=False) -> dict:
"""If convert_objects, convert all attributes to native types
(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)
e["module_id"] = self.id
e["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
e["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
e["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
e["code_apogee"] = e["code_apogee"] or "" # pas de None
return e
d["module_id"] = self.id
d["heures_cours"] = 0.0 if self.heures_cours is None else self.heures_cours
d["heures_td"] = 0.0 if self.heures_td is None else self.heures_td
d["heures_tp"] = 0.0 if self.heures_tp is None else self.heures_tp
d["numero"] = 0 if self.numero is None else self.numero
d["coefficient"] = 0.0 if self.coefficient is None else self.coefficient
d["module_type"] = 0 if self.module_type is None else self.module_type
d["code_apogee"] = d["code_apogee"] or "" # pas de None
return d
def is_apc(self):
"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):
"""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())
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):
"""Historique des modifs sur notes (anciennes entrees de notes_notes)"""

View File

@ -59,18 +59,25 @@ class UniteEns(db.Model):
self.semestre_idx} {
'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
(except ECTS: keep None)
If convert_objects, convert all attributes to native types
(suitable jor json encoding).
"""
e = dict(self.__dict__)
e.pop("_sa_instance_state", None)
e.pop("evaluation_ue_poids", None)
# ScoDoc7 output_formators
e["ue_id"] = self.id
e["numero"] = e["numero"] if e["numero"] else 0
e["ects"] = e["ects"]
e["coefficient"] = e["coefficient"] if e["coefficient"] else 0.0
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
def is_locked(self):

View File

@ -131,12 +131,14 @@ class EvaluationCache(ScoDocCache):
@classmethod
def invalidate_sem(cls, formsemestre_id):
"delete evaluations in this formsemestre from cache"
req = """SELECT e.id
FROM notes_formsemestre s, notes_evaluation e, notes_moduleimpl m
WHERE s.id = %(formsemestre_id)s and s.id=m.formsemestre_id and e.moduleimpl_id=m.id;
"""
from app.models.evaluations import Evaluation
from app.models.moduleimpls import ModuleImpl
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)

View File

@ -1,4 +1,3 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
@ -27,15 +26,15 @@
"""Gestion des cursus (jurys suivant la formation)
"""
from sqlalchemy.sql import text
from app import db
from app.but import cursus_but
from app.scodoc import sco_cursus_dut
from app.comp.res_compat import NotesTableCompat
from app.comp import res_sem
from app.models import FormSemestre
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formations
import app.scodoc.notesdb as ndb
# 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
(et qui doivent donc etre sortis du cache si l'on modifie ce
semestre): meme code formation, meme semestre_id, date posterieure"""
cnx = ndb.GetDBConnexion()
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cursor.execute(
"""SELECT sem.id
FROM notes_formsemestre sem, notes_formations F
WHERE sem.formation_id = F.id
and F.formation_code = %(formation_code)s
and sem.semestre_id = %(semestre_id)s
and sem.date_debut >= %(date_debut)s
and sem.id != %(formsemestre_id)s;
""",
formsemestre = FormSemestre.query.get(formsemestre_id)
cursor = db.session.execute(
text(
"""
SELECT sem.id
FROM notes_formsemestre sem, notes_formations F
WHERE sem.formation_id = F.id
and F.formation_code = :formation_code
and sem.semestre_id = :semestre_id
and sem.date_debut >= :date_debut
and sem.id != :formsemestre_id;
"""
),
{
"formation_code": F["formation_code"],
"semestre_id": sem["semestre_id"],
"formation_code": formsemestre.formation.formation_code,
"semestre_id": formsemestre.semestre_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]
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)"""
partitions = ndb.SimpleDictFetch(
"""SELECT p.id AS partition_id, p.*
@ -146,9 +146,9 @@ def get_partitions_list(formsemestre_id, with_default=True):
{"formsemestre_id": formsemestre_id},
)
# 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:
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
@ -179,6 +179,24 @@ def get_formsemestre_groups(formsemestre_id, with_default=False):
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):
"""List of groups in this partition (list of dicts).
Some groups may be empty."""

View File

@ -51,7 +51,7 @@ _SCO_PERMISSIONS = (
# 27 à 39 ... réservé pour "entreprises"
# Api scodoc9
(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 << 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}")
cnx = ndb.GetDBConnexion()
if self.dept_id:
g.scodoc_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.default = {} # { name : default_value }

View File

@ -65,6 +65,8 @@ class DevConfig(Config):
os.environ.get("SCODOC_DATABASE_URI") or "postgresql:///SCODOC_DEV"
)
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):

View File

@ -20,6 +20,7 @@ Travail en cours.
"""
from dotenv import load_dotenv
import json
import os
import requests
import urllib3
@ -35,8 +36,13 @@ load_dotenv(os.path.join(BASEDIR, ".env"))
CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
SCODOC_URL = os.environ.get("SCODOC_URL") or "http://localhost:5000"
API_URL = SCODOC_URL + "/ScoDoc/api"
# Admin:
SCODOC_USER = os.environ["SCODOC_USER"]
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"API URL={API_URL}")
@ -51,25 +57,48 @@ class ScoError(Exception):
def GET(path: str, headers={}, errmsg=None):
"""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:
raise ScoError(errmsg or "erreur !")
raise ScoError(errmsg or f"erreur status={r.status_code} !")
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"""
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:
raise ScoError(errmsg or "erreur !")
return r.text
raise ScoError(errmsg or f"erreur status={r.status_code} !")
return r.json() # decode la reponse JSON
# --- Obtention du jeton (token)
r = requests.post(API_URL + "/tokens", auth=(SCODOC_USER, SCODOC_PASSWORD))
assert r.status_code == 200
token = r.json()["token"]
HEADERS = {"Authorization": f"Bearer {token}"}
def POST_JSON(path: str, data: dict = {}, headers={}, errmsg=None):
"""Post"""
r = requests.post(
API_URL + path,
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)
if r.status_code != 200:
@ -113,8 +142,52 @@ print("\n".join([s["titre_num"] for s in sems]))
# Evaluation
evals = GET("/evaluations/1")
# # --- Recupere la liste de tous les semestres:
# sems = GET(s, "Notes/formsemestre_list?format=json", "Aucun semestre !")
# Partitions d'un BUT
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)
# 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
# Etudiant pour les tests
from tests.api.tools_test_api import (
verify_fields,
ABSENCES_FIELDS,
ABSENCES_GROUP_ETAT_FIELDS,
)
ETUDID = 1
# absences
def test_absences(api_headers):
"""
Route: /absences/etudid/<int:etudid>
Test 'absences'
Route :
- /absences/etudid/<int:etudid>
"""
r = requests.get(
f"{API_URL}/absences/etudid/{ETUDID}",
@ -35,29 +44,165 @@ def test_absences(api_headers):
verify=CHECK_CERTIFICATE,
)
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
def test_absences_justify(api_headers):
"""
Route: /absences/etudid/<etudid:int>/just
Test 'absences_just'
Route :
- /absences/etudid/<int:etudid>/just
"""
r = requests.get(
API_URL + f"/absences/etudid/{ETUDID}/just",
f"{API_URL}/absences/etudid/{ETUDID}/just",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
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
# 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(
# 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,
# verify=CHECK_CERTIFICATE,
# )

View File

@ -19,29 +19,20 @@ Utilisation :
import requests
from tests.api.setup_test_api import (
API_URL,
CHECK_CERTIFICATE,
DEPT_ACRONYM,
api_headers,
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
from tests.api.tools_test_api import (
verify_fields,
DEPARTEMENT_FIELDS,
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):
""" "
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(
API_URL + "/departements_ids",
headers=api_headers,
@ -53,8 +44,17 @@ def test_departements(api_headers):
assert len(departements_ids) > 0
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]
# --- Infos sur un département, accès par id
# --- departement
# Infos sur un département, accès par id
r = requests.get(
f"{API_URL}/departement/{dept_id}",
headers=api_headers,
@ -62,8 +62,7 @@ def test_departements(api_headers):
)
assert r.status_code == 200
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(
f"{API_URL}/departement/{dept_a['acronym']}",
headers=api_headers,
@ -71,65 +70,131 @@ def test_departements(api_headers):
)
assert r.status_code == 200
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(
f"{API_URL}/departement/{dept_a['acronym']}/formsemestres_ids",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
dept_ids = r.json()
assert isinstance(dept_ids, list)
assert all(isinstance(x, int) for x in dept_ids)
assert len(dept_ids) > 0
assert dept_id in dept_ids
dept_ids_a = r.json()
r = requests.get(
f"{API_URL}/departement/{dept_a['id']}/formsemestres_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):
fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"}
r = requests.get(
f"{API_URL}/departement/{DEPT_ACRONYM}/etudiants",
API_URL + "/departement/TAPI/etudiants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud = r.json()[0]
assert verify_fields(etud, fields) is True
etud_a = r.json()[0]
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
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
r = requests.get(
f"{API_URL}/departement/{dept_id}",
@ -139,6 +204,7 @@ def test_semestres_courant(api_headers):
assert r.status_code == 200
dept = r.json()
assert dept["id"] == dept_id
# Accès via acronyme
r = requests.get(
f"{API_URL}/departement/{dept['acronym']}/formsemestres_courants",
@ -147,10 +213,6 @@ def test_semestres_courant(api_headers):
)
assert r.status_code == 200
result_a = r.json()
assert isinstance(result_a, list) # liste de formsemestres
assert len(result_a) > 0
sem = result_a[0]
assert verify_fields(sem, fields) is True
# accès via dept_id
r = requests.get(
@ -161,3 +223,7 @@ def test_semestres_courant(api_headers):
assert r.status_code == 200
result_b = r.json()
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 -*-
"""Test API: accès aux étudiants
"""Test Logos
Utilisation :
créer les variables d'environnement: (indiquer les valeurs
@ -19,24 +19,54 @@ Utilisation :
import requests
from tests.api.setup_test_api import (
API_URL,
CHECK_CERTIFICATE,
DEPT_ACRONYM,
api_headers,
from tests.api.setup_test_api import API_URL, CHECK_CERTIFICATE, api_headers
from tests.api.tools_test_api import (
verify_fields,
verify_occurences_ids_etus,
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
ETUDID = 1
NIP = "1"
INE = "1"
def test_etudiants_courant(api_headers):
"""
Route: /etudiants/courant
"""
fields = {"id", "nip", "ine", "nom", "nom_usuel", "prenom", "civilite"}
fields = {"id", "nip", "nom", "prenom", "civilite"}
r = requests.get(
API_URL + "/etudiants/courant",
API_URL + "/etudiants/courants",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
@ -46,10 +76,18 @@ def test_etudiants_courant(api_headers):
etud = etudiants[-1]
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 ################
r = requests.get(
API_URL + "/etudiants/courant/long",
API_URL + "/etudiants/courants/long",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
@ -63,77 +101,119 @@ def test_etudiants_courant(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 #########
r = requests.get(
API_URL + "/etudiant/etudid/1",
API_URL + "/etudiant/etudid/" + str(ETUDID),
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud = r.json()
assert verify_fields(etud, ETUD_FIELDS) is True
code_nip = r.json()["code_nip"]
code_ine = r.json()["code_ine"]
######### Test code nip #########
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()
etud_nip = 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",
API_URL + "/etudiant/ine/" + code_ine,
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud_ine = r.json()
assert len(etud) == 26
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()
assert len(etud) == 25
fields_ok = verify_fields(etud, ETUD_FIELDS)
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
# 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,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud_nip = r.json()
# On doit avoir obtenue le même étudiant
assert etud_nip == etud
r = requests.get(
f"{API_URL }/etudiant/ine/{ine}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
etud_ine = r.json()
# On doit avoir obtenue le même étudiant
assert etud_ine == etud
######### 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):
@ -144,20 +224,52 @@ def test_etudiant_formsemestres(api_headers):
######### Test etudid #########
r = requests.get(
API_URL + "/etudiant/etudid/1/formsemestres",
API_URL + "/etudiant/etudid/" + str(ETUDID) + "/formsemestres",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
formsemestres = r.json()
assert len(formsemestres) == 1
list_formsemestres = r.json()
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
######### Test code nip #########
r = requests.get(
API_URL + "/etudiant/nip/1/formsemestres",
API_URL + "/etudiant/nip/" + str(NIP) + "/formsemestres",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
@ -170,7 +282,7 @@ def test_etudiant_formsemestres(api_headers):
######### Test code ine #########
r = requests.get(
API_URL + "/etudiant/ine/INE1/formsemestres",
API_URL + "/etudiant/ine/" + str(INE) + "/formsemestres",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
@ -186,10 +298,391 @@ def test_etudiant_bulletin_semestre(api_headers):
"""
Route: /etudiant/etudid/<etudid>/formsemestre/<formsemestre_id>/bulletin
"""
##################### LONG ########################
######### Test etudid #########
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,
verify=CHECK_CERTIFICATE,
)
@ -200,7 +693,7 @@ def test_etudiant_bulletin_semestre(api_headers):
######### Test code nip #########
r = requests.get(
API_URL + "/etudiant/nip/1/formsemestre/1/bulletin",
API_URL + "/etudiant/nip/" + str(NIP) + "/formsemestre/1/bulletin/short",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
@ -210,7 +703,7 @@ def test_etudiant_bulletin_semestre(api_headers):
######### Test code ine #########
r = requests.get(
API_URL + "/etudiant/ine/INE1/formsemestre/1/bulletin",
API_URL + "/etudiant/ine/" + str(INE) + "/formsemestre/1/bulletin/short",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
@ -218,6 +711,33 @@ def test_etudiant_bulletin_semestre(api_headers):
bul = r.json()
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
r = requests.get(
API_URL + "/etudiant/ine/189919919119191/formsemestre/1/bulletin",
@ -260,7 +780,7 @@ def test_etudiant_groups(api_headers):
######### Test code nip #########
r = requests.get(
API_URL + "/etudiant/nip/1/formsemestre/1/groups",
API_URL + "/etudiant/nip/" + str(NIP) + "/formsemestre/1/groups",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
@ -273,7 +793,7 @@ def test_etudiant_groups(api_headers):
######### Test code ine #########
r = requests.get(
API_URL + "/etudiant/ine/INE1/formsemestre/1/groups",
API_URL + "/etudiant/ine/" + str(INE) + "/formsemestre/1/groups",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)

View File

@ -20,30 +20,79 @@ Utilisation :
import requests
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):
"""
Route: /evaluation/<int:moduleimpl_id>
Test 'evaluations'
Route :
- /evaluations/<int:moduleimpl_id>
"""
moduleimpl_id = 1
r = requests.get(
API_URL + "/evaluations/1",
f"{API_URL}/evaluations/{moduleimpl_id}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
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):
# """
# Route: /evaluation/eval_notes/<int:evaluation_id>
# """
# r = requests.get(
# API_URL + "/evaluation/eval_notes/1",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
# # TODO
def test_evaluation_notes(api_headers): # XXX TODO changer la boucle pour parcourir le dict sans les indices
"""
Test 'evaluation_notes'
Route :
- /evaluation/eval_notes/<int:evaluation_id>
"""
eval_id = 1
r = requests.get(
f"{API_URL}/evaluation/eval_notes/{eval_id}",
headers=api_headers,
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
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
@ -45,15 +54,52 @@ def test_formations_by_id(api_headers):
"""
Route: /formation/<int:formation_id>
"""
id_formation = 1
r = requests.get(
API_URL + "/formation/1",
f"{API_URL}/formation/{id_formation}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
formation = r.json()
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):
@ -67,33 +113,150 @@ def test_formation_export(api_headers):
)
assert r.status_code == 200
export_formation = r.json()
assert verify_fields(export_formation, FORMATION_FIELDS) is True
# TODO tester le contenu de certains champs
assert verify_fields(export_formation, FORMATION_EXPORT_FIELDS) is True
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
# def test_formsemestre_apo(api_headers):
# r = requests.get(
# API_URL + "/formation/apo/<string:etape_apo>",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
for ue in ues:
assert verify_fields(ue, FORMATION_EXPORT_UE_FIELDS) is True
assert isinstance(ue["acronyme"], str)
assert isinstance(ue["numero"], int)
assert isinstance(ue["titre"], str)
assert isinstance(ue["type"], int)
assert isinstance(ue["ue_code"], str)
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):
"""
Route: /formation/moduleimpl/<int:moduleimpl_id>
"""
moduleimpl_id = 1
r = requests.get(
API_URL + "/formation/moduleimpl/1",
f"{API_URL}/formation/moduleimpl/{moduleimpl_id}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
moduleimpl = r.json()
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):
@ -101,9 +264,32 @@ def test_referentiel_competences(api_headers):
Route: "/formation/<int:formation_id>/referentiel_competences",
"""
r = requests.get(
API_URL + "/formation/1/referentiel_competences",
f"{API_URL}/formation/1/referentiel_competences",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
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 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
ETUDID = 1
@ -34,62 +62,421 @@ def test_formsemestre(api_headers):
"""
Route: /formsemestre/<id>
"""
formsemestre_id = 1
r = requests.get(
API_URL + "/formsemestre/1",
f"{API_URL}/formsemestre/{formsemestre_id}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
formsemestre = r.json()
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(
f"{API_URL}/etudiant/etudid/1/formsemestre/{formsemestre_id}/bulletin",
f"{API_URL}/formsemestre/apo/{etape_apo}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
bull_a = r.json()
list_formsemestre = r.json()
assert isinstance(list_formsemestre, list)
r = requests.get(
f"{API_URL}/etudiant/nip/{NIP}/formsemestre/{formsemestre_id}/bulletin",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
bull_b = r.json()
for formsemestre in list_formsemestre:
assert isinstance(formsemestre, dict)
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)
r = requests.get(
f"{API_URL}/etudiant/ine/{INE}/formsemestre/{formsemestre_id}/bulletin",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
bull_c = r.json()
# elimine les dates de publication pour comparer les autres champs
del bull_a["date"]
del bull_b["date"]
del bull_c["date"]
assert bull_a == bull_b == bull_c
### ERROR ###
# etape_apo_inexistante = "aoefiaozidaoẑidjnoaiznjd"
# r_error = requests.get(
# f"{API_URL}/formsemestre/apo/{etape_apo_inexistante}",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r_error.status_code == 404
def test_bulletins(api_headers):
"""
Route:
Route: /formsemestre/<int:formsemestre_id>/bulletins
"""
formsemestre_id = 1
r = requests.get(
API_URL + "/formsemestre/1/bulletins",
f"{API_URL}/formsemestre/{formsemestre_id}/bulletins",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
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
# def test_jury():
@ -101,6 +488,141 @@ def test_bulletins(api_headers):
# 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):
"""
Route: /formsemestre/1/programme
@ -131,3 +653,122 @@ def test_formsemestre_programme(api_headers):
assert verify_fields(modules[0], MODIMPL_FIELDS)
assert verify_fields(ressource, 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 -*-
"""Test API Jurys XXX TODO A ECRIRE
"""Test Logos
Utilisation :
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):
"""
Route:
Test 'jury_preparation'
Route :
- /jury/formsemestre/<int:formsemestre_id>/preparation_jury
"""
r = requests.get(
SCODOC_URL
API_URL
+ "/ScoDoc/api/jury/formsemestre/<int:formsemestre_id>/preparation_jury",
headers=api_headers,
verify=CHECK_CERTIFICATE,
@ -37,7 +40,10 @@ def test_jury_preparation(api_headers):
def test_jury_decisions(api_headers):
"""
Route:
Test 'jury_decisions'
Route :
- /jury/formsemestre/<int:formsemestre_id>/decisions_jury
"""
r = requests.get(
API_URL + "/jury/formsemestre/<int:formsemestre_id>/decisions_jury",
@ -45,62 +51,3 @@ def test_jury_decisions(api_headers):
verify=CHECK_CERTIFICATE,
)
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
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):
"""
Route:
"""
fields = [
"partition_id",
"id",
"formsemestre_id",
"partition_name",
"numero",
"bul_show_rank",
"show_in_lists",
]
Test 'partition'
Route :
- /partitions/<int:formsemestre_id>
"""
partition_id = 1
r = requests.get(
API_URL + "/partitions/1",
f"{API_URL}/partitions/{partition_id}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
assert r.status_code == 200
partitions = r.json()
assert len(partitions) == 1
assert isinstance(partitions, list)
partition = partitions[0]
fields_ok = verify_fields(partition, fields)
assert fields_ok is True
assert isinstance(partition, dict)
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):
"""
Route:
"""
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",
]
Test 'etud_in_group'
Routes :
- /partition/group/<int:group_id>
- /partition/group/<int:group_id>/etat/<string:etat>
"""
group_id = 1
r = requests.get(
API_URL + "/partitions/groups/1",
f"{API_URL}/partition/group/{group_id}",
headers=api_headers,
verify=CHECK_CERTIFICATE,
)
etu = r.json()[0]
fields_ok = verify_fields(etu, fields)
assert r.status_code == 200
assert len(r.json()) == 16
assert fields_ok is True
# r = requests.get(
# API_URL + "/partitions/groups/1/etat/<string:etat>",
# headers=api_headers,
# verify=CHECK_CERTIFICATE,
# )
# assert r.status_code == 200
assert isinstance(r.json(), list)
for etu in r.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)
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
# 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(
# 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
app = create_app(RunningConfig)
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 = [
r
for r in app.url_map.iter_rules()

View File

@ -1,5 +1,6 @@
"""Utilitaires pour les tests de l'API
"""
import json
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)
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 = {
"boursier",
"civilite",
@ -43,12 +78,12 @@ ETUD_FIELDS = {
}
FORMATION_FIELDS = {
"id",
"dept_id",
"acronyme",
"titre_officiel",
"formation_code",
"code_specialite",
"dept_id",
"id",
"titre",
"version",
"type_parcours",
@ -56,6 +91,92 @@ FORMATION_FIELDS = {
"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 = {
"block_moyennes",
"bul_bgcolor",
@ -131,3 +252,468 @@ UE_FIELDS = {
"color",
"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
2) En tant qu'utilisateur scodoc, lancer:
tools/create_database.sh SCODOC_TEST_API
tools/create_database.sh
flask db upgrade
flask sco-db-init --erase
flask init-test-database
@ -24,12 +24,32 @@
"""
import datetime
import random
import time
import sys
from app.auth.models import Role, User
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.models.but_refcomp import (
ApcParcours,
ApcAnneeParcours,
ApcSituationPro,
ApcComposanteEssentielle,
ApcNiveau,
ApcAppCritique,
)
from app.scodoc import (
sco_cache,
sco_evaluation_db,
@ -38,6 +58,7 @@ from app.scodoc import (
sco_groups,
)
from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_saisie_notes import notes_add
from tools.fakeportal.gen_nomprenoms import nomprenom
random.seed(12345678) # tests reproductibles
@ -160,13 +181,31 @@ def create_formsemestre(
def inscrit_etudiants(etuds: list, formsemestre: FormSemestre):
"""Inscrit les etudiants aux semestres et à tous ses modules"""
for etud in etuds:
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
formsemestre.id,
etud.id,
group_ids=[],
etat="I",
method="init db test",
)
aleatoire = random.randint(0, 10)
if aleatoire <= 3:
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
formsemestre.id,
etud.id,
group_ids=[],
etat="I",
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):
@ -188,6 +227,286 @@ def create_evaluations(formsemestre: FormSemestre):
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():
"""Appelé par la commande `flask init-test-database`
@ -201,6 +520,10 @@ def init_test_database():
formsemestre = create_formsemestre(formation, user_lecteur)
create_evaluations(formsemestre)
inscrit_etudiants(etuds, formsemestre)
saisie_notes_evaluations(formsemestre, user_lecteur)
create_ref_comp(formation)
add_absences(formsemestre)
create_etape_apo(formsemestre)
# à compléter
# - groupes
# - absences