Compare commits

...

8 Commits

19 changed files with 1786 additions and 1628 deletions

View File

@ -23,4 +23,14 @@ def requested_format(default_format="json", allowed_formats=None):
from app.api import tokens from app.api import tokens
from app.api import sco_api from app.api import sco_api
from app.api import test_api
from app.api import departements
from app.api import etudiants
from app.api import formations
from app.api import formsemestres
from app.api import partitions
from app.api import evaluations
from app.api import jury
from app.api import absences
from app.api import logos from app.api import logos

307
app/api/absences.py Normal file
View File

@ -0,0 +1,307 @@
#################################################### Absences #########################################################
from datetime import datetime
from flask import jsonify
from app import models
from app.api import bp
from app.api.auth import token_auth
from app.api.errors import error_response
from app.scodoc.sco_abs import add_absence, add_justif, annule_absence, annule_justif, list_abs_date
from app.scodoc.sco_groups import get_group_members
@bp.route("/absences/etudid/<int:etudid>", methods=["GET"])
@bp.route("/absences/nip/<int:nip>", methods=["GET"])
@bp.route("/absences/ine/<int:ine>", methods=["GET"])
def absences(etudid: int = None, nip: int = None, ine: int = None):
"""
Retourne la liste des absences d'un étudiant donné
etudid : l'etudid d'un étudiant
nip: le code nip d'un étudiant
ine : le code ine d'un étudiant
"""
abs = None
if etudid is not None: # Si route etudid
# Récupération des absences de l'étudiant
abs = models.Absence.query.filter_by(etudid=etudid).all()
else:
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
# Récupération des absences de l'étudiant
abs = models.Absence.query.filter_by(etudid=etu.etudid).all()
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).first()
# Récupération des absences de l'étudiant
abs = models.Absence.query.filter_by(etudid=etu.etudid).all()
if abs is not None: # Si des absences ont bien été trouvé
# Mise en forme des données
data = [d.to_dict() for d in abs]
return jsonify(data)
return error_response(501, message="Not implemented")
@bp.route("/absences/etudid/<int:etudid>/abs_just_only", methods=["GET"])
@bp.route("/absences/nip/<int:nip>/abs_just_only", methods=["GET"])
@bp.route("/absences/ine/<int:ine>/abs_just_only", methods=["GET"])
def absences_justify(etudid: int = None, nip: int = None, ine: int = None):
"""
Retourne la liste des absences justifiées d'un étudiant donné
etudid : l'etudid d'un étudiant
nip: le code nip d'un étudiant
ine : le code ine d'un étudiant
"""
abs = None
if etudid is not None: # Si route etudid
# Récupération des absences justifiées de l'étudiant
abs = models.Absence.query.filter_by(etudid=etudid, estjust=True).all()
else:
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
# Récupération des absences justifiées de l'étudiant
abs = models.Absence.query.filter_by(etudid=etu.etudid, estjust=True).all()
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).first()
# Récupération des absences justifiées de l'étudiant
abs = models.Absence.query.filter_by(etudid=etu.etudid, estjust=True).all()
if abs is not None: # Si des absences ont bien été trouvé
# Mise en forme des données
data = [d.to_dict() for d in abs]
return jsonify(data)
return error_response(501, message="Not implemented")
@bp.route("/absences/abs_signale?etudid=<int:etudid>&date=<string:date>&matin=<string:matin>&justif=<string:justif>"
"&description=<string:description>", methods=["POST"])
@bp.route("/absences/abs_signale?nip=<int:nip>&date=<string:date>&matin=<string:matin>&justif=<string:justif>"
"&description=<string:description>", methods=["POST"])
@bp.route("/absences/abs_signale?ine=<int:ine>&date=<string:date>&matin=<string:matin>&justif=<string:justif>"
"&description=<string:description>", methods=["POST"])
@bp.route("/absences/abs_signale?ine=<int:ine>&date=<string:date>&matin=<string:matin>&justif=<string:justif>"
"&description=<string:description>&moduleimpl_id=<int:moduleimpl_id>", methods=["POST"])
@token_auth.login_required
def abs_signale(date: datetime, matin: bool, justif: bool, etudid: int = None, nip: int = None, ine: int = None,
description: str = None, moduleimpl_id: int = None):
"""
Permet d'ajouter une absence en base
date : la date de l'absence
matin : True ou False
justif : True ou False
etudid : l'etudid d'un étudiant
nip: le code nip d'un étudiant
ine : le code ine d'un étudiant
description : description possible à ajouter sur l'absence
moduleimpl_id : l'id d'un moduleimpl
"""
# Fonctions utilisées : app.scodoc.sco_abs.add_absence() et app.scodoc.sco_abs.add_justif()
if description is not None: # Si la description a été renseignée
if moduleimpl_id is not None: # Si le moduleimpl a été renseigné
if etudid is not None: # Si route etudid
try:
# Utilisation de la fonction add_absence
add_absence(etudid, date, matin, justif, description, moduleimpl_id)
# Utilisation de la fonction add_justif
add_justif(etudid, date, matin, description)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
try:
# Utilisation de la fonction add_absence
add_absence(etu.etudid, date, matin, justif, description, moduleimpl_id)
# Utilisation de la fonction add_justif
add_justif(etu.etudid, date, matin, description)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).first()
try:
# Utilisation de la fonction add_absence
add_absence(etu.etudid, date, matin, justif, description, moduleimpl_id)
# Utilisation de la fonction add_justif
add_justif(etu.etudid, date, matin, description)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return error_response(409, message="La requête ne peut être traitée en létat actuel")
else: # Si le moduleimpl n'a pas été renseigné
if etudid is not None: # Si route etudid
try:
# Utilisation de la fonction add_absence
add_absence(etudid, date, matin, justif, description)
# Utilisation de la fonction add_justif
add_justif(etudid, date, matin, description)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
try:
# Utilisation de la fonction add_absence
add_absence(etu.etudid, date, matin, justif, description)
# Utilisation de la fonction add_justif
add_justif(etu.etudid, date, matin, description)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).first()
try:
# Utilisation de la fonction add_absence
add_absence(etu.etudid, date, matin, justif, description)
# Utilisation de la fonction add_justif
add_justif(etu.etudid, date, matin, description)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return error_response(409, message="La requête ne peut être traitée en létat actuel")
else:
if etudid is not None: # Si route etudid
try:
# Utilisation de la fonction add_absence
add_absence(etudid, date, matin, justif)
# Utilisation de la fonction add_justif
add_justif(etudid, date, matin)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
try:
# Utilisation de la fonction add_absence
add_absence(etu.etudid, date, matin, justif)
# Utilisation de la fonction add_justif
add_justif(etu.etudid, date, matin)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).first()
try:
# Utilisation de la fonction add_absence
add_absence(etu.etudid, date, matin, justif)
# Utilisation de la fonction add_justif
add_justif(etu.etudid, date, matin)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return error_response(200, message="OK")
@bp.route("/absences/abs_annule?etudid=<int:etudid>&jour=<string:jour>&matin=<string:matin>", methods=["POST"])
@bp.route("/absences/abs_annule?nip=<int:nip>&jour=<string:jour>&matin=<string:matin>", methods=["POST"])
@bp.route("/absences/abs_annule?ine=<int:ine>&jour=<string:jour>&matin=<string:matin>", methods=["POST"])
@token_auth.login_required
def abs_annule(jour: datetime, matin: str, etudid: int = None, nip: int = None, ine: int = None):
"""
Retourne un html
jour : la date de l'absence a annulé
matin : True ou False
etudid : l'etudid d'un étudiant
nip: le code nip d'un étudiant
ine : le code ine d'un étudiant
"""
# Fonction utilisée : app.scodoc.sco_abs.annule_absence()
if etudid is None:
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
# Récupération de l'etudid de l'étudiant
etudid = etu.etudid
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).first()
# Récupération de l'etudid de l'étudiant
etudid = etu.etudid
try:
# Utilisation de la fonction annule_absence
annule_absence(etudid, jour, matin)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return error_response(200, message="OK")
@bp.route("/absences/abs_annule_justif?etudid=<int:etudid>&jour=<string:jour>&matin=<string:matin>", methods=["POST"])
@bp.route("/absences/abs_annule_justif?nip=<int:nip>&jour=<string:jour>&matin=<string:matin>", methods=["POST"])
@bp.route("/absences/abs_annule_justif?ine=<int:ine>&jour=<string:jour>&matin=<string:matin>", methods=["POST"])
@token_auth.login_required
def abs_annule_justif(jour: datetime, matin: str, etudid: int = None, nip: int = None, ine: int = None):
"""
Retourne un html
jour : la date de l'absence a annulé
matin : True ou False
etudid : l'etudid d'un étudiant
nip: le code nip d'un étudiant
ine : le code ine d'un étudiant
"""
# Fonction utilisée : app.scodoc.sco_abs.annule_justif()
if etudid is None:
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
# Récupération de l'etudid de l'étudiant
etudid = etu.etudid
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).first()
# Récupération de l'etudid de l'étudiant
etudid = etu.etudid
try:
# Utilisation de la fonction annule_justif
annule_justif(etudid, jour, matin)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return error_response(200, message="OK")
@bp.route("/absences/abs_group_etat/?group_id=<int:group_id>&date_debut=date_debut&date_fin=date_fin", methods=["GET"])
def abs_groupe_etat(group_id: int, date_debut, date_fin, with_boursier=True, format="html"):
"""
Retoune la liste des absences d'un ou plusieurs groupes entre deux dates
"""
# Fonction utilisée : app.scodoc.sco_groups.get_group_members() et app.scodoc.sco_abs.list_abs_date()
try:
# Utilisation de la fonction get_group_members
members = get_group_members(group_id)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
data = []
# Filtre entre les deux dates renseignées
for member in members:
abs = list_abs_date(member.id, date_debut, date_fin)
data.append(abs)
# return jsonify(data) # XXX TODO faire en sorte de pouvoir renvoyer sa (ex to_dict() dans absences)
return error_response(501, message="Not implemented")

