MonScoDocEssai/app/api/sco_api.py
2022-02-18 15:53:54 +01:00

732 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""API ScoDoc 9
"""
# PAS ENCORE IMPLEMENTEE, juste un essai
# Pour P. Bouron, il faudrait en priorité l'équivalent de
# Scolarite/Notes/moduleimpl_withmodule_list (alias scodoc7 do_moduleimpl_withmodule_list)
# Scolarite/Notes/evaluation_create
# Scolarite/Notes/evaluation_delete
# Scolarite/Notes/formation_list
# Scolarite/Notes/formsemestre_list
# Scolarite/Notes/formsemestre_partition_list
# Scolarite/Notes/groups_view
# Scolarite/Notes/moduleimpl_status
# Scolarite/setGroups
from datetime import datetime
from flask import jsonify, request, g, send_file
from sqlalchemy.sql import func
from app import db, log
from app.api import bp, requested_format
from app.api.auth import token_auth
from app.api.errors import error_response
from app import models
from app.models import FormSemestre, FormSemestreInscription, Identite
from app.models import ApcReferentielCompetences
from app.scodoc.sco_abs import annule_absence, annule_justif
from app.scodoc.sco_bulletins import formsemestre_bulletinetud_dict
from app.scodoc.sco_bulletins_json import make_json_formsemestre_bulletinetud
from app.scodoc.sco_formations import formation_export
from app.scodoc.sco_formsemestre_inscriptions import do_formsemestre_inscription_listinscrits
from app.scodoc.sco_groups import setGroups, get_etud_groups
from app.scodoc.sco_moduleimpl import moduleimpl_list
from app.scodoc.sco_permissions import Permission
############################################### Departements ##########################################################
@bp.route("/departements", methods=["GET"])
@token_auth.login_required
def departements():
"""
Liste des ids de départements
"""
depts = models.Departement.query.filter_by(visible=True).all()
data = [d.id for d in depts]
return jsonify(data)
@bp.route("/departements/<string:dept>/etudiants/liste/<int:sem_id>", methods=["GET"])
@token_auth.login_required
def liste_etudiants(dept: str, *args, sem_id): # XXX TODO A REVOIR
"""
Liste des étudiants d'un département
"""
# Test si le sem_id à été renseigné ou non
if sem_id is not None:
# Récupération du/des depts
list_depts = models.Departement.query.filter(
models.Departement.acronym == dept,
models.FormSemestre.semestre_id == sem_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.getId())
for y in x:
# Ajout des étudiants dans la liste global
list_etuds.append(y)
else:
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.getId())
for y in x:
list_etuds.append(y)
data = [d.to_dict() for d in list_etuds]
# return jsonify(data)
return error_response(501, message="Not implemented")
@bp.route("/departements/<string:dept>/semestres_courant", methods=["GET"])
@token_auth.login_required
def liste_semestres_courant(dept: str):
"""
Liste des semestres actifs d'un départements donné
"""
depts = models.Departement.query.filter_by(acronym=dept).all()
id_dept = depts[0].id
semestres = models.FormSemestre.query.filter_by(dept_id=id_dept, etat=True).all()
data = semestres[0].to_dict()
return jsonify(data)
@bp.route(
"/departements/<string:dept>/formations/<int:formation_id>/referentiel_competences",
methods=["GET"],
)
def referenciel_competences(dept: str, formation_id: int):
"""
Le référentiel de compétences
"""
# 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>/formations/programme/<string:sem_idx>", methods=["GET"])
def semestre_index(dept: str, sem_idx: str):
"""
"""
return error_response(501, message="not implemented")
#################################################### Etudiants ########################################################
@bp.route("/etudiants", methods=["GET"])
def etudiants():
"""
La liste des étudiants
"""
etu = models.Identite.query.all()
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():
"""
La liste des étudiants courant
"""
etus = models.Identite.query.all()
data = []
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=None, nip=None, ine=None):
"""
Les informations de l'étudiant correspondant à l'id passé en paramètres.
"""
etu = []
if etudid is not None:
etu = models.Identite.query.filter_by(id=etudid).first()
if nip is not None:
etu = models.Identite.query.filter_by(code_nip=etudid).first()
if ine is not None:
etu = models.Identite.query.filter_by(code_ine=etudid).first()
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=None, nip=None, ine=None):
"""
Retourne les semestres qu'un étudiant a suivi
"""
inscriptions = models.FormSemestreInscription.query.all()
sems = []
for sem in inscriptions:
if etudid is not None:
if sem.etudid == etudid:
sems.append(sem)
if nip is not None:
etu = models.Identite.query.filter_by(code_nip=nip).first()
if sem.etudid == etu.etudid:
sems.append(sem)
if ine is not None:
etu = models.Identite.query.filter_by(code_ine=ine).firt()
if sem.etudid == etu.etudid:
sems.append(sem)
data_inscriptions = [d.to_dict() for d in sems]
formsemestres = []
for sem in data_inscriptions:
res = models.FormSemestre.query.filter_by(id=sem['formsemestre_id']).first()
formsemestres.append(res)
data = []
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=None, nip=None, ine=None):
"""
Le bulletin d'un étudiant en fonction de son id et d'un semestre donné
"""
# fonction to use: make_json_formsemestre_bulletinetud()
if etudid is not None:
return make_json_formsemestre_bulletinetud(formsemestre_id, etudid)
else:
etu = None
if nip is not None:
etu = models.Identite.query.filter_by(code_nip=nip).first()
if ine is not None:
etu = models.Identite.query.filter_by(code_nip=ine).first()
if etu is not None:
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=None, nip=None, ine=None):
"""
Liste des groupes auxquels appartient l'étudiant dans le semestre indiqué
"""
# fonction to use : get_etud_groups
if etudid is None:
if nip is not None:
etu = models.Identite.query.filter_by(code_nip=nip).first()
etudid = etu.etudid
if ine is not None:
etu = models.Identite.query.filter_by(code_ine=ine).first()
etudid = etu.etudid
sem = models.FormSemestre.query.filter_by(id=formsemestre_id).first()
try:
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)
##############################################" Formations ############################################################
@bp.route("/formations", methods=["GET"])
def formations():
"""
Liste des formations
"""
list_formations = models.Formation.query.all()
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):
"""
Formation en fonction d'un id donné
"""
forma = models.Formation.query.filter_by(id=formation_id).all()
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):
"""
La formation, avec UE, matières, modules
"""
data = formation_export(formation_id)
return jsonify(data)
@bp.route("/formations/apo/<int:etape_apo>", methods=["GET"])
def formsemestre_apo(etape_apo: int):
"""
Information sur les formsemestres
"""
return error_response(501, message="Not implemented")
@bp.route("/formations/moduleimpl/<int:moduleimpl_id>", methods=["GET"])
def moduleimpls(moduleimpl_id: int):
"""
Liste des moduleimpl
"""
list_moduleimpls = models.ModuleImpl.query.filter_by(id=moduleimpl_id)
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):
"""
Liste de moduleimpl d'un semestre
"""
data = moduleimpl_list(moduleimpl_id, formsemestre_id)
return jsonify(data)
# return error_response(501, message="Not implemented")
#################################################### UE ###############################################################
@bp.route(
"/departements/<string:dept>/formations/programme/<string:sem_id>", methods=["GET"]
)
def eus(dept: str, sem_id: int):
"""
Liste des UES, ressources et SAE d'un semestre
"""
return error_response(501, message="Not implemented")
########################################## Formsemestres ##############################################################
@bp.route("/formations/formsemestre/<int:formsemestre_id>", methods=["GET"])
def formsemestre(formsemestre_id: int):
"""
Information sur le formsemestre correspondant au formsemestre_id
"""
list_formsemetre = models.FormSemestre.query.filter_by(id=formsemestre_id)
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):
"""
Un bulletin de note
"""
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(501, message="Not implemented")
return jsonify(data)
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"])
def bulletins(formsemestre_id: int):
"""
Les bulletins d'un formsemestre donné
"""
return error_response(501, message="Not implemented")
@bp.route("/formsemestre/<int:formsemestre_id>/jury", methods=["GET"])
def jury(formsemestre_id: int):
"""
"""
return error_response(501, message="Not implemented")
############################################### Partitions ############################################################
@bp.route("/partitions/<int:formsemestre_id>", methods=["GET"])
def partition(formsemestre_id: int):
"""
La liste de toutes les partitions d'un formsemestre
"""
partitions = models.Partition.query.filter_by(id=formsemestre_id)
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"],
)
def groups(formsemestre_id: int, group_ids: int):
"""
Liste des étudiants dans un groupe
"""
return error_response(501, message="Not implemented")
@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
"""
try:
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")
############################################### Evaluations ###########################################################
@bp.route("/evaluations/<int:moduleimpl_id>", methods=["GET"])
def evaluations(moduleimpl_id: int):
"""
Liste des évaluations à partir de l'id d'un moduleimpl
"""
evals = models.Evaluation.query.filter_by(id=moduleimpl_id)
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):
"""
Liste des notes à partir de l'id d'une évaluation donnée
"""
notes = models.NotesNotes.query.filter_by(evaluation_id=evaluation_id).all()
data = [d.to_dict() for d in notes]
return jsonify(data)
# return error_response(501, message="Not implemented")
@bp.route(
"/evaluations/eval_set_notes?eval_id=<int:eval_id>&etudid=<int:etudid>&note=<int:note>",
methods=["POST"],
)
@token_auth.login_required
def evaluation_set_notes(eval_id: int, etudid: int, note: float):
"""
Set les notes d'une évaluation pour un étudiant donnée
"""
return error_response(501, message="Not implemented")
#################################################### Jury #############################################################
@bp.route("/jury/formsemestre/<int:formsemestre_id>/preparation_jury", methods=["GET"])
def jury_preparation(formsemestre_id: int):
"""
Feuille de préparation du jury
"""
return error_response(501, message="Not implemented")
@bp.route("/jury/formsemestre/<int:formsemestre_id>/decisions_jury", methods=["GET"])
def jury_decisions(formsemestre_id: int):
"""
Retourne les décisions du jury suivant un formsemestre donné
"""
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=None,
nip=None, ine=None):
"""
Attribuer la décision du jury et le devenir à un etudiant
"""
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=None, nip=None, ine=None):
"""
Supprime la déciosion du jury pour un étudiant donné
"""
return error_response(501, message="Not implemented")
#################################################### Absences #########################################################
@bp.route("/absences/<int:etudid>", methods=["GET"])
def absences(etudid=None, nip=None, ine=None):
"""
Liste des absences d'un étudiant donné
"""
abs = ""
if etudid is not None:
abs = models.Absence.query.filter_by(etudid=etudid).all()
else:
if nip is not None:
etu = models.Identite.query.filter_by(code_nip=nip).first()
abs = models.Absence.query.filter_by(etudid=etu.etudid).all()
if ine is not None:
etu = models.Identite.query.filter_by(code_ine=ine).first()
abs = models.Absence.query.filter_by(etudid=etu.etudid).all()
if abs != "":
data = [d.to_dict() for d in abs]
return jsonify(data)
return error_response(501, message="Not implemented")
@bp.route("/absences/<int:etudid>/abs_just_only", methods=["GET"])
def absences_justify(etudid=None, nip=None, ine=None):
"""
Liste des absences justifiés d'un étudiant donné
"""
abs = ""
if etudid is not None:
abs = models.Absence.query.filter_by(etudid=etudid, estjust=True).all()
else:
if nip is not None:
etu = models.Identite.query.filter_by(code_nip=nip).first()
abs = models.Absence.query.filter_by(etudid=etu.etudid, estjust=True).all()
if ine is not None:
etu = models.Identite.query.filter_by(code_ine=ine).first()
abs = models.Absence.query.filter_by(etudid=etu.etudid, estjust=True).all()
if abs != "":
data = [d.to_dict() for d in abs]
return jsonify(data)
return error_response(501, message="Not implemented")
@bp.route("/absences/abs_signale", methods=["POST"])
@token_auth.login_required
def abs_signale():
"""
Retourne un html
"""
return error_response(501, message="Not implemented")
@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=None, nip=None, ine=None):
"""
Retourne un html
"""
# fonction to use : annule_absence
if etudid is None:
if nip is not None:
etu = models.Identite.query.filter_by(code_nip=nip).first()
etudid = etu.etudid
if ine is not None:
etu = models.Identite.query.filter_by(code_ine=ine).first()
etudid = etu.etudid
try:
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=None, nip=None, ine=None):
"""
Retourne un html
"""
# fonction to use : annule_justif
if etudid is None:
if nip is not None:
etu = models.Identite.query.filter_by(code_nip=nip).first()
etudid = etu.etudid
if ine is not None:
etu = models.Identite.query.filter_by(code_ine=ine).first()
etudid = etu.etudid
try:
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_ids=<int:group_ids>&date_debut=date_debut&date_fin=date_fin",
methods=["GET"],
)
def abs_groupe_etat(
group_ids: int, date_debut, date_fin, with_boursier=True, format="html"
):
"""
Liste des absences d'un ou plusieurs groupes entre deux dates
"""
# list_abs_date
return error_response(501, message="Not implemented")
###################################################### Logos ##########################################################
@bp.route("/logos", methods=["GET"])
def liste_logos(format="json"):
"""
Liste des logos définis pour le site scodoc.
"""
return error_response(501, message="Not implemented")
@bp.route("/logos/<string:nom>", methods=["GET"])
def recup_logo_global(nom: str):
"""
Retourne l'image au format png ou jpg
"""
return error_response(501, message="Not implemented")
@bp.route("/departements/<string:dept>/logos", methods=["GET"])
def logo_dept(dept: str):
"""
Liste des logos définis pour le département visé.
"""
return error_response(501, message="Not implemented")
@bp.route("/departement/<string:dept>/logos/<string:nom>", methods=["GET"])
def recup_logo_dept_global(dept: str, nom: str):
"""
L'image format png ou jpg
"""
return error_response(501, message="Not implemented")