From 407c3ef47292299ccaf9e3d990b3b6ce2d62292b Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 3 Jan 2022 12:25:42 +0100 Subject: [PATCH] WIP: nouveau format XML Orebut ref. comp. --- app/but/import_refcomp.py | 7 ++++--- app/models/but_refcomp.py | 33 +++++++++++++++++++++++++++++++-- app/views/refcomp.py | 6 ++++-- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/app/but/import_refcomp.py b/app/but/import_refcomp.py index 04e96b50c..aa59d7c3d 100644 --- a/app/but/import_refcomp.py +++ b/app/but/import_refcomp.py @@ -29,8 +29,8 @@ def orebut_import_refcomp(xml_data: str, dept_id: int, orig_filename=None): """ try: root = ElementTree.XML(xml_data) - except ElementTree.ParseError: - raise ScoFormatError("fichier XML Orébut invalide") + except ElementTree.ParseError as exc: + raise ScoFormatError(f"fichier XML Orébut invalide (2): {exc.args}") if root.tag != "referentiel_competence": raise ScoFormatError("élément racine 'referentiel_competence' manquant") args = ApcReferentielCompetences.attr_from_xml(root.attrib) @@ -77,7 +77,8 @@ def orebut_import_refcomp(xml_data: str, dept_id: int, orig_filename=None): a = ApcAnneeParcours(**ApcAnneeParcours.attr_from_xml(annee.attrib)) parc.annees.append(a) for competence in annee.findall("competence"): - nom = competence.attrib["nom"] + nom_court = competence.attrib["nom_court"] + XXX niveau = int(competence.attrib["niveau"]) # Retrouve la competence comp = ref.competences.filter_by(titre=nom).all() diff --git a/app/models/but_refcomp.py b/app/models/but_refcomp.py index bf1b4cb68..30229a965 100644 --- a/app/models/but_refcomp.py +++ b/app/models/but_refcomp.py @@ -9,11 +9,24 @@ from datetime import datetime from enum import unique from typing import Any +from sqlalchemy.orm import class_mapper +import sqlalchemy + from app import db from app.scodoc.sco_utils import ModuleType +# from https://stackoverflow.com/questions/2537471/method-of-iterating-over-sqlalchemy-models-defined-columns +def attribute_names(cls): + "liste ids (noms de colonnes) d'un modèle" + return [ + prop.key + for prop in class_mapper(cls).iterate_properties + if isinstance(prop, sqlalchemy.orm.ColumnProperty) + ] + + class XMLModel: _xml_attribs = {} # to be overloaded id = "_" @@ -24,8 +37,12 @@ class XMLModel: and renamed for our models. The mapping is specified by the _xml_attribs attribute in each model class. + Keep only attributes corresponding to columns in our model: + other XML attributes are simply ignored. """ - return {cls._xml_attribs.get(k, k): v for (k, v) in args.items()} + columns = attribute_names(cls) + renamed_attributes = {cls._xml_attribs.get(k, k): v for (k, v) in args.items()} + return {k: renamed_attributes[k] for k in renamed_attributes if k in columns} def __repr__(self): return f'<{self.__class__.__name__} {self.id} "{self.titre if hasattr(self, "titre") else ""}">' @@ -34,11 +51,16 @@ class XMLModel: class ApcReferentielCompetences(db.Model, XMLModel): id = db.Column(db.Integer, primary_key=True) dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True) + annexe = db.Column(db.Text()) specialite = db.Column(db.Text()) specialite_long = db.Column(db.Text()) type_titre = db.Column(db.Text()) + type_structure = db.Column(db.Text()) + type_departement = db.Column(db.Text()) # "secondaire", "tertiaire" + version_orebut = db.Column(db.Text()) _xml_attribs = { # Orébut xml attrib : attribute "type": "type_titre", + "version": "version_orebut", } # ScoDoc specific fields: scodoc_date_loaded = db.Column(db.DateTime, default=datetime.utcnow) @@ -64,9 +86,13 @@ class ApcReferentielCompetences(db.Model, XMLModel): """ return { "dept_id": self.dept_id, + "annexe": self.annexe, "specialite": self.specialite, "specialite_long": self.specialite_long, + "type_structure": self.type_structure, + "type_departement": self.type_departement, "type_titre": self.type_titre, + "version_orebut": self.version_orebut, "scodoc_date_loaded": self.scodoc_date_loaded.isoformat() + "Z" if self.scodoc_date_loaded else "", @@ -88,12 +114,14 @@ class ApcCompetence(db.Model, XMLModel): referentiel_id = db.Column( db.Integer, db.ForeignKey("apc_referentiel_competences.id"), nullable=False ) + id_orebut = db.Column(db.Integer, nullable=True, index=True, unique=True) titre = db.Column(db.Text(), nullable=False, index=True) titre_long = db.Column(db.Text()) couleur = db.Column(db.Text()) numero = db.Column(db.Integer) # ordre de présentation _xml_attribs = { # xml_attrib : attribute - "name": "titre", + "id": "id_orebut", + "nom_court": "titre", # was name "libelle_long": "titre_long", } situations = db.relationship( @@ -117,6 +145,7 @@ class ApcCompetence(db.Model, XMLModel): def to_dict(self): return { + "id_orebut": self.id_orebut, "titre": self.titre, "titre_long": self.titre_long, "couleur": self.couleur, diff --git a/app/views/refcomp.py b/app/views/refcomp.py index a144b3904..4bfb5e959 100644 --- a/app/views/refcomp.py +++ b/app/views/refcomp.py @@ -172,11 +172,13 @@ def refcomp_load(formation_id=None): filename = secure_filename(f.filename) try: xml_data = f.read() - ref = orebut_import_refcomp( + _ = orebut_import_refcomp( xml_data, dept_id=g.scodoc_dept_id, orig_filename=filename ) except TypeError as exc: - raise ScoFormatError("fichier XML Orébut invalide") from exc + raise ScoFormatError( + f"fichier XML Orébut invalide (1): {exc.args}" + ) from exc except ScoFormatError: raise