191
app/api/departements.py Normal file
View File

@ -0,0 +1,191 @@
############################################### Departements ##########################################################
from flask import jsonify
from app import models
from app.api import bp
from app.api.auth import token_auth
from app.api.errors import error_response
from app.models import ApcReferentielCompetences
from app.scodoc.sco_prepajury import feuille_preparation_jury
from app.scodoc.sco_pvjury import formsemestre_pvjury
from app.scodoc.sco_recapcomplet import formsemestre_recapcomplet
from app.scodoc.sco_saisie_notes import notes_add
@bp.route("/departements", methods=["GET"])
#@token_auth.login_required # Commenté le temps des tests
def departements():
"""
Retourne la liste des ids de départements visibles
Exemple de résultat : [2, 5, 8, 1, 4, 18]
"""
# Récupération de tous les départements
depts = models.Departement.query.filter_by(visible=True).all()
# Mise en place de la liste avec tous les ids de départements
depts_ids = [d.id for d in depts]
return jsonify(depts_ids)
@bp.route("/departements/<string:dept>/etudiants/liste/<int:formsemestre_id>", methods=["GET"])
@token_auth.login_required
def liste_etudiants(dept: str, formsemestre_id=None): # XXX TODO A REVOIR
"""
Retourne la liste des étudiants d'un département
dept: l'acronym d'un département
formsemestre_id: l'id d'un formesemestre
Exemple de résultat :
{
"civilite": "X",
"code_ine": null,
"code_nip": null,
"date_naissance": null,
"email": null,
"emailperso": null,
"etudid": 18,
"nom": "MOREL",
"prenom": "JACQUES"
},
{
"civilite": "X",
"code_ine": null,
"code_nip": null,
"date_naissance": null,
"email": null,
"emailperso": null,
"etudid": 19,
"nom": "FOURNIER",
"prenom": "ANNE"
},
...
"""
# Si le formsemestre_id a été renseigné
if formsemestre_id is not None:
# Récupération du/des depts
list_depts = models.Departement.query.filter(
models.Departement.acronym == dept,
models.FormSemestre.id == formsemestre_id,
)
list_etuds = []
for dept in list_depts:
# Récupération des étudiants d'un département
x = models.Identite.query.filter(models.Identite.dept_id == dept.id).all()
print(x)
for y in x:
# Ajout des étudiants dans la liste global
list_etuds.append(y)
else: # Si le formsemestre_id n'a pas été renseigné
list_depts = models.Departement.query.filter(
models.Departement.acronym == dept,
models.FormSemestre.semestre_id == models.Departement.formsemestres,
)
list_etuds = []
for dept in list_depts:
x = models.Identite.query.filter(models.Identite.dept_id == dept.id).all()
for y in x:
list_etuds.append(y)
# Mise en forme des données
data = [d.to_dict_bul() for d in list_etuds]
return jsonify(data)
# return error_response(501, message="Not implemented")
@bp.route("/departements/<string:dept>/semestres_courants", methods=["GET"])
# @token_auth.login_required # Commenté le temps des tests
def liste_semestres_courant(dept: str):
"""
Liste des semestres actifs d'un départements donné
dept: l'acronym d'un département
Exemple de résultat :
[
{
"titre": "master machine info",
"gestion_semestrielle": false,
"scodoc7_id": null,
"date_debut": "01/09/2021",
"bul_bgcolor": null,
"date_fin": "15/12/2022",
"resp_can_edit": false,
"dept_id": 1,
"etat": true,
"resp_can_change_ens": 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,
"block_moyennes": false,
"formsemestre_id": 1,
"titre_num": "master machine info semestre 1",
"date_debut_iso": "2021-09-01",
"date_fin_iso": "2022-12-15",
"responsables": [
3,
2
]
},
...
]
"""
# Récupération des départements comportant l'acronym mit en paramètre
depts = models.Departement.query.filter_by(acronym=dept).all()
# Récupération de l'id
id_dept = depts[0].id
# Récupération des semestres suivant id_dept
semestres = models.FormSemestre.query.filter_by(dept_id=id_dept, etat=True).all()
# Mise en forme des données
data = [d.to_dict() for d in semestres]
return jsonify(data)
@bp.route(
"/departements/<string:dept>/formations/<int:formation_id>/referentiel_competences", methods=["GET"])
def referenciel_competences(dept: str, formation_id: int):
"""
Retourne le référentiel de compétences
dept : l'acronym d'un département
formation_id : l'id d'une formation
"""
# depts = models.Departement.query.filter_by(acronym=dept).all()
#
# id_dept = depts[0].id
#
# formations = models.Formation.query.filter_by(id=formation_id, dept_id=id_dept).all()
#
# ref_comp = formations[0].referentiel_competence_id
#
# if ref_comp is None:
# return error_response(204, message="Pas de référenciel de compétences pour cette formation")
# else:
# return jsonify(ref_comp)
ref = ApcReferentielCompetences.query.get_or_404(formation_id)
return jsonify(ref.to_dict())
@bp.route("/departements/<string:dept>/formsemestre<string:formsemestre_id>/programme", methods=["GET"])
def semestre_index(dept: str, formsemestre_id: int):
"""
Retourne la liste des Ues, ressources et SAE d'un semestre
"""
return error_response(501, message="not implemented")

