########################################## 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/", 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/", 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//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&T", "titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications", "titre": "BUT R&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'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//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//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//etudiants", methods=["GET"], defaults={"etat": "I"}, ) @bp.route( "/formsemestre//etudiants/demissionnaires", methods=["GET"], defaults={"etat": "D"}, ) @bp.route( "/formsemestre//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//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)