############################################################################## # ScoDoc # Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """ ScoDoc 9 API : accès aux formsemestres """ from flask import abort, jsonify, request import app from app import models from app.api import bp from app.api.auth import token_auth, token_permission_required from app.comp import res_sem from app.comp.moy_mod import ModuleImplResults from app.comp.res_compat import NotesTableCompat from app.models import Evaluation, FormSemestre, FormSemestreEtape, ModuleImpl from app.scodoc.sco_bulletins import get_formsemestre_bulletin_etud_json from app.scodoc import sco_groups from app.scodoc.sco_permissions import Permission from app.scodoc.sco_utils import ModuleType import app.scodoc.sco_utils as scu @bp.route("/formsemestre/", methods=["GET"]) @token_auth.login_required @token_permission_required(Permission.APIView) def formsemestre_infos(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.get_or_404(formsemestre_id) return jsonify(formsemestre.to_dict_api()) @bp.route("/formsemestres/query", methods=["GET"]) @token_auth.login_required @token_permission_required(Permission.APIView) def formsemestres_query(): """ Retourne les formsemestres filtrés par étape Apogée ou année scolaire ou département (acronyme ou id) etape_apo : un code étape apogée annee_scolaire : année de début de l'année scolaire dept_acronym : acronyme du département (eg "RT") dept_id : id du département """ etape_apo = request.args.get("etape_apo") annee_scolaire = request.args.get("annee_scolaire") dept_acronym = request.args.get("dept_acronym") dept_id = request.args.get("dept_id") formsemestres = FormSemestre.query if etape_apo is not None: formsemestres = formsemestres.join(FormSemestreEtape).filter( FormSemestreEtape.etape_apo == etape_apo ) if annee_scolaire is not None: try: annee_scolaire_int = int(annee_scolaire) except ValueError: abort(404, "invalid annee_scolaire: not int") debut_annee = scu.date_debut_anne_scolaire(annee_scolaire_int) fin_annee = scu.date_fin_anne_scolaire(annee_scolaire_int) formsemestres = formsemestres.filter( FormSemestre.date_fin >= debut_annee, FormSemestre.date_debut <= fin_annee ) if dept_acronym is not None: formsemestres = formsemestres.join(models.Departement).filter_by( acronym=dept_acronym ) if dept_id is not None: try: dept_id = int(dept_id) except ValueError: abort(404, "invalid dept_id: not int") formsemestres = formsemestres.filter_by(dept_id=dept_id) return jsonify([formsemestre.to_dict_api() for formsemestre in formsemestres]) @bp.route("/formsemestre//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 : liste, voir https://scodoc.org/ScoDoc9API/#bulletin """ formsemestre = models.FormSemestre.query.get_or_404(formsemestre_id) app.set_sco_dept(formsemestre.departement.acronym) data = [] for etu in formsemestre.etuds: bul_etu = get_formsemestre_bulletin_etud_json(formsemestre, etu) data.append(bul_etu.json) return jsonify(data) @bp.route( "/formsemestre//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.get_or_404(formsemestre_id) ues = formsemestre.query_ues() m_list = { ModuleType.RESSOURCE: [], ModuleType.SAE: [], ModuleType.STANDARD: [], } for modimpl in formsemestre.modimpls_sorted: d = modimpl.to_dict(convert_objects=True) m_list[modimpl.module.module_type].append(d) return jsonify( { "ues": [ue.to_dict(convert_objects=True) for ue in ues], "ressources": m_list[ModuleType.RESSOURCE], "saes": m_list[ModuleType.SAE], "modules": m_list[ModuleType.STANDARD], } ) @bp.route( "/formsemestre//etudiants", methods=["GET"], defaults={"etat": scu.INSCRIT}, ) @bp.route( "/formsemestre//etudiants/demissionnaires", methods=["GET"], defaults={"etat": scu.DEMISSION}, ) @bp.route( "/formsemestre//etudiants/defaillants", methods=["GET"], defaults={"etat": scu.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 formsemestre formsemestre_id : l'id d'un formsemestre """ formsemestre: FormSemestre = models.FormSemestre.query.filter_by( id=formsemestre_id ).first_or_404() inscriptions = [ins for ins in formsemestre.inscriptions if ins.etat == etat] etuds = [ins.etud.to_dict_short() for ins in inscriptions] # Ajout des groupes de chaque étudiants # XXX A REVOIR: trop inefficace ! for etud in etuds: etud["groups"] = sco_groups.get_etud_groups(etud["id"], formsemestre_id) return jsonify(etuds) @bp.route("/formsemestre//etat_evals", methods=["GET"]) @token_auth.login_required @token_permission_required(Permission.APIView) def etat_evals(formsemestre_id: int): """ Informations sur l'état des évaluations d'un formsemestre. formsemestre_id : l'id d'un semestre Exemple de résultat : [ { "id": 1, // moduleimpl_id "titre": "Initiation aux réseaux informatiques", "evaluations": [ { "id": 1, "description": null, "datetime_epreuve": null, "heure_fin": "09:00:00", "coefficient": "02.00" "is_complete": true, "nb_inscrits": 16, "nb_manquantes": 0, "ABS": 0, "ATT": 0, "EXC": 0, "saisie_notes": { "datetime_debut": "2021-09-11T00:00:00+02:00", "datetime_fin": "2022-08-25T00:00:00+02:00", "datetime_mediane": "2022-03-19T00:00:00+01:00" } }, ... ] }, ] """ formsemestre = FormSemestre.query.get_or_404(formsemestre_id) app.set_sco_dept(formsemestre.departement.acronym) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) result = [] for modimpl_id in nt.modimpls_results: modimpl_results: ModuleImplResults = nt.modimpls_results[modimpl_id] modimpl = ModuleImpl.query.get_or_404(modimpl_id) modimpl_dict = modimpl.to_dict() list_eval = [] for evaluation_id in modimpl_results.evaluations_etat: eval_etat = modimpl_results.evaluations_etat[evaluation_id] evaluation = Evaluation.query.get_or_404(evaluation_id) eval_dict = evaluation.to_dict() eval_dict["etat"] = eval_etat.to_dict() eval_dict["nb_inscrits"] = modimpl_results.nb_inscrits_module eval_dict["nb_notes_manquantes"] = len( modimpl_results.evals_etudids_sans_note[evaluation.id] ) eval_dict["nb_notes_abs"] = sum( modimpl_results.evals_notes[evaluation.id] == scu.NOTES_ABSENCE ) eval_dict["nb_notes_att"] = eval_etat.nb_attente eval_dict["nb_notes_exc"] = sum( modimpl_results.evals_notes[evaluation.id] == scu.NOTES_NEUTRALISE ) # Récupération de toutes les notes de l'évaluation # eval["notes"] = modimpl_results.get_eval_notes_dict(evaluation_id) notes = models.NotesNotes.query.filter_by(evaluation_id=evaluation.id).all() date_debut = None date_fin = None date_mediane = None # Si il y a plus d'une note saisie pour l'évaluation if len(notes) >= 1: # Trie des notes en fonction de leurs dates notes_sorted = sorted(notes, key=lambda note: note.date) date_debut = notes_sorted[0].date date_fin = notes_sorted[-1].date # Récupération de l'id de la note médiane list_id_notes_sorted = [note.id for note in notes_sorted] id_mediane = list_id_notes_sorted[len(list_id_notes_sorted) // 2] date_mediane = notes_sorted[id_mediane].date eval_dict["saisie_notes"] = { "datetime_debut": date_debut.isoformat() if date_debut is not None else None, "datetime_fin": date_fin.isoformat() if date_fin is not None else None, "datetime_mediane": date_mediane.isoformat() if date_mediane is not None else None, } list_eval.append(eval) modimpl_dict["evaluations"] = list_eval result.append(modimpl_dict) return jsonify(result) @bp.route("/formsemestre//resultats", methods=["GET"]) @token_auth.login_required @token_permission_required(Permission.APIView) def formsemestre_resultat(formsemestre_id: int): """Tableau récapitulatif des résultats Pour chaque étudiant, son état, ses groupes, ses moyennes d'UE et de modules. """ formsemestre = FormSemestre.query.get_or_404(formsemestre_id) app.set_sco_dept(formsemestre.departement.acronym) res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) rows, footer_rows, titles, column_ids = res.get_table_recap( convert_values=False, include_evaluations=False, mode_jury=False, allow_html=False, ) # Supprime les champs inutiles (mise en forme) table = [{k: row[k] for k in row if not k[0] == "_"} for row in rows] # Ajoute les groupes etud_groups = sco_groups.get_formsemestre_etuds_groups(formsemestre_id) for row in table: row["partitions"] = etud_groups.get(row["etudid"], {}) return jsonify(table)