322
app/api/etudiants.py Normal file
View File

@ -0,0 +1,322 @@
#################################################### Etudiants ########################################################
from flask import jsonify
from app import models
from app.api import bp
from app.api.errors import error_response
from app.scodoc.sco_bulletins_json import make_json_formsemestre_bulletinetud
from app.scodoc.sco_groups import get_etud_groups
@bp.route("/etudiants", methods=["GET"])
def etudiants():
"""
Retourne la liste de tous les étudiants
Exemple de résultat :
{
"civilite": "X",
"code_ine": null,
"code_nip": null,
"date_naissance": null,
"email": null,
"emailperso": null,
"etudid": 18,
"nom": "MOREL",
"prenom": "JACQUES"
},
{
"civilite": "X",
"code_ine": null,
"code_nip": null,
"date_naissance": null,
"email": null,
"emailperso": null,
"etudid": 19,
"nom": "FOURNIER",
"prenom": "ANNE"
},
...
"""
# Récupération de tous les étudiants
etu = models.Identite.query.all()
# Mise en forme des données
data = [d.to_dict_bul(include_urls=False) for d in etu]
return jsonify(data)
# return error_response(501, message="Not implemented")
@bp.route("/etudiants/courant", methods=["GET"])
def etudiants_courant():
"""
Retourne la liste des étudiants courant
Exemple de résultat :
{
"civilite": "X",
"code_ine": null,
"code_nip": null,
"date_naissance": null,
"email": null,
"emailperso": null,
"etudid": 18,
"nom": "MOREL",
"prenom": "JACQUES"
},
{
"civilite": "X",
"code_ine": null,
"code_nip": null,
"date_naissance": null,
"email": null,
"emailperso": null,
"etudid": 19,
"nom": "FOURNIER",
"prenom": "ANNE"
},
...
"""
# Récupération de tous les étudiants
etus = models.Identite.query.all()
data = []
# Récupère uniquement les étudiants courant
for etu in etus:
if etu.inscription_courante() is not None:
data.append(etu.to_dict_bul(include_urls=False))
return jsonify(data)
# return error_response(501, message="Not implemented")
@bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiant/nip/<int:nip>", methods=["GET"])
@bp.route("/etudiant/ine/<int:ine>", methods=["GET"])
def etudiant(etudid: int = None, nip: int = None, ine: int = None):
"""
Retourne les informations de l'étudiant correspondant à l'id passé en paramètres.
etudid : l'etudid d'un étudiant
nip : le code nip d'un étudiant
ine : le code ine d'un étudiant
Exemple de résultat :
{
"civilite": "X",
"code_ine": null,
"code_nip": null,
"date_naissance": null,
"email": null,
"emailperso": null,
"etudid": 18,
"nom": "MOREL",
"prenom": "JACQUES"
}
"""
etu = []
if etudid is not None: # Si route etudid
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(id=etudid).first()
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).first()
# Mise en forme des données
data = etu.to_dict_bul(include_urls=False)
return jsonify(data)
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
@bp.route("/etudiant/nip/<int:nip>/formsemestres")
@bp.route("/etudiant/ine/<int:ine>/formsemestres")
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
"""
Retourne la liste des semestres qu'un étudiant a suivis
etudid : l'etudid d'un étudiant
nip : le code nip d'un étudiant
ine : le code ine d'un étudiant
Exemple de résultat :
[
{
"titre": "master machine info",
"gestion_semestrielle": false,
"scodoc7_id": null,
"date_debut": "01/09/2021",
"bul_bgcolor": null,
"date_fin": "15/12/2022",
"resp_can_edit": false,
"dept_id": 1,
"etat": true,
"resp_can_change_ens": 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,
"block_moyennes": false,
"formsemestre_id": 1,
"titre_num": "master machine info semestre 1",
"date_debut_iso": "2021-09-01",
"date_fin_iso": "2022-12-15",
"responsables": [
3,
2
]
},
...
]
"""
# Récupération de toutes les inscriptions
inscriptions = models.FormSemestreInscription.query.all()
sems = []
# Filtre les inscriptions contenant l'étudiant
for sem in inscriptions:
if etudid is not None: # Si route etudid
if sem.etudid == etudid:
sems.append(sem)
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
if sem.etudid == etu.etudid:
sems.append(sem)
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).firt()
if sem.etudid == etu.etudid:
sems.append(sem)
# Mise en forme des données
data_inscriptions = [d.to_dict() for d in sems]
formsemestres = []
# Filtre les formsemestre contenant les inscriptions de l'étudiant
for sem in data_inscriptions:
res = models.FormSemestre.query.filter_by(id=sem['formsemestre_id']).first()
formsemestres.append(res)
data = []
# Mise en forme des données
for formsem in formsemestres:
data.append(formsem.to_dict())
return jsonify(data)
@bp.route("/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin", methods=["GET"])
@bp.route("/etudiant/nip/<int:nip>/formsemestre/<int:formsemestre_id>/bulletin", methods=["GET"])
@bp.route("/etudiant/ine/<int:ine>/formsemestre/<int:formsemestre_id>/bulletin", methods=["GET"])
def etudiant_bulletin_semestre(formsemestre_id, etudid: int = None, nip: int = None, ine: int = None):
"""
Retourne le bulletin d'un étudiant en fonction de son id et d'un semestre donné
formsemestre_id : l'id d'un formsemestre
etudid : l'etudid d'un étudiant
nip : le code nip d'un étudiant
ine : le code ine d'un étudiant
"""
# Fonction utilisée : app.scodoc.sco_bulletins_json.make_json_formsemestre_bulletinetud()
etu = None
if etudid is not None: # Si route etudid
return make_json_formsemestre_bulletinetud(formsemestre_id, etudid)
else:
if nip is not None: # Si route nip
etu = models.Identite.query.filter_by(code_nip=nip).first()
if ine is not None: # Si route ine
etu = models.Identite.query.filter_by(code_nip=ine).first()
if etu is not None: # Si route nip ou ine
return make_json_formsemestre_bulletinetud(formsemestre_id, etu.etudid)
# return error_response(501, message="Not implemented")
@bp.route(
"/etudiant/etudid/<int:etudid>/semestre/<int:formsemestre_id>/groups", methods=["GET"]
)
@bp.route(
"/etudiant/nip/<int:nip>/semestre/<int:formsemestre_id>/groups", methods=["GET"]
)
@bp.route(
"/etudiant/ine/<int:ine>/semestre/<int:formsemestre_id>/groups", methods=["GET"]
)
def etudiant_groups(formsemestre_id: int, etudid: int = None, nip: int = None, ine: int = None):
"""
Retourne la liste des groupes auxquels appartient l'étudiant dans le semestre indiqué
formsemestre_id : l'id d'un formsemestre
etudid : l'etudid d'un étudiant
nip : le code nip d'un étudiant
ine : le code ine d'un étudiant
Exemple de résultat :
[
{
"partition_id": 1,
"id": 1,
"formsemestre_id": 1,
"partition_name": null,
"numero": 0,
"bul_show_rank": false,
"show_in_lists": true,
"group_id": 1,
"group_name": null
},
{
"partition_id": 2,
"id": 2,
"formsemestre_id": 1,
"partition_name": "TD",
"numero": 1,
"bul_show_rank": false,
"show_in_lists": true,
"group_id": 2,
"group_name": "A"
}
]
"""
# Fonction utilisée : app.scodoc.sco_groups.get_etud_groups()
if etudid is None:
if nip is not None: # Si route nip
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_nip=nip).first()
# Récupération de sont etudid
etudid = etu.etudid
if ine is not None: # Si route ine
# Récupération de l'étudiant
etu = models.Identite.query.filter_by(code_ine=ine).first()
# Récupération de sont etudid
etudid = etu.etudid
# Récupération du formsemestre
sem = models.FormSemestre.query.filter_by(id=formsemestre_id).first()
try:
# Utilisation de la fonction get_etud_groups
data = get_etud_groups(etudid, sem.to_dict())
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return jsonify(data)

