ScoDoc/app/api/etudiants.py

450 lines
14 KiB
Python

##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""
API : accès aux étudiants
"""
from flask import g, jsonify
from flask_login import current_user
from sqlalchemy import or_
import app
from app.api import bp
from app.api.errors import error_response
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/courants", defaults={"long": False})
@bp.route("/etudiants/courants/long", defaults={"long": True})
@permission_required_api(Permission.ScoView, Permission.APIView)
def etudiants_courants(long=False):
"""
La liste des étudiants des semestres "courants" (tous département)
(date du jour comprise dans la période couverte par le sem.)
dans lesquels l'utilisateur a le rôle APIView (donc tous si le dept du
rôle est None).
Exemple de résultat :
[
{
"id": 1234,
"nip": "12345678",
"ine": null,
"nom": "JOHN",
"nom_usuel": None,
"prenom": "DEUF",
"civilite": "M",
}
...
]
En format "long":
{
"boursier": True,
"civilite": "F",
"code_ine": "AP987654",
"code_nip": "1234567",
"codepostaldomicile": "92800",
"date_naissance": "21/06/2000",
"dept_acronym": "CJ",
"dept_id": 1,
"dept_naissance": "092",
"description": "infos portail",
"domicile": "Plaza Condell",
"email": "jeanne.dupont@xxx.fr",
"emailperso": "",
"etudid": 4853,
"id": 4863,
"lieu_naissance": "SEVRES",
"nationalite": "",
"nom": "DUPONT",
"nomprenom": "Mme Jeanne Dupont",
"paysdomicile": "FRANCE",
"prenom": "JEANNE",
"telephone": "0102030405",
"telephonemobile": "",
"typeadresse": "domicile",
"villedomicile": "VALPARAISO",
}
"""
allowed_depts = current_user.get_depts_with_permission(
Permission.APIView | Permission.ScoView
)
etuds = Identite.query.filter(
Identite.id == FormSemestreInscription.etudid,
FormSemestreInscription.formsemestre_id == FormSemestre.id,
FormSemestre.date_debut <= app.db.func.now(),
FormSemestre.date_fin >= app.db.func.now(),
)
if not None in allowed_depts:
# restreint aux départements autorisés:
etuds = etuds.join(Departement).filter(
or_(Departement.acronym == acronym for acronym in allowed_depts)
)
if long:
data = [etud.to_dict_bul(include_urls=False) for etud in etuds]
else:
data = [etud.to_dict_short() for etud in etuds]
return jsonify(data)
@bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiant/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiant/ine/<string:ine>", methods=["GET"])
@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é.
etudid : l'etudid de l'étudiant
nip : le code nip de l'étudiant
ine : le code ine de l'étudiant
Les codes INE et NIP sont uniques au sein d'un département.
Si plusieurs objets ont le même code, on ramène le plus récemment inscrit.
Exemple de résultat :
{
"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": "",
"id": 1,
"codepostaldomicile": "",
"paysdomicile": "",
"telephonemobile": "",
"typeadresse": "domicile",
"domicile": "",
"villedomicile": "",
"telephone": "",
"fax": "",
"description": ""
}
"""
etud = tools.get_etud(etudid, nip, ine)
if etud is None:
return error_response(
404,
message="étudiant inconnu",
)
return jsonify(etud.to_dict_bul(include_urls=False))
@bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiants/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
@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
toujours une liste.
Si non trouvé, liste vide, pas d'erreur.
Dans 99% des cas, la liste contient un seul étudiant, mais si l'étudiant a
été inscrit dans plusieurs départements, on a plusieurs objets (1 par dept.).
"""
if etudid is not None:
query = Identite.query.filter_by(id=etudid)
elif nip is not None:
query = Identite.query.filter_by(code_nip=nip)
elif ine is not None:
query = Identite.query.filter_by(code_ine=ine)
else:
return error_response(
404,
message="parametre manquant",
)
return jsonify([etud.to_dict_bul(include_urls=False) for etud in query])
@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
@bp.route("/etudiant/nip/<string:nip>/formsemestres")
@bp.route("/etudiant/ine/<string:ine>/formsemestres")
@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",
"dept_id": 1,
"elt_annee_apo": null,
"elt_sem_apo": null,
"ens_can_edit_eval": false,
"etat": true,
"formation_id": 1,
"formsemestre_id": 1,
"gestion_compensation": false,
"gestion_semestrielle": false,
"id": 1,
"modalite": "FI",
"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",
},
...
]
"""
if etudid is not None:
query = FormSemestre.query.filter(
FormSemestreInscription.etudid == etudid,
FormSemestreInscription.formsemestre_id == FormSemestre.id,
)
elif nip is not None:
query = FormSemestre.query.filter(
Identite.code_nip == nip,
FormSemestreInscription.etudid == Identite.id,
FormSemestreInscription.formsemestre_id == FormSemestre.id,
)
elif ine is not None:
query = FormSemestre.query.filter(
Identite.code_ine == ine,
FormSemestreInscription.etudid == Identite.id,
FormSemestreInscription.formsemestre_id == FormSemestre.id,
)
else:
return error_response(
404,
message="parametre manquant",
)
formsemestres = query.order_by(FormSemestre.date_debut)
return jsonify(
[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", "pdf": False},
)
@bp.route(
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin",
methods=["GET"],
defaults={"version": "long", "pdf": False},
)
@bp.route(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin",
methods=["GET"],
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", "pdf": False},
)
@bp.route(
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"],
defaults={"version": "short", "pdf": False},
)
@bp.route(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short",
methods=["GET"],
defaults={"version": "short", "pdf": False},
)
@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é
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 : 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()
if etudid is not None:
query = Identite.query.filter_by(id=etudid)
elif nip is not None:
query = Identite.query.filter_by(code_nip=nip, dept_id=dept.id)
elif ine is not None:
query = Identite.query.filter_by(code_ine=ine, dept_id=dept.id)
else:
return error_response(
404,
message="parametre manquant",
)
etud = query.first()
if etud is None:
return error_response(
404,
message="id de l'étudiant (etudid, nip, ine) inconnu",
)
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
)
@bp.route(
"/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/groups",
methods=["GET"],
)
@bp.route(
"/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/groups",
methods=["GET"],
)
@bp.route(
"/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/groups",
methods=["GET"],
)
@permission_required_api(Permission.ScoView, Permission.APIView)
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 formsemestre 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"
}
]
"""
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
if formsemestre is None:
return error_response(
404,
message="formsemestre inconnu",
)
dept = Departement.query.get(formsemestre.dept_id)
if etudid is not None:
query = Identite.query.filter_by(id=etudid)
elif nip is not None:
query = Identite.query.filter_by(code_nip=nip, dept_id=dept.id)
elif ine is not None:
query = Identite.query.filter_by(code_ine=ine, dept_id=dept.id)
else:
return error_response(
404,
message="parametre manquant",
)
etud = query.first()
if etud is None:
return error_response(
404,
message="etudiant inconnu",
)
app.set_sco_dept(dept.acronym)
data = sco_groups.get_etud_groups(etud.id, formsemestre.id)
return jsonify(data)