From 1598537f24c5593124806252521c80a622ccfdb3 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 24 Oct 2022 23:18:45 +0200 Subject: [PATCH] Corrige Import/Export formations BUT en XML --- app/models/but_refcomp.py | 4 +- app/scodoc/sco_edit_ue.py | 4 +- app/scodoc/sco_formations.py | 159 +++++++++++++++++++++++++--------- app/scodoc/sco_ue_external.py | 13 ++- app/views/notes.py | 12 ++- 5 files changed, 140 insertions(+), 52 deletions(-) diff --git a/app/models/but_refcomp.py b/app/models/but_refcomp.py index e8b9f0f3..dd995331 100644 --- a/app/models/but_refcomp.py +++ b/app/models/but_refcomp.py @@ -376,7 +376,9 @@ class ApcAppCritique(db.Model, XMLModel): query = query.filter(ApcNiveau.competence == competence) return query - def to_dict(self) -> dict: + def to_dict(self, with_code=False) -> dict: + if with_code: + return {"code": self.code, "libelle": self.libelle} return {"libelle": self.libelle} def get_label(self) -> str: diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 71236df0..830e92d6 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -135,7 +135,6 @@ def do_ue_create(args): formation.invalidate_module_coefs() # news ue = UniteEns.query.get(ue_id) - flash(f"UE créée (code {ue.ue_code})") formation = Formation.query.get(args["formation_id"]) ScolarNews.add( typ=ScolarNews.NEWS_FORM, @@ -512,7 +511,8 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No "semestre_id": tf[2]["semestre_idx"], }, ) - flash("UE créée") + ue = UniteEns.query.get(ue_id) + flash(f"UE créée (code {ue.ue_code})") else: do_ue_edit(tf[2]) flash("UE modifiée") diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py index 5742d248..150f2af3 100644 --- a/app/scodoc/sco_formations.py +++ b/app/scodoc/sco_formations.py @@ -38,9 +38,15 @@ import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app import db from app import log -from app.models import Formation, Module +from app.models import Formation, Module, UniteEns from app.models import ScolarNews -from app.models.but_refcomp import ApcParcours, ApcReferentielCompetences +from app.models.but_refcomp import ( + ApcAppCritique, + ApcCompetence, + ApcNiveau, + ApcParcours, + ApcReferentielCompetences, +) from app.scodoc import sco_cache from app.scodoc import sco_codes_parcours from app.scodoc import sco_edit_matiere @@ -109,23 +115,32 @@ def formation_export( in desired format """ formation: Formation = Formation.query.get_or_404(formation_id) - F = formation.to_dict(with_refcomp_attrs=True) + f_dict = formation.to_dict(with_refcomp_attrs=True) selector = {"formation_id": formation_id} if not export_external_ues: selector["is_external"] = False ues = sco_edit_ue.ue_list(selector) - F["ue"] = ues - for ue in ues: - ue_id = ue["ue_id"] - ue["reference"] = ue_id # pour les coefficients + f_dict["ue"] = ues + for ue_dict in ues: + ue_id = ue_dict["ue_id"] + if formation.is_apc(): + # BUT: indique niveau de compétence associé à l'UE + ue = UniteEns.query.get(ue_id) + if ue.niveau_competence: + ue_dict["apc_niveau_libelle"] = ue.niveau_competence.libelle + ue_dict["apc_niveau_annee"] = ue.niveau_competence.annee + ue_dict["apc_niveau_ordre"] = ue.niveau_competence.ordre + ue_dict["reference"] = ue_id # pour les coefficients if not export_ids: - del ue["id"] - del ue["ue_id"] - del ue["formation_id"] - if ue["ects"] is None: - del ue["ects"] + del ue_dict["id"] + del ue_dict["ue_id"] + del ue_dict["formation_id"] + if "niveau_competence_id" in ue_dict: + del ue_dict["niveau_competence_id"] + if ue_dict["ects"] is None: + del ue_dict["ects"] mats = sco_edit_matiere.matiere_list({"ue_id": ue_id}) - ue["matiere"] = mats + ue_dict["matiere"] = mats for mat in mats: matiere_id = mat["matiere_id"] if not export_ids: @@ -153,9 +168,15 @@ def formation_export( p.to_dict(with_annees=False) for p in module.parcours ] # Et les AC - mod["app_critiques"] = { - x.code: x.to_dict() for x in module.app_critiques - } + if format == "xml": + # XML préfère une liste + mod["app_critiques"] = [ + x.to_dict(with_code=True) for x in module.app_critiques + ] + else: + mod["app_critiques"] = { + x.code: x.to_dict() for x in module.app_critiques + } if not export_ids: del mod["id"] del mod["ue_id"] @@ -167,7 +188,7 @@ def formation_export( filename = f"scodoc_formation_{formation.departement.acronym}_{formation.acronyme or ''}_v{formation.version}" return scu.sendResult( - F, + f_dict, name="formation", format=format, force_outer_xml_tag=False, @@ -176,6 +197,50 @@ def formation_export( ) +def _formation_retreive_refcomp(f_dict: dict) -> int: + """Recherche si on un référentiel de compétence chargé pour + cette formation: utilise comme clé (version_orebut, specialite, type_titre) + Retourne: referentiel_competence_id ou None + """ + refcomp_version_orebut = f_dict.get("refcomp_version_orebut") + refcomp_specialite = f_dict.get("refcomp_specialite") + refcomp_type_titre = f_dict.get("refcomp_type_titre") + if all((refcomp_version_orebut, refcomp_specialite, refcomp_type_titre)): + refcomp = ApcReferentielCompetences.query.filter_by( + dept_id=g.scodoc_dept_id, + type_titre=refcomp_type_titre, + specialite=refcomp_specialite, + version_orebut=refcomp_version_orebut, + ).first() + if refcomp: + return refcomp.id + else: + flash( + f"Impossible de trouver le référentiel de compétence pour {refcomp_specialite} : est-il chargé ?" + ) + return None + + +def _formation_retreive_apc_niveau( + referentiel_competence_id: int, ue_dict: dict +) -> int: + """Recherche dans le ref. de comp. un niveau pour cette UE + utilise comme clé (libelle, annee, ordre) + """ + libelle = ue_dict.get("apc_niveau_libelle") + annee = ue_dict.get("apc_niveau_annee") + ordre = ue_dict.get("apc_niveau_ordre") + if all((libelle, annee, ordre)): + niveau = ( + ApcNiveau.query.filter_by(libelle=libelle, annee=annee, ordre=ordre) + .join(ApcCompetence) + .filter_by(referentiel_id=referentiel_competence_id) + ).first() + if niveau is not None: + return niveau.id + return None + + def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False): """Create a formation from XML representation (format dumped by formation_export( format='xml' )) @@ -206,47 +271,32 @@ def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False): (élément 'formation' inexistant par exemple).""" ) from exc assert D[0] == "formation" - F = D[1] - F["dept_id"] = g.scodoc_dept_id + f_dict = D[1] + f_dict["dept_id"] = g.scodoc_dept_id # Pour les clonages, on prend le refcomp_id donné: referentiel_competence_id = ( - F.get("referentiel_competence_id") if use_local_refcomp else None + f_dict.get("referentiel_competence_id") if use_local_refcomp else None ) # Sinon, on cherche a retrouver le ref. comp. if referentiel_competence_id is None: - refcomp_version_orebut = F.get("refcomp_version_orebut") - refcomp_specialite = F.get("refcomp_specialite") - refcomp_type_titre = F.get("refcomp_type_titre") - if all((refcomp_version_orebut, refcomp_specialite, refcomp_type_titre)): - refcomp = ApcReferentielCompetences.query.filter_by( - dept_id=g.scodoc_dept_id, - type_titre=refcomp_type_titre, - specialite=refcomp_specialite, - version_orebut=refcomp_version_orebut, - ).first() - if refcomp: - referentiel_competence_id = refcomp.id - else: - flash( - f"Impossible de trouver le référentiel de compétence pour {refcomp_specialite} : est-il chargé ?" - ) - F["referentiel_competence_id"] = referentiel_competence_id + referentiel_competence_id = _formation_retreive_refcomp(f_dict) + f_dict["referentiel_competence_id"] = referentiel_competence_id # find new version number formations = sco_formations.formation_list( args={ - "acronyme": F["acronyme"], - "titre": F["titre"], - "dept_id": F["dept_id"], + "acronyme": f_dict["acronyme"], + "titre": f_dict["titre"], + "dept_id": f_dict["dept_id"], } ) if formations: version = max(f["version"] or 0 for f in formations) else: version = 0 - F["version"] = version + 1 + f_dict["version"] = version + 1 # create formation - formation_id = sco_edit_formation.do_formation_create(F) + formation_id = sco_edit_formation.do_formation_create(f_dict) log(f"formation {formation_id} created") ues_old2new = {} # xml ue_id : new ue_id @@ -265,6 +315,13 @@ def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False): del ue_info[1]["ue_id"] else: xml_ue_id = None + if referentiel_competence_id is None: + if "niveau_competence_id" in ue_info[1]: + del ue_info[1]["niveau_competence_id"] + else: + ue_info[1]["niveau_competence_id"] = _formation_retreive_apc_niveau( + referentiel_competence_id, ue_info[1] + ) ue_id = sco_edit_ue.do_ue_create(ue_info[1]) if xml_ue_id: ues_old2new[xml_ue_id] = ue_id @@ -304,10 +361,26 @@ def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False): ue_reference = int(child[1]["ue_reference"]) coef = float(child[1]["coef"]) ue_coef_dict[ue_reference] = coef + elif child[0] == "app_critiques" and ( + referentiel_competence_id is not None + ): + ac_code = child[1]["code"] + ac = ( + ApcAppCritique.query.filter_by(code=ac_code) + .join(ApcNiveau) + .join(ApcCompetence) + .filter_by(referentiel_id=referentiel_competence_id) + ).first() + if ac is not None: + module.app_critiques.append(ac) + db.session.add(module) + else: + log(f"Warning: AC {ac_code} inexistant !") + elif child[0] == "parcours": # Si on a un référentiel de compétences, # associe les parcours de ce module (BUT) - if referentiel_competence_id: + if referentiel_competence_id is not None: code_parcours = child[1]["code"] parcours = ApcParcours.query.filter_by( code=code_parcours, diff --git a/app/scodoc/sco_ue_external.py b/app/scodoc/sco_ue_external.py index ca1bdba7..0e17492c 100644 --- a/app/scodoc/sco_ue_external.py +++ b/app/scodoc/sco_ue_external.py @@ -54,13 +54,14 @@ Solution proposée (nov 2014): """ import flask -from flask import request +from flask import flash, request from flask_login import current_user from app.models.formsemestre import FormSemestre import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from app import log +from app.models import UniteEns from app.scodoc import html_sco_header from app.scodoc import sco_codes_parcours from app.scodoc import sco_edit_matiere @@ -83,8 +84,11 @@ def external_ue_create( acronyme="", ue_type=sco_codes_parcours.UE_STANDARD, ects=0.0, -): - """Crée UE/matiere/module/evaluation puis saisie les notes""" +) -> int: + """Crée UE/matiere/module dans la formation du formsemestre + puis un moduleimpl. + Return: moduleimpl_id + """ formsemestre = FormSemestre.query.get_or_404(formsemestre_id) log(f"creating external UE in {formsemestre}: {acronyme}") @@ -112,7 +116,8 @@ def external_ue_create( "is_external": True, }, ) - + ue = UniteEns.query.get(ue_id) + flash(f"UE créée (code {ue.ue_code})") matiere_id = sco_edit_matiere.do_matiere_create( {"ue_id": ue_id, "titre": titre or acronyme, "numero": 1} ) diff --git a/app/views/notes.py b/app/views/notes.py index b73adf41..3fca69d9 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -679,9 +679,17 @@ def formation_import_xml_form(): return f""" { html_sco_header.sco_header(page_title="Import d'une formation") }

Import effectué !

-

Voir la formation

+ )}">Voir la formation + +
  • Supprimer cette formation + (en cas d'erreur, par exemple pour charger auparavant le référentiel de compétences) +
  • + { html_sco_header.sco_footer() } """