64
app/api/evaluations.py Normal file
View File

@ -0,0 +1,64 @@
############################################### Evaluations ###########################################################
from flask import jsonify
from app import models
from app.api import bp
from app.api.auth import token_auth
from app.api.errors import error_response
from app.scodoc.sco_evaluation_db import do_evaluation_get_all_notes
@bp.route("/evaluations/<int:moduleimpl_id>", methods=["GET"])
def evaluations(moduleimpl_id: int):
"""
Retourne la liste des évaluations à partir de l'id d'un moduleimpl
moduleimpl_id : l'id d'un moduleimpl
"""
# Récupération de toutes les évaluations
evals = models.Evaluation.query.filter_by(id=moduleimpl_id).all()
# Mise en forme des données
data = [d.to_dict() for d in evals]
return jsonify(data)
# return error_response(501, message="Not implemented")
@bp.route("/evaluations/eval_notes/<int:evaluation_id>", methods=["GET"])
def evaluation_notes(evaluation_id: int):
"""
Retourne la liste des notes à partir de l'id d'une évaluation donnée
evaluation_id : l'id d'une évaluation
"""
# Fonction utilisée : app.scodoc.sco_evaluation_db.do_evaluation_get_all_notes()
try:
# Utilisation de la fonction do_evaluation_get_all_notes
data = do_evaluation_get_all_notes(evaluation_id)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return jsonify(data)
@bp.route("/evaluations/eval_set_notes?eval_id=<int:eval_id>&etudid=<int:etudid>&note=<float:note>", methods=["POST"])
@bp.route("/evaluations/eval_set_notes?eval_id=<int:eval_id>&nip=<int:nip>&note=<float:note>", methods=["POST"])
@bp.route("/evaluations/eval_set_notes?eval_id=<int:eval_id>&ine=<int:ine>&note=<float:note>", methods=["POST"])
@token_auth.login_required
def evaluation_set_notes(eval_id: int, note: float, etudid: int = None, nip: int = None, ine: int = None):
"""
Set les notes d'une évaluation pour un étudiant donnée
eval_id : l'id d'une évaluation
note : la note à attribuer
etudid : l'etudid d'un étudiant
nip : le code nip d'un étudiant
ine : le code ine d'un étudiant
"""
# Fonction utilisée : app.scodoc.sco_saisie_notes.notes_add()
# Qu'est ce qu'un user ???
# notes_add()
return error_response(501, message="Not implemented")

