############################################################################## # ScoDoc # Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """ScoDoc 9 models : Référentiel Compétence BUT 2021 """ from datetime import datetime from enum import unique from typing import Any from app import db from app.scodoc.sco_utils import ModuleType class XMLModel: _xml_attribs = {} # to be overloaded id = "_" @classmethod def attr_from_xml(cls, args: dict) -> dict: """dict with attributes imported from Orébut XML and renamed for our models. The mapping is specified by the _xml_attribs attribute in each model class. """ return {cls._xml_attribs.get(k, k): v for (k, v) in args.items()} def __repr__(self): return f'<{self.__class__.__name__} {self.id} "{self.titre if hasattr(self, "titre") else ""}">' 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) specialite = db.Column(db.Text()) specialite_long = db.Column(db.Text()) type_titre = db.Column(db.Text()) _xml_attribs = { # Orébut xml attrib : attribute "type": "type_titre", } # ScoDoc specific fields: scodoc_date_loaded = db.Column(db.DateTime, default=datetime.utcnow) scodoc_orig_filename = db.Column(db.Text()) # Relations: competences = db.relationship( "ApcCompetence", backref="referentiel", lazy="dynamic", cascade="all, delete-orphan", ) parcours = db.relationship( "ApcParcours", backref="referentiel", lazy="dynamic", cascade="all, delete-orphan", ) formations = db.relationship("Formation", backref="referentiel_competence") def to_dict(self): """Représentation complète du ref. de comp. comme un dict. """ return { "dept_id": self.dept_id, "specialite": self.specialite, "specialite_long": self.specialite_long, "type_titre": self.type_titre, "scodoc_date_loaded": self.scodoc_date_loaded.isoformat() + "Z" if self.scodoc_date_loaded else "", "scodoc_orig_filename": self.scodoc_orig_filename, "competences": {x.titre: x.to_dict() for x in self.competences}, "parcours": {x.code: x.to_dict() for x in self.parcours}, } class ApcCompetence(db.Model, XMLModel): __table_args__ = ( # les compétences dans Orébut sont identifiées par leur "titre" # unique au sein d'un référentiel: db.UniqueConstraint( "referentiel_id", "titre", name="apc_competence_referentiel_id_titre_key" ), ) id = db.Column(db.Integer, primary_key=True) referentiel_id = db.Column( db.Integer, db.ForeignKey("apc_referentiel_competences.id"), nullable=False ) 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", "libelle_long": "titre_long", } situations = db.relationship( "ApcSituationPro", backref="competence", lazy="dynamic", cascade="all, delete-orphan", ) composantes_essentielles = db.relationship( "ApcComposanteEssentielle", backref="competence", lazy="dynamic", cascade="all, delete-orphan", ) niveaux = db.relationship( "ApcNiveau", backref="competence", lazy="dynamic", cascade="all, delete-orphan", ) def to_dict(self): return { "titre": self.titre, "titre_long": self.titre_long, "couleur": self.couleur, "numero": self.numero, "situations": [x.to_dict() for x in self.situations], "composantes_essentielles": [ x.to_dict() for x in self.composantes_essentielles ], "niveaux": {x.annee: x.to_dict() for x in self.niveaux}, } class ApcSituationPro(db.Model, XMLModel): id = db.Column(db.Integer, primary_key=True) competence_id = db.Column( db.Integer, db.ForeignKey("apc_competence.id"), nullable=False ) libelle = db.Column(db.Text(), nullable=False) # aucun attribut (le text devient le libellé) def to_dict(self): return {"libelle": self.libelle} class ApcComposanteEssentielle(db.Model, XMLModel): id = db.Column(db.Integer, primary_key=True) competence_id = db.Column( db.Integer, db.ForeignKey("apc_competence.id"), nullable=False ) libelle = db.Column(db.Text(), nullable=False) def to_dict(self): return {"libelle": self.libelle} class ApcNiveau(db.Model, XMLModel): id = db.Column(db.Integer, primary_key=True) competence_id = db.Column( db.Integer, db.ForeignKey("apc_competence.id"), nullable=False ) libelle = db.Column(db.Text(), nullable=False) annee = db.Column(db.Text(), nullable=False) # "BUT2" # L'ordre est l'année d'apparition de ce niveau ordre = db.Column(db.Integer, nullable=False) # 1, 2, 3 app_critiques = db.relationship( "ApcAppCritique", backref="niveau", lazy="dynamic", cascade="all, delete-orphan", ) def to_dict(self): return { "libelle": self.libelle, "annee": self.annee, "ordre": self.ordre, "app_critiques": {x.code: x.to_dict() for x in self.app_critiques}, } class ApcAppCritique(db.Model, XMLModel): "Apprentissage Critique BUT" id = db.Column(db.Integer, primary_key=True) niveau_id = db.Column(db.Integer, db.ForeignKey("apc_niveau.id"), nullable=False) code = db.Column(db.Text(), nullable=False, index=True) libelle = db.Column(db.Text()) modules = db.relationship( "Module", secondary="apc_modules_acs", lazy="dynamic", backref=db.backref("app_critiques", lazy="dynamic"), ) def to_dict(self): return {"libelle": self.libelle} def get_label(self): return self.code + " - " + self.titre def __repr__(self): return "".format(self.code) def get_saes(self): """Liste des SAE associées""" return [m for m in self.modules if m.module_type == ModuleType.SAE] ApcAppCritiqueModules = db.Table( "apc_modules_acs", db.Column("module_id", db.ForeignKey("notes_modules.id")), db.Column("app_crit_id", db.ForeignKey("apc_app_critique.id")), ) class ApcParcours(db.Model, XMLModel): id = db.Column(db.Integer, primary_key=True) referentiel_id = db.Column( db.Integer, db.ForeignKey("apc_referentiel_competences.id"), nullable=False ) numero = db.Column(db.Integer) # ordre de présentation code = db.Column(db.Text(), nullable=False) libelle = db.Column(db.Text(), nullable=False) annees = db.relationship( "ApcAnneeParcours", backref="parcours", lazy="dynamic", cascade="all, delete-orphan", ) def to_dict(self): return { "code": self.code, "numero": self.numero, "libelle": self.libelle, "annees": {x.ordre: x.to_dict() for x in self.annees}, } class ApcAnneeParcours(db.Model, XMLModel): id = db.Column(db.Integer, primary_key=True) parcours_id = db.Column( db.Integer, db.ForeignKey("apc_parcours.id"), nullable=False ) ordre = db.Column(db.Integer) def to_dict(self): return { "ordre": self.ordre, "competences": { x.competence.titre: {"niveau": x.niveau} for x in self.niveaux_competences }, } class ApcParcoursNiveauCompetence(db.Model): """Association entre année de parcours et compétence. Le "niveau" de la compétence est donné ici (convention Orébut) """ competence_id = db.Column( db.Integer, db.ForeignKey("apc_competence.id", ondelete="CASCADE"), primary_key=True, ) annee_parcours_id = db.Column( db.Integer, db.ForeignKey("apc_annee_parcours.id", ondelete="CASCADE"), primary_key=True, ) niveau = db.Column(db.Integer, nullable=False) # 1, 2, 3 competence = db.relationship( ApcCompetence, backref=db.backref( "annee_parcours", passive_deletes=True, cascade="save-update, merge, delete, delete-orphan", ), ) annee_parcours = db.relationship( ApcAnneeParcours, backref=db.backref( "niveaux_competences", passive_deletes=True, cascade="save-update, merge, delete, delete-orphan", ), )