ScoDoc-Lille/app/api/formsemestres.py

631 lines
20 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.

########################################## Formsemestres ##############################################################
import statistics
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.errors import error_response
from app.comp import res_sem
from app.comp.moy_mod import ModuleImplResults
from app.comp.res_compat import NotesTableCompat
from app.models import Departement, FormSemestre, FormSemestreEtape, ModuleImpl
from app.scodoc.sco_bulletins import get_formsemestre_bulletin_etud_json
from app.scodoc.sco_groups import get_etud_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):
"""
Information sur le formsemestre indiqué.
formsemestre_id : l'id du formsemestre
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": [1, 99], // uids
"scodoc7_id": null,
"semestre_id": 1,
"titre_formation" : "BUT GEA",
"titre_num": "BUT GEA semestre 1",
"titre": "BUT GEA",
}
"""
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)
@bp.route("/formsemestre/apo/<string:etape_apo>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def formsemestre_apo(etape_apo: str):
"""
Retourne les informations sur les formsemestres ayant cette étape Apogée
etape_apo : un code étape apogée
Exemple de résultat :
[
{ ...formsemestre...
}, ...
]
"""
formsemestres = FormSemestre.query.filter(
FormSemestreEtape.etape_apo == etape_apo,
FormSemestreEtape.formsemestre_id == FormSemestre.id,
)
return jsonify(
[formsemestre.to_dict(convert_parcours=True) for formsemestre in formsemestres]
)
@bp.route("/formsemestre/<int:formsemestre_id>/bulletins", methods=["GET"])
@token_auth.login_required
@token_permission_required(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
}
}
},
...
]
"""
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 = []
for etu in formsemestre.etuds:
bul_etu = get_formsemestre_bulletin_etud_json(formsemestre, etu)
data.append(bul_etu.json)
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)
def formsemestre_programme(formsemestre_id: int):
"""
Retourne la liste des Ues, ressources et SAE d'un semestre
formsemestre_id : l'id d'un formsemestre
Exemple de résultat :
{
"ues": [
{
"type": 0,
"formation_id": 1,
"ue_code": "UCOD11",
"id": 1,
"ects": 12.0,
"acronyme": "RT1.1",
"is_external": false,
"numero": 1,
"code_apogee": "",
"titre": "Administrer les r\u00e9seaux et l\u2019Internet",
"coefficient": 0.0,
"semestre_idx": 1,
"color": "#B80004",
"ue_id": 1
},
...
],
"ressources": [
{
"ens": [ 10, 18 ],
"formsemestre_id": 1,
"id": 15,
"module": {
"abbrev": "Programmer",
"code": "SAE15",
"code_apogee": "V7GOP",
"coefficient": 1.0,
"formation_id": 1,
"heures_cours": 0.0,
"heures_td": 0.0,
"heures_tp": 0.0,
"id": 15,
"matiere_id": 3,
"module_id": 15,
"module_type": 3,
"numero": 50,
"semestre_id": 1,
"titre": "Programmer en Python",
"ue_id": 3
},
"module_id": 15,
"moduleimpl_id": 15,
"responsable_id": 2
},
...
],
"saes": [
{
...
},
...
],
"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()
ues = formsemestre.query_ues()
m_list = {
ModuleType.RESSOURCE: [],
ModuleType.SAE: [],
ModuleType.STANDARD: [],
}
for modimpl in formsemestre.modimpls_sorted:
d = modimpl.to_dict()
m_list[modimpl.module.module_type].append(d)
return jsonify(
{
"ues": [ue.to_dict() 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": "I"},
)
@bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants/demissionnaires",
methods=["GET"],
defaults={"etat": "D"},
)
@bp.route(
"/formsemestre/<int:formsemestre_id>/etudiants/defaillants",
methods=["GET"],
defaults={"etat": "DEF"},
)
@token_auth.login_required
@token_permission_required(Permission.APIView)
def formsemestre_etudiants(formsemestre_id: int, etat: str):
"""
Retourne la liste des étudiants d'un semestre
formsemestre_id : l'id d'un semestre
"""
# fonction to use : sco_groups.get_etud_groups
formsemestre = models.FormSemestre.query.filter_by(
id=formsemestre_id
).first_or_404()
# Récupération des étudiants du formsemestre
etuds = [etu.to_dict_short() for etu in formsemestre.etuds]
res = []
# Trie des étudiants suivant leur état d'inscription voulu
for etu in etuds:
formsemestre_inscription = models.FormSemestreInscription.query.filter_by(
formsemestre_id=formsemestre_id, etudid=etu["id"]
).first_or_404()
if formsemestre_inscription.etat == etat:
res.append(etu)
# Ajout des groups de chaques étudiants
for etu in res:
etu["groups"] = get_etud_groups(etu["id"], formsemestre_id)
return jsonify(res)
@bp.route("/formsemestre/<int:formsemestre_id>/etat_evals", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etat_evals(formsemestre_id: int):
"""
Retourne les informations sur l'état des évaluations d'un semestre donnée
formsemestre_id : l'id d'un semestre
Exemple de résultat :
{
"RT1.1": [
{
"id": 1,
"titre": "Initiation aux réseaux informatiques",
"evaluations": [
{
"id": 1,
"description": null,
"datetime_epreuve": null,
"heure_fin": "09:00:00",
"coefficient": "02.00"
"comptee": "oui",
"inscrits": 16,
"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"
}
},
{
"id": 22,
"description": null,
"datetime_epreuve": "Tue, 31 May 2022 00:00:00 GMT",
"heure_fin": "08:00:00",
"comptee": "oui",
"inscrits": 16,
"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"
}
},
]
},
]
}
"""
# Récupération du semestre
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
# Set du dept
dept = Departement.query.get(formsemestre.dept_id)
app.set_sco_dept(dept.acronym)
# Récupération des Ues
list_ues = formsemestre.query_ues()
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
ues = {}
for ue in list_ues:
modules = []
mods = ue.modules
for mod in mods:
dict_module = {}
moduleimpl = ModuleImpl.query.get_or_404(mod.id)
modimpl_results: ModuleImplResults = nt.modimpls_results[moduleimpl.id]
dict_module["id"] = mod.id
dict_module["titre"] = mod.titre
list_eval = []
for evaluation in moduleimpl.evaluations:
eval_etat = modimpl_results.evaluations_etat[evaluation.id]
eval = {}
eval["id"] = evaluation.id
eval["description"] = evaluation.description
eval["datetime_epreuve"] = (
evaluation.jour.isoformat() if evaluation.jour is not None else None
)
eval["heure_fin"] = evaluation.heure_fin.isoformat()
eval["coefficient"] = evaluation.coefficient
eval["comptee"] = "oui" if eval_etat.is_complete else "non"
eval["inscrits"] = modimpl_results.nb_inscrits_module
eval["manquantes"] = len(
modimpl_results.evals_etudids_sans_note[evaluation.id]
)
eval["ABS"] = sum(
modimpl_results.evals_notes[evaluation.id] == scu.NOTES_ABSENCE
)
eval["ATT"] = eval_etat.nb_attente
eval["EXC"] = sum(
modimpl_results.evals_notes[evaluation.id] == scu.NOTES_NEUTRALISE
)
# Récupération de toutes les notes de l'évaluation
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]
# Ici si la longueur est paire on prend, on prend le +1 car un indice ne peux pas avoir de nombre floatant
id_mediane = list_id_notes_sorted[
int((len(list_id_notes_sorted)) / 2)
]
for n in notes_sorted:
if n.id == id_mediane:
date_mediane = n.date
eval["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)
dict_module["evaluations"] = list_eval
modules.append(dict_module)
ues[ue.acronyme] = modules
return jsonify(ues)