110
app/api/formations.py Normal file
View File

@ -0,0 +1,110 @@
##############################################" Formations ############################################################
from flask import jsonify
from app import models
from app.api import bp
from app.api.errors import error_response
from app.scodoc.sco_formations import formation_export
from app.scodoc.sco_moduleimpl import moduleimpl_list
@bp.route("/formations", methods=["GET"])
def formations():
"""
Retourne la liste des formations
"""
# Récupération de toutes les formations
list_formations = models.Formation.query.all()
# Mise en forme des données
data = [d.to_dict() for d in list_formations]
return jsonify(data)
@bp.route("/formations/<int:formation_id>", methods=["GET"])
def formations_by_id(formation_id: int):
"""
Retourne une formation en fonction d'un id donné
formation_id : l'id d'une formation
"""
# Récupération de la formation
forma = models.Formation.query.filter_by(id=formation_id).first()
# Mise en forme des données
data = [d.to_dict() for d in forma]
return jsonify(data)
@bp.route("/formations/formation_export/<int:formation_id>", methods=["GET"])
def formation_export_by_formation_id(formation_id: int, export_ids=False):
"""
Retourne la formation, avec UE, matières, modules
"""
# Fonction utilité : app.scodoc.sco_formations.formation_export()
try:
# Utilisation de la fonction formation_export
data = formation_export(formation_id)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return jsonify(data)
@bp.route("/formations/apo/<string:etape_apo>", methods=["GET"])
def formsemestre_apo(etape_apo: int):
"""
Retourne les informations sur les formsemestres
etape_apo : l'id d'une étape apogée
"""
# Récupération des formsemestres
apos = models.FormSemestreEtape.query.filter_by(etape_apo=etape_apo).all()
data = []
# Filtre les formsemestres correspondant + mise en forme des données
for apo in apos:
formsem = models.FormSemestre.query.filter_by(id=apo["formsemestre_id"]).first()
data.append(formsem.to_dict())
return jsonify(data)
# return error_response(501, message="Not implemented")
@bp.route("/formations/moduleimpl/<int:moduleimpl_id>", methods=["GET"])
def moduleimpls(moduleimpl_id: int):
"""
Retourne la liste des moduleimpl
moduleimpl_id : l'id d'un moduleimpl
"""
# Récupération des tous les moduleimpl
list_moduleimpls = models.ModuleImpl.query.filter_by(id=moduleimpl_id).all()
# Mise en forme des données
data = list_moduleimpls[0].to_dict()
return jsonify(data)
@bp.route(
"/formations/moduleimpl/<int:moduleimpl_id>/formsemestre/<int:formsemestre_id>", methods=["GET"])
def moduleimpls_sem(moduleimpl_id: int, formsemestre_id: int):
"""
Retourne la liste des moduleimpl d'un semestre
moduleimpl_id : l'id d'un moduleimpl
formsemestre_id : l'id d'un formsemestre
"""
# Fonction utilisée : app.scodoc.sco_moduleimpl.moduleimpl_list()
try:
# Utilisation de la fonction moduleimpl_list
data = moduleimpl_list(moduleimpl_id, formsemestre_id)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return jsonify(data)

98
app/api/formsemestres.py Normal file
View File

@ -0,0 +1,98 @@
########################################## Formsemestres ##############################################################
from flask import jsonify
from app import models
from app.api import bp
from app.api.errors import error_response
from app.scodoc.sco_bulletins import formsemestre_bulletinetud_dict
from app.scodoc.sco_pvjury import formsemestre_pvjury
from app.scodoc.sco_recapcomplet import formsemestre_recapcomplet
@bp.route("/formations/formsemestre/<int:formsemestre_id>", methods=["GET"])
def formsemestre(formsemestre_id: int):
"""
Retourne l'information sur le formsemestre correspondant au formsemestre_id
formsemestre_id : l'id d'un formsemestre
"""
# Récupération de tous les formsemestres
list_formsemetre = models.FormSemestre.query.filter_by(id=formsemestre_id)
# Mise en forme des données
data = list_formsemetre[0].to_dict()
return jsonify(data)
@bp.route(
"/formsemestre/<int:formsemestre_id>/departements/<string:dept>/etudiant/etudid/<int:etudid>/bulletin",
methods=["GET"],
)
@bp.route(
"/formsemestre/<int:formsemestre_id>/departements/<string:dept>/etudiant/nip/<int:nip>/bulletin",
methods=["GET"],
)
@bp.route(
"/formsemestre/<int:formsemestre_id>/departements/<string:dept>/etudiant/ine/<int:ine>/bulletin",
methods=["GET"],
)
def etudiant_bulletin(formsemestre_id, dept, etudid, format="json", *args, size):
"""
Retourne le bulletin de note d'un étudiant
formsemestre_id : l'id d'un formsemestre
etudid : l'etudid d'un étudiant
nip : le code nip d'un étudiant
ine : le code ine d'un étudiant
"""
# Fonction utilisée : app.scodoc.sco_bulletins.formsemestre_billetinetud_dict()
data = []
if args[0] == "short":
data = formsemestre_bulletinetud_dict(formsemestre_id, etudid, version=args[0])
elif args[0] == "selectevals":
data = formsemestre_bulletinetud_dict(formsemestre_id, etudid, version=args[0])
elif args[0] == "long":
data = formsemestre_bulletinetud_dict(formsemestre_id, etudid)
else:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return jsonify(data)
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"])
def bulletins(formsemestre_id: int):
"""
Retourne les bulletins d'un formsemestre donné
formsemestre_id : l'id d'un formesemestre
"""
# Fonction utilisée : app.scodoc.sco_recapcomplet.formsemestre_recapcomplet()
try:
# Utilisation de la fonction formsemestre_recapcomplet
data = formsemestre_recapcomplet(formsemestre_id)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return jsonify(data)
@bp.route("/formsemestre/<int:formsemestre_id>/jury", methods=["GET"])
def jury(formsemestre_id: int):
"""
Retourne le récapitulatif des décisions jury
formsemestre_id : l'id d'un formsemestre
"""
# Fonction utilisée : app.scodoc.sco_pvjury.formsemestre_pvjury()
try:
# Utilisation de la fonction formsemestre_pvjury
data = formsemestre_pvjury(formsemestre_id)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return jsonify(data)

75
app/api/jury.py Normal file
View File

@ -0,0 +1,75 @@
#################################################### Jury #############################################################
from flask import jsonify
from app import models
from app.api import bp
from app.api.errors import error_response
from app.scodoc.sco_prepajury import feuille_preparation_jury
from app.scodoc.sco_pvjury import formsemestre_pvjury
@bp.route("/jury/formsemestre/<int:formsemestre_id>/preparation_jury", methods=["GET"])
def jury_preparation(formsemestre_id: int): # XXX TODO check à quoi resemble le retour de la fonction
"""
Retourne la feuille de préparation du jury
formsemestre_id : l'id d'un formsemestre
"""
# Fonction utilisée : app.scodoc.sco_prepajury.feuille_preparation_jury()
# Utilisation de la fonction feuille_preparation_jury
prepa_jury = feuille_preparation_jury(formsemestre_id)
return error_response(501, message="Not implemented")
@bp.route("/jury/formsemestre/<int:formsemestre_id>/decisions_jury", methods=["GET"])
def jury_decisions(formsemestre_id: int): # XXX TODO check à quoi resemble le retour de la fonction
"""
Retourne les décisions du jury suivant un formsemestre donné
formsemestre_id : l'id d'un formsemestre
"""
# Fonction utilisée : app.scodoc.sco_pvjury.formsemestre_pvjury()
# Utilisation de la fonction formsemestre_pvjury
decision_jury = formsemestre_pvjury(formsemestre_id)
return error_response(501, message="Not implemented")
@bp.route("/jury/set_decision/etudid?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>", methods=["POST"])
@bp.route("/jury/set_decision/nip?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>", methods=["POST"])
@bp.route("/jury/set_decision/ine?etudid=<int:etudid>&formsemestre_id=<int:formesemestre_id>"
"&jury=<string:decision_jury>&devenir=<string:devenir_jury>&assiduite=<bool>", methods=["POST"])
def set_decision_jury(formsemestre_id: int, decision_jury: str, devenir_jury: str, assiduite: bool,
etudid: int = None, nip: int = None, ine: int = None):
"""
Attribuer la décision du jury et le devenir à un etudiant
formsemestre_id : l'id d'un formsemestre
decision_jury : la décision du jury
devenir_jury : le devenir du jury
assiduite : True ou False
etudid : l'etudid d'un étudiant
nip: le code nip d'un étudiant
ine : le code ine d'un étudiant
"""
return error_response(501, message="Not implemented")
@bp.route("/jury/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/annule_decision", methods=["DELETE"])
@bp.route("/jury/nip/<int:nip>/formsemestre/<int:formsemestre_id>/annule_decision", methods=["DELETE"])
@bp.route("/jury/ine/<int:ine>/formsemestre/<int:formsemestre_id>/annule_decision", methods=["DELETE"])
def annule_decision_jury(formsemestre_id: int, etudid: int = None, nip: int = None, ine: int = None):
"""
Supprime la déciosion du jury pour un étudiant donné
formsemestre_id : l'id d'un formsemestre
etudid : l'etudid d'un étudiant
nip: le code nip d'un étudiant
ine : le code ine d'un étudiant
"""
return error_response(501, message="Not implemented")

79
app/api/partitions.py Normal file
View File

@ -0,0 +1,79 @@
############################################### Partitions ############################################################
from flask import jsonify
from app import models
from app.api import bp
from app.api.auth import token_auth
from app.api.errors import error_response
from app.scodoc.sco_groups import get_group_members, setGroups
@bp.route("/partitions/<int:formsemestre_id>", methods=["GET"])
def partition(formsemestre_id: int):
"""
Retourne la liste de toutes les partitions d'un formsemestre
formsemestre_id : l'id d'un formsemestre
"""
# Récupération de toutes les partitions
partitions = models.Partition.query.filter_by(id=formsemestre_id).all()
# Mise en forme des données
data = [d.to_dict() for d in partitions]
return jsonify(data)
# return error_response(501, message="Not implemented")
# @bp.route(
# "/partitions/formsemestre/<int:formsemestre_id>/groups/group_ids?with_codes=&all_groups=&etat=",
# methods=["GET"],
# )
@bp.route("/partitions/groups/<int:group_id>", methods=["GET"])
@bp.route("/partitions/groups/<int:group_id>/etat/<string:etat>", methods=["GET"])
def etud_in_group(group_id: int, etat=None):
"""
Retourne la liste des étudiants dans un groupe
group_id : l'id d'un groupe
etat :
"""
# Fonction utilisée : app.scodoc.sco_groups.get_group_members()
if etat is None: # Si l'état n'est pas renseigné
try:
# Utilisation de la fonction get_group_members
data = get_group_members(group_id)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
else: # Si l'état est renseigné
try:
# Utilisation de la fonction get_group_members
data = get_group_members(group_id, etat)
except ValueError:
return error_response(409, message="La requête ne peut être traitée en létat actuel")
return jsonify(data)
@bp.route(
"/partitions/set_groups?partition_id=<int:partition_id>&groups_lists=<int:groups_lists>&"
"groups_to_create=<int:groups_to_create>&groups_to_delete=<int:groups_to_delete>", methods=["POST"],
)
@token_auth.login_required
def set_groups(partition_id: int, groups_lists: int, groups_to_delete: int, groups_to_create: int):
"""
Set les groups
partition_id : l'id d'une partition
groups_lists :
groups_ti_delete : les groupes à supprimer
groups_to_create : les groupes à créer
"""
# 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(409, message="La requête ne peut être traitée en létat actuel")

File diff suppressed because it is too large Load Diff

414
app/api/test_api.py Normal file
View File

@ -0,0 +1,414 @@
################################################## Tests ##############################################################
import requests
import os
from app import models
from app.api import bp, requested_format
from app.api.auth import token_auth
from app.api.errors import error_response
SCODOC_USER = "admin"
SCODOC_PASSWORD = "admin"
SCODOC_URL = "http://192.168.1.12:5000"
CHECK_CERTIFICATE = bool(int(os.environ.get("CHECK_CERTIFICATE", False)))
# r0 = requests.post(
# SCODOC_URL + "/ScoDoc/api/tokens", auth=(SCODOC_USER, SCODOC_PASSWORD)
# )
# token = r0.json()["token"]
# HEADERS = {"Authorization": f"Bearer {token}"}
DEPT = None
FORMSEMESTRE = None
ETU = None
@bp.route("/test_dept", methods=["GET"])
def get_departement():
"""
Retourne un département pour les tests
"""
r = requests.get(
SCODOC_URL + "/ScoDoc/api/departements",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
if r.status_code == 200:
dept_id = r.json()[0]
# print(dept_id)
dept = models.Departement.query.filter_by(id=dept_id).first()
dept = dept.to_dict()
fields = ["id", "acronym", "description", "visible", "date_creation"]
for field in dept:
if field not in fields:
return error_response(501, field + " field missing")
global DEPT
DEPT = dept
return error_response(200, "OK")
return error_response(409, "La requête ne peut être traitée en létat actuel")
@bp.route("/test_formsemestre", methods=["GET"])
def get_formsemestre():
"""
Retourne un formsemestre pour les tests
"""
global DEPT
dept_acronym = DEPT["acronym"]
r = requests.get(
SCODOC_URL + "/ScoDoc/api/departements/" + dept_acronym + "/semestres_courant",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
if r.status_code == 200:
formsemestre = r.json()[0]
# print(r.json()[0])
fields = ["gestion_semestrielle", "titre", "scodoc7_id", "date_debut", "bul_bgcolor", "date_fin",
"resp_can_edit", "dept_id", "etat", "resp_can_change_ens", "id", "modalite", "ens_can_edit_eval",
"formation_id", "gestion_compensation", "elt_sem_apo", "semestre_id", "bul_hide_xml", "elt_annee_apo",
"block_moyennes", "formsemestre_id", "titre_num", "date_debut_iso", "date_fin_iso", "responsables"]
for field in formsemestre:
if field not in fields:
return error_response(501, field + " field missing")
global FORMSEMESTRE
FORMSEMESTRE = formsemestre
return error_response(200, "OK")
return error_response(409, "La requête ne peut être traitée en létat actuel")
@bp.route("/test_etu", methods=["GET"])
def get_etudiant():
"""
Retourne un étudiant pour les tests
"""
r = requests.get(
SCODOC_URL + "/ScoDoc/api/etudiants",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
if r.status_code == 200:
etu = r.json()[0]
fields = ["civilite", "code_ine", "code_nip", "date_naissance", "email", "emailperso", "etudid", "nom",
"prenom"]
for field in etu:
if field not in fields:
return error_response(501, field + " field missing")
global ETU
ETU = etu
print(etu)
return error_response(200, "OK")
return error_response(409, "La requête ne peut être traitée en létat actuel")
def test_routes_departements():
"""
Test les routes de la partie Département
"""
# departements
r1 = requests.get(
SCODOC_URL + "/ScoDoc/api/departements",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# liste_etudiants
r2 = requests.post(
SCODOC_URL + "/ScoDoc/api/departements/<string:dept>/etudiants/liste/<int:sem_id>",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# liste_semestres_courant
r3 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# referenciel_competences
r4 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# semestre_index
r5 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
def test_routes_etudiants():
"""
Test les routes de la partie Etudiants
"""
# etudiants
r1 = requests.get(
SCODOC_URL + "/ScoDoc/api/etudiants",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# etudiants_courant
r2 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# etudiant
r3 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# etudiant_formsemestres
r4 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# etudiant_bulletin_semestre
r5 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# etudiant_groups
r6 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
def test_routes_formation():
"""
Test les routes de la partie Formation
"""
# formations
r1 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# formations_by_id
r2 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# formation_export_by_formation_id
r3 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# formsemestre_apo
r4 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# moduleimpls
r5 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# moduleimpls_sem
r6 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
def test_routes_formsemestres():
"""
Test les routes de la partie Formsemestres
"""
# formsemestre
r1 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# etudiant_bulletin
r2 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# bulletins
r3 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# jury
r4 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
def test_routes_partitions():
"""
Test les routes de la partie Partitions
"""
# partition
r1 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# etud_in_group
r2 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# set_groups
r3 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
def test_routes_evaluations():
"""
Test les routes de la partie Evaluations
"""
# evaluations
r1 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# evaluation_notes
r2 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# evaluation_set_notes
r3 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
def test_routes_jury():
"""
Test les routes de la partie Jury
"""
# jury_preparation
r1 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# jury_decisions
r2 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# set_decision_jury
r3 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# annule_decision_jury
r4 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
def test_routes_absences():
"""
Test les routes de la partie Absences
"""
# absences
r1 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# absences_justify
r2 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# abs_signale
r3 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# abs_annule
r4 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# abs_annule_justif
r5 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# abs_groupe_etat
r6 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
def test_routes_logos():
"""
Test les routes de la partie Logos
"""
# liste_logos
r1 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# recup_logo_global
r2 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# logo_dept
r3 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)
# recup_logo_dept_global
r4 = requests.post(
SCODOC_URL + "/ScoDoc/api",
auth=(SCODOC_USER, SCODOC_PASSWORD)
)

View File

@ -220,8 +220,7 @@ class BonusSportAdditif(BonusSport):
bonus_moy_arr = np.sum( bonus_moy_arr = np.sum(
np.where( np.where(
sem_modimpl_moys_inscrits > self.seuil_moy_gen, sem_modimpl_moys_inscrits > self.seuil_moy_gen,
(sem_modimpl_moys_inscrits - self.seuil_comptage) (sem_modimpl_moys_inscrits - seuil_comptage) * self.proportion_point,
* self.proportion_point,
0.0, 0.0,
), ),
axis=1, axis=1,

View File

@ -206,7 +206,11 @@ class FormSemestre(db.Model):
modimpls = self.modimpls.all() modimpls = self.modimpls.all()
if self.formation.is_apc(): if self.formation.is_apc():
modimpls.sort( modimpls.sort(
key=lambda m: (m.module.module_type, m.module.numero, m.module.code) key=lambda m: (
m.module.module_type or 0,
m.module.numero or 0,
m.module.code or 0,
)
) )
else: else:
modimpls.sort( modimpls.sort(

View File

@ -52,6 +52,7 @@ def html_edit_formation_apc(
""" """
parcours = formation.get_parcours() parcours = formation.get_parcours()
assert parcours.APC_SAE assert parcours.APC_SAE
ressources = formation.modules.filter_by(module_type=ModuleType.RESSOURCE).order_by( ressources = formation.modules.filter_by(module_type=ModuleType.RESSOURCE).order_by(
Module.semestre_id, Module.numero, Module.code Module.semestre_id, Module.numero, Module.code
) )
@ -68,6 +69,19 @@ def html_edit_formation_apc(
).order_by( ).order_by(
Module.semestre_id, Module.module_type.desc(), Module.numero, Module.code Module.semestre_id, Module.module_type.desc(), Module.numero, Module.code
) )
ues_by_sem = {}
ects_by_sem = {}
for semestre_idx in semestre_ids:
ues_by_sem[semestre_idx] = formation.ues.filter_by(
semestre_idx=semestre_idx
).order_by(UniteEns.semestre_idx, UniteEns.numero, UniteEns.acronyme)
ects = [ue.ects for ue in ues_by_sem[semestre_idx]]
if None in ects:
ects_by_sem[semestre_idx] = '<span class="missing_ue_ects">manquant</span>'
else:
ects_by_sem[semestre_idx] = sum(ects)
arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags() arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags()
icons = { icons = {
@ -93,7 +107,8 @@ def html_edit_formation_apc(
editable=editable, editable=editable,
tag_editable=tag_editable, tag_editable=tag_editable,
icons=icons, icons=icons,
UniteEns=UniteEns, ues_by_sem=ues_by_sem,
ects_by_sem=ects_by_sem,
), ),
] ]
for semestre_idx in semestre_ids: for semestre_idx in semestre_ids:

View File

@ -562,7 +562,7 @@ def module_edit(module_id=None):
"code", "code",
{ {
"size": 10, "size": 10,
"explanation": "code du module (doit être unique dans la formation)", "explanation": "code du module (issu du programme, exemple M1203 ou R2.01. Doit être unique dans la formation)",
"allow_null": False, "allow_null": False,
"validator": lambda val, field, formation_id=formation_id: check_module_code_unicity( "validator": lambda val, field, formation_id=formation_id: check_module_code_unicity(
val, field, formation_id, module_id=module_id val, field, formation_id, module_id=module_id
@ -701,7 +701,10 @@ def module_edit(module_id=None):
{ {
"title": "Code Apogée", "title": "Code Apogée",
"size": 25, "size": 25,
"explanation": "(optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules", "explanation": """(optionnel) code élément pédagogique Apogée ou liste de codes ELP
séparés par des virgules (ce code est propre à chaque établissement, se rapprocher
du référent Apogée).
""",
"validator": lambda val, _: len(val) < APO_CODE_STR_LEN, "validator": lambda val, _: len(val) < APO_CODE_STR_LEN,
}, },
), ),

View File

@ -305,7 +305,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
( (
"numero", "numero",
{ {
"size": 2, "size": 4,
"explanation": "numéro (1,2,3,4) de l'UE pour l'ordre d'affichage", "explanation": "numéro (1,2,3,4) de l'UE pour l'ordre d'affichage",
"type": "int", "type": "int",
}, },
@ -722,16 +722,16 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
<a href="{url_for('notes.refcomp_show', <a href="{url_for('notes.refcomp_show',
scodoc_dept=g.scodoc_dept, refcomp_id=formation.referentiel_competence.id)}"> scodoc_dept=g.scodoc_dept, refcomp_id=formation.referentiel_competence.id)}">
{formation.referentiel_competence.type_titre} {formation.referentiel_competence.specialite_long} {formation.referentiel_competence.type_titre} {formation.referentiel_competence.specialite_long}
</a> """ </a>&nbsp;"""
msg_refcomp = "changer" msg_refcomp = "changer"
H.append( H.append(
f""" f"""
<ul> <ul>
<li>{descr_refcomp}&nbsp; <a class="stdlink" href="{url_for('notes.refcomp_assoc_formation', <li>{descr_refcomp} <a class="stdlink" href="{url_for('notes.refcomp_assoc_formation',
scodoc_dept=g.scodoc_dept, formation_id=formation_id) scodoc_dept=g.scodoc_dept, formation_id=formation_id)
}">{msg_refcomp}</a> }">{msg_refcomp}</a>
</li> </li>
<li><a class="stdlink" href="{ <li> <a class="stdlink" href="{
url_for('notes.edit_modules_ue_coefs', scodoc_dept=g.scodoc_dept, formation_id=formation_id, semestre_idx=semestre_idx) url_for('notes.edit_modules_ue_coefs', scodoc_dept=g.scodoc_dept, formation_id=formation_id, semestre_idx=semestre_idx)
}">éditer les coefficients des ressources et SAÉs</a> }">éditer les coefficients des ressources et SAÉs</a>
</li> </li>

View File

@ -3,11 +3,9 @@
<div class="formation_list_ues"> <div class="formation_list_ues">
<div class="formation_list_ues_titre">Unités d'Enseignement (UEs)</div> <div class="formation_list_ues_titre">Unités d'Enseignement (UEs)</div>
{% for semestre_idx in semestre_ids %} {% for semestre_idx in semestre_ids %}
<div class="formation_list_ues_sem">Semestre S{{semestre_idx}}</div> <div class="formation_list_ues_sem">Semestre S{{semestre_idx}} (ECTS: {{ects_by_sem[semestre_idx] | safe}})</div>
<ul class="apc_ue_list"> <ul class="apc_ue_list">
{% for ue in formation.ues.filter_by(semestre_idx=semestre_idx).order_by( {% for ue in ues_by_sem[semestre_idx] %}
UniteEns.semestre_idx, UniteEns.numero, UniteEns.acronyme
) %}
<li class="notes_ue_list"> <li class="notes_ue_list">
{% if editable and not loop.first %} {% if editable and not loop.first %}
<a href="{{ url_for('notes.ue_move', <a href="{{ url_for('notes.ue_move',

View File

@ -611,8 +611,7 @@ def SignaleAbsenceGrSemestre(
"""<option value="%(modimpl_id)s" %(sel)s>%(modname)s</option>\n""" """<option value="%(modimpl_id)s" %(sel)s>%(modname)s</option>\n"""
% { % {
"modimpl_id": modimpl["moduleimpl_id"], "modimpl_id": modimpl["moduleimpl_id"],
"modname": modimpl["module"]["code"] "modname": (modimpl["module"]["code"] or "")
or ""
+ " " + " "
+ (modimpl["module"]["abbrev"] or modimpl["module"]["titre"]), + (modimpl["module"]["abbrev"] or modimpl["module"]["titre"]),
"sel": sel, "sel": sel,

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.1.67" SCOVERSION = "9.1.69"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"