############################################################################## # ScoDoc # Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """ ScoDoc 9 API : accès aux formations """ from flask import flash, g, request from flask_json import as_json from flask_login import login_required import app from app import db, log from app.api import api_bp as bp, api_web_bp from app.scodoc.sco_utils import json_error from app.decorators import scodoc, permission_required from app.models import ( ApcNiveau, ApcParcours, Formation, UniteEns, ) from app.scodoc import sco_formations from app.scodoc.sco_permissions import Permission @bp.route("/formations") @api_web_bp.route("/formations") @login_required @scodoc @permission_required(Permission.ScoView) @as_json def formations(): """ Retourne la liste de toutes les formations (tous départements) """ query = Formation.query if g.scodoc_dept: query = query.filter_by(dept_id=g.scodoc_dept_id) return [d.to_dict() for d in query] @bp.route("/formations_ids") @api_web_bp.route("/formations_ids") @login_required @scodoc @permission_required(Permission.ScoView) @as_json def formations_ids(): """ Retourne la liste de toutes les id de formations (tous départements, ou du département indiqué dans la route) Exemple de résultat : [ 17, 99, 32 ] """ query = Formation.query if g.scodoc_dept: query = query.filter_by(dept_id=g.scodoc_dept_id) return [d.id for d in query] @bp.route("/formation/") @api_web_bp.route("/formation/") @login_required @scodoc @permission_required(Permission.ScoView) @as_json def formation_by_id(formation_id: int): """ La formation d'id donné formation_id : l'id d'une formation Exemple de résultat : { "id": 1, "acronyme": "BUT R&T", "titre_officiel": "Bachelor technologique réseaux et télécommunications", "formation_code": "V1RET", "code_specialite": null, "dept_id": 1, "titre": "BUT R&T", "version": 1, "type_parcours": 700, "referentiel_competence_id": null, "formation_id": 1 } """ query = Formation.query.filter_by(id=formation_id) if g.scodoc_dept: query = query.filter_by(dept_id=g.scodoc_dept_id) return query.first_or_404().to_dict() @bp.route( "/formation//export", defaults={"export_ids": False}, ) @bp.route( "/formation//export_with_ids", defaults={"export_ids": True}, ) @api_web_bp.route( "/formation//export", defaults={"export_ids": False}, ) @api_web_bp.route( "/formation//export_with_ids", defaults={"export_ids": True}, ) @login_required @scodoc @permission_required(Permission.ScoView) @as_json def formation_export_by_formation_id(formation_id: int, export_ids=False): """ Retourne la formation, avec UE, matières, modules formation_id : l'id d'une formation export_ids : True ou False, si l'on veut ou non exporter les ids Exemple de résultat : { "id": 1, "acronyme": "BUT R&T", "titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications", "formation_code": "V1RET", "code_specialite": null, "dept_id": 1, "titre": "BUT R&T", "version": 1, "type_parcours": 700, "referentiel_competence_id": null, "formation_id": 1, "ue": [ { "acronyme": "RT1.1", "numero": 1, "titre": "Administrer les r\u00e9seaux et l\u2019Internet", "type": 0, "ue_code": "UCOD11", "ects": 12.0, "is_external": false, "code_apogee": "", "coefficient": 0.0, "semestre_idx": 1, "color": "#B80004", "reference": 1, "matiere": [ { "titre": "Administrer les r\u00e9seaux et l\u2019Internet", "numero": 1, "module": [ { "titre": "Initiation aux r\u00e9seaux informatiques", "abbrev": "Init aux r\u00e9seaux informatiques", "code": "R101", "heures_cours": 0.0, "heures_td": 0.0, "heures_tp": 0.0, "coefficient": 1.0, "ects": "", "semestre_id": 1, "numero": 10, "code_apogee": "", "module_type": 2, "coefficients": [ { "ue_reference": "1", "coef": "12.0" }, { "ue_reference": "2", "coef": "4.0" }, { "ue_reference": "3", "coef": "4.0" } ] }, { "titre": "Se sensibiliser \u00e0 l'hygi\u00e8ne informatique...", "abbrev": "Hygi\u00e8ne informatique", "code": "SAE11", "heures_cours": 0.0, "heures_td": 0.0, "heures_tp": 0.0, "coefficient": 1.0, "ects": "", "semestre_id": 1, "numero": 10, "code_apogee": "", "module_type": 3, "coefficients": [ { "ue_reference": "1", "coef": "16.0" } ] }, ... ] }, ... ] }, ] } """ query = Formation.query.filter_by(id=formation_id) if g.scodoc_dept: query = query.filter_by(dept_id=g.scodoc_dept_id) formation = query.first_or_404(formation_id) app.set_sco_dept(formation.departement.acronym) try: data = sco_formations.formation_export(formation_id, export_ids) except ValueError: return json_error(500, message="Erreur inconnue") return data @bp.route("/formation//referentiel_competences") @api_web_bp.route("/formation//referentiel_competences") @login_required @scodoc @permission_required(Permission.ScoView) @as_json def referentiel_competences(formation_id: int): """ Retourne le référentiel de compétences formation_id : l'id d'une formation return null si pas de référentiel associé. """ query = Formation.query.filter_by(id=formation_id) if g.scodoc_dept: query = query.filter_by(dept_id=g.scodoc_dept_id) formation = query.first_or_404(formation_id) if formation.referentiel_competence is None: return None return formation.referentiel_competence.to_dict() @bp.route("/set_ue_parcours/", methods=["POST"]) @api_web_bp.route("/set_ue_parcours/", methods=["POST"]) @login_required @scodoc @permission_required(Permission.EditFormation) @as_json def set_ue_parcours(ue_id: int): """Associe UE et parcours BUT. La liste des ids de parcours est passée en argument JSON. JSON arg: [parcour_id1, parcour_id2, ...] """ query = UniteEns.query.filter_by(id=ue_id) if g.scodoc_dept: query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id) ue: UniteEns = query.first_or_404() parcours_ids = request.get_json(force=True) or [] # may raise 400 Bad Request if parcours_ids == [""]: parcours = [] else: parcours = [ ApcParcours.query.get_or_404(int(parcour_id)) for parcour_id in parcours_ids ] log(f"set_ue_parcours: ue_id={ue.id} parcours_ids={parcours_ids}") ok, error_message = ue.set_parcours(parcours) if not ok: return json_error(404, error_message) return {"status": ok, "message": error_message} @bp.route( "/assoc_ue_niveau//", methods=["POST"], ) @api_web_bp.route( "/assoc_ue_niveau//", methods=["POST"], ) @login_required @scodoc @permission_required(Permission.EditFormation) @as_json def assoc_ue_niveau(ue_id: int, niveau_id: int): """Associe l'UE au niveau de compétence""" query = UniteEns.query.filter_by(id=ue_id) if g.scodoc_dept: query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id) ue: UniteEns = query.first_or_404() niveau: ApcNiveau = ApcNiveau.query.get_or_404(niveau_id) ok, error_message = ue.set_niveau_competence(niveau) if not ok: if g.scodoc_dept: # "usage web" flash(error_message, "error") return json_error(404, error_message) if g.scodoc_dept: # "usage web" flash(f"""{ue.acronyme} associée au niveau "{niveau.libelle}" """) return {"status": 0} @bp.route( "/desassoc_ue_niveau/", methods=["POST"], ) @api_web_bp.route( "/desassoc_ue_niveau/", methods=["POST"], ) @login_required @scodoc @permission_required(Permission.EditFormation) @as_json def desassoc_ue_niveau(ue_id: int): """Désassocie cette UE de son niveau de compétence (si elle n'est pas associée, ne fait rien) """ query = UniteEns.query.filter_by(id=ue_id) if g.scodoc_dept: query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id) ue: UniteEns = query.first_or_404() ue.niveau_competence = None db.session.add(ue) db.session.commit() # Invalidation du cache ue.formation.invalidate_cached_sems() log(f"desassoc_ue_niveau: {ue}") if g.scodoc_dept: # "usage web" flash(f"UE {ue.acronyme} dé-associée") return {"status": 0}