forked from ScoDoc/ScoDoc
Import formations / ref. comp. : fixes #510
This commit is contained in:
parent
a92d2a6edf
commit
dac46b8366
@ -61,12 +61,19 @@ class Formation(db.Model):
|
|||||||
"titre complet pour affichage"
|
"titre complet pour affichage"
|
||||||
return f"""Formation {self.titre} ({self.acronyme}) [version {self.version}] code {self.formation_code}"""
|
return f"""Formation {self.titre} ({self.acronyme}) [version {self.version}] code {self.formation_code}"""
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self, with_refcomp_attrs=False):
|
||||||
|
""" "as a dict.
|
||||||
|
Si with_refcomp_attrs, ajoute attributs permettant de retrouver le ref. de comp.
|
||||||
|
"""
|
||||||
e = dict(self.__dict__)
|
e = dict(self.__dict__)
|
||||||
e.pop("_sa_instance_state", None)
|
e.pop("_sa_instance_state", None)
|
||||||
e["departement"] = self.departement.to_dict()
|
e["departement"] = self.departement.to_dict()
|
||||||
# ScoDoc7 output_formators: (backward compat)
|
e["formation_id"] = self.id # ScoDoc7 backward compat
|
||||||
e["formation_id"] = self.id
|
if with_refcomp_attrs and self.referentiel_competence:
|
||||||
|
e["refcomp_version_orebut"] = self.referentiel_competence.version_orebut
|
||||||
|
e["refcomp_specialite"] = self.referentiel_competence.specialite
|
||||||
|
e["refcomp_type_titre"] = self.referentiel_competence.type_titre
|
||||||
|
|
||||||
return e
|
return e
|
||||||
|
|
||||||
def get_parcours(self):
|
def get_parcours(self):
|
||||||
|
@ -45,6 +45,7 @@ from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
|||||||
from app.scodoc.sco_exceptions import ScoValueError, ScoNonEmptyFormationObject
|
from app.scodoc.sco_exceptions import ScoValueError, ScoNonEmptyFormationObject
|
||||||
|
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc import sco_codes_parcours
|
from app.scodoc import sco_codes_parcours
|
||||||
from app.scodoc import sco_edit_ue
|
from app.scodoc import sco_edit_ue
|
||||||
from app.scodoc import sco_formations
|
from app.scodoc import sco_formations
|
||||||
@ -117,15 +118,16 @@ def do_formation_delete(formation_id):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Suppression des modules
|
with sco_cache.DeferredSemCacheManager():
|
||||||
for module in formation.modules:
|
# Suppression des modules
|
||||||
db.session.delete(module)
|
for module in formation.modules:
|
||||||
db.session.flush()
|
db.session.delete(module)
|
||||||
# Suppression des UEs
|
db.session.flush()
|
||||||
for ue in formation.ues:
|
# Suppression des UEs
|
||||||
sco_edit_ue.do_ue_delete(ue, force=True)
|
for ue in formation.ues:
|
||||||
|
sco_edit_ue.do_ue_delete(ue, force=True)
|
||||||
|
|
||||||
db.session.delete(formation)
|
db.session.delete(formation)
|
||||||
|
|
||||||
# news
|
# news
|
||||||
ScolarNews.add(
|
ScolarNews.add(
|
||||||
|
@ -33,7 +33,6 @@ import xml.dom.minidom
|
|||||||
import flask
|
import flask
|
||||||
from flask import flash, g, url_for, request
|
from flask import flash, g, url_for, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from app.models.but_refcomp import ApcParcours
|
|
||||||
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
@ -41,10 +40,13 @@ from app import db
|
|||||||
from app import log
|
from app import log
|
||||||
from app.models import Formation, Module
|
from app.models import Formation, Module
|
||||||
from app.models import ScolarNews
|
from app.models import ScolarNews
|
||||||
|
from app.models.but_refcomp import ApcParcours, ApcReferentielCompetences
|
||||||
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc import sco_codes_parcours
|
from app.scodoc import sco_codes_parcours
|
||||||
from app.scodoc import sco_edit_matiere
|
from app.scodoc import sco_edit_matiere
|
||||||
from app.scodoc import sco_edit_module
|
from app.scodoc import sco_edit_module
|
||||||
from app.scodoc import sco_edit_ue
|
from app.scodoc import sco_edit_ue
|
||||||
|
from app.scodoc import sco_formations
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_formsemestre
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc import sco_tag_module
|
from app.scodoc import sco_tag_module
|
||||||
@ -107,7 +109,7 @@ def formation_export(
|
|||||||
in desired format
|
in desired format
|
||||||
"""
|
"""
|
||||||
formation: Formation = Formation.query.get_or_404(formation_id)
|
formation: Formation = Formation.query.get_or_404(formation_id)
|
||||||
F = formation.to_dict()
|
F = formation.to_dict(with_refcomp_attrs=True)
|
||||||
selector = {"formation_id": formation_id}
|
selector = {"formation_id": formation_id}
|
||||||
if not export_external_ues:
|
if not export_external_ues:
|
||||||
selector["is_external"] = False
|
selector["is_external"] = False
|
||||||
@ -174,7 +176,7 @@ def formation_export(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def formation_import_xml(doc: str, import_tags=True):
|
def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False):
|
||||||
"""Create a formation from XML representation
|
"""Create a formation from XML representation
|
||||||
(format dumped by formation_export( format='xml' ))
|
(format dumped by formation_export( format='xml' ))
|
||||||
XML may contain object (UE, modules) ids: this function returns two
|
XML may contain object (UE, modules) ids: this function returns two
|
||||||
@ -183,13 +185,12 @@ def formation_import_xml(doc: str, import_tags=True):
|
|||||||
Args:
|
Args:
|
||||||
doc: str, xml data
|
doc: str, xml data
|
||||||
import_tags: if false, does not import tags on modules.
|
import_tags: if false, does not import tags on modules.
|
||||||
|
use_local_refcomp: if True, utilise les id vers les ref. de compétences.
|
||||||
Returns:
|
Returns:
|
||||||
formation_id, modules_old2new, ues_old2new
|
formation_id, modules_old2new, ues_old2new
|
||||||
"""
|
"""
|
||||||
from app.scodoc import sco_edit_formation
|
from app.scodoc import sco_edit_formation
|
||||||
|
|
||||||
# log("formation_import_xml: doc=%s" % doc)
|
|
||||||
try:
|
try:
|
||||||
dom = xml.dom.minidom.parseString(doc)
|
dom = xml.dom.minidom.parseString(doc)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@ -207,104 +208,130 @@ def formation_import_xml(doc: str, import_tags=True):
|
|||||||
assert D[0] == "formation"
|
assert D[0] == "formation"
|
||||||
F = D[1]
|
F = D[1]
|
||||||
F["dept_id"] = g.scodoc_dept_id
|
F["dept_id"] = g.scodoc_dept_id
|
||||||
referentiel_competence_id = F.get("referentiel_competence_id")
|
# Pour les clonages, on prend le refcomp_id donné:
|
||||||
# find new version number
|
referentiel_competence_id = (
|
||||||
cnx = ndb.GetDBConnexion()
|
F.get("referentiel_competence_id") if use_local_refcomp else None
|
||||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
|
||||||
cursor.execute(
|
|
||||||
"""SELECT max(version)
|
|
||||||
FROM notes_formations
|
|
||||||
WHERE acronyme=%(acronyme)s and titre=%(titre)s and dept_id=%(dept_id)s
|
|
||||||
""",
|
|
||||||
F,
|
|
||||||
)
|
)
|
||||||
res = cursor.fetchall()
|
# Sinon, on cherche a retrouver le ref. comp.
|
||||||
try:
|
if referentiel_competence_id is None:
|
||||||
version = int(res[0][0]) + 1
|
refcomp_version_orebut = F.get("refcomp_version_orebut")
|
||||||
except (ValueError, IndexError, TypeError):
|
refcomp_specialite = F.get("refcomp_specialite")
|
||||||
version = 1
|
refcomp_type_titre = F.get("refcomp_type_titre")
|
||||||
F["version"] = version
|
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
|
||||||
|
# find new version number
|
||||||
|
formations = sco_formations.formation_list(
|
||||||
|
args={
|
||||||
|
"acronyme": F["acronyme"],
|
||||||
|
"titre": F["titre"],
|
||||||
|
"dept_id": F["dept_id"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if formations:
|
||||||
|
version = max(f["version"] or 0 for f in formations)
|
||||||
|
else:
|
||||||
|
version = 0
|
||||||
|
F["version"] = version + 1
|
||||||
|
|
||||||
# create formation
|
# create formation
|
||||||
# F_unquoted = F.copy()
|
|
||||||
# unescape_html_dict(F_unquoted)
|
|
||||||
formation_id = sco_edit_formation.do_formation_create(F)
|
formation_id = sco_edit_formation.do_formation_create(F)
|
||||||
log(f"formation {formation_id} created")
|
log(f"formation {formation_id} created")
|
||||||
|
|
||||||
ues_old2new = {} # xml ue_id : new ue_id
|
ues_old2new = {} # xml ue_id : new ue_id
|
||||||
modules_old2new = {} # xml module_id : new module_id
|
modules_old2new = {} # xml module_id : new module_id
|
||||||
# (nb: mecanisme utilise pour cloner semestres seulement, pas pour I/O XML)
|
# (nb: mecanisme utilise pour cloner semestres seulement, pas pour I/O XML)
|
||||||
|
|
||||||
ue_reference_to_id = {} # pour les coefs APC (map reference -> ue_id)
|
ue_reference_to_id = {} # pour les coefs APC (map reference -> ue_id)
|
||||||
modules_a_coefficienter = [] # Liste des modules avec coefs APC
|
modules_a_coefficienter = [] # Liste des modules avec coefs APC
|
||||||
# -- create UEs
|
with sco_cache.DeferredSemCacheManager():
|
||||||
for ue_info in D[2]:
|
# -- create UEs
|
||||||
assert ue_info[0] == "ue"
|
for ue_info in D[2]:
|
||||||
ue_info[1]["formation_id"] = formation_id
|
assert ue_info[0] == "ue"
|
||||||
if "ue_id" in ue_info[1]:
|
ue_info[1]["formation_id"] = formation_id
|
||||||
xml_ue_id = int(ue_info[1]["ue_id"])
|
if "ue_id" in ue_info[1]:
|
||||||
del ue_info[1]["ue_id"]
|
xml_ue_id = int(ue_info[1]["ue_id"])
|
||||||
else:
|
del ue_info[1]["ue_id"]
|
||||||
xml_ue_id = None
|
else:
|
||||||
ue_id = sco_edit_ue.do_ue_create(ue_info[1])
|
xml_ue_id = None
|
||||||
if xml_ue_id:
|
ue_id = sco_edit_ue.do_ue_create(ue_info[1])
|
||||||
ues_old2new[xml_ue_id] = ue_id
|
if xml_ue_id:
|
||||||
# élément optionnel présent dans les exports BUT:
|
ues_old2new[xml_ue_id] = ue_id
|
||||||
ue_reference = ue_info[1].get("reference")
|
# élément optionnel présent dans les exports BUT:
|
||||||
if ue_reference:
|
ue_reference = ue_info[1].get("reference")
|
||||||
ue_reference_to_id[int(ue_reference)] = ue_id
|
if ue_reference:
|
||||||
# -- create matieres
|
ue_reference_to_id[int(ue_reference)] = ue_id
|
||||||
for mat_info in ue_info[2]:
|
# -- create matieres
|
||||||
assert mat_info[0] == "matiere"
|
for mat_info in ue_info[2]:
|
||||||
mat_info[1]["ue_id"] = ue_id
|
assert mat_info[0] == "matiere"
|
||||||
mat_id = sco_edit_matiere.do_matiere_create(mat_info[1])
|
mat_info[1]["ue_id"] = ue_id
|
||||||
# -- create modules
|
mat_id = sco_edit_matiere.do_matiere_create(mat_info[1])
|
||||||
for mod_info in mat_info[2]:
|
# -- create modules
|
||||||
assert mod_info[0] == "module"
|
for mod_info in mat_info[2]:
|
||||||
if "module_id" in mod_info[1]:
|
assert mod_info[0] == "module"
|
||||||
xml_module_id = int(mod_info[1]["module_id"])
|
if "module_id" in mod_info[1]:
|
||||||
del mod_info[1]["module_id"]
|
xml_module_id = int(mod_info[1]["module_id"])
|
||||||
else:
|
del mod_info[1]["module_id"]
|
||||||
xml_module_id = None
|
else:
|
||||||
mod_info[1]["formation_id"] = formation_id
|
xml_module_id = None
|
||||||
mod_info[1]["matiere_id"] = mat_id
|
mod_info[1]["formation_id"] = formation_id
|
||||||
mod_info[1]["ue_id"] = ue_id
|
mod_info[1]["matiere_id"] = mat_id
|
||||||
if not "module_type" in mod_info[1]:
|
mod_info[1]["ue_id"] = ue_id
|
||||||
mod_info[1]["module_type"] = scu.ModuleType.STANDARD
|
if not "module_type" in mod_info[1]:
|
||||||
mod_id = sco_edit_module.do_module_create(mod_info[1])
|
mod_info[1]["module_type"] = scu.ModuleType.STANDARD
|
||||||
if xml_module_id:
|
mod_id = sco_edit_module.do_module_create(mod_info[1])
|
||||||
modules_old2new[int(xml_module_id)] = mod_id
|
if xml_module_id:
|
||||||
if len(mod_info) > 2:
|
modules_old2new[int(xml_module_id)] = mod_id
|
||||||
module: Module = Module.query.get(mod_id)
|
if len(mod_info) > 2:
|
||||||
tag_names = []
|
module: Module = Module.query.get(mod_id)
|
||||||
ue_coef_dict = {}
|
tag_names = []
|
||||||
for child in mod_info[2]:
|
ue_coef_dict = {}
|
||||||
if child[0] == "tags" and import_tags:
|
for child in mod_info[2]:
|
||||||
tag_names.append(child[1]["name"])
|
if child[0] == "tags" and import_tags:
|
||||||
elif child[0] == "coefficients":
|
tag_names.append(child[1]["name"])
|
||||||
ue_reference = int(child[1]["ue_reference"])
|
elif child[0] == "coefficients":
|
||||||
coef = float(child[1]["coef"])
|
ue_reference = int(child[1]["ue_reference"])
|
||||||
ue_coef_dict[ue_reference] = coef
|
coef = float(child[1]["coef"])
|
||||||
elif child[0] == "parcours":
|
ue_coef_dict[ue_reference] = coef
|
||||||
# associe les parcours de ce module (BUT)
|
elif child[0] == "parcours":
|
||||||
code_parcours = child[1]["code"]
|
# Si on a un référentiel de compétences,
|
||||||
parcours = ApcParcours.query.filter_by(
|
# associe les parcours de ce module (BUT)
|
||||||
code=code_parcours,
|
if referentiel_competence_id:
|
||||||
referentiel_id=referentiel_competence_id,
|
code_parcours = child[1]["code"]
|
||||||
).first()
|
parcours = ApcParcours.query.filter_by(
|
||||||
if parcours:
|
code=code_parcours,
|
||||||
module.parcours.append(parcours)
|
referentiel_id=referentiel_competence_id,
|
||||||
db.session.add(module)
|
).first()
|
||||||
else:
|
if parcours:
|
||||||
log(f"Warning: parcours {code_parcours} inexistant !")
|
module.parcours.append(parcours)
|
||||||
if import_tags and tag_names:
|
db.session.add(module)
|
||||||
sco_tag_module.module_tag_set(mod_id, tag_names)
|
else:
|
||||||
if module.is_apc() and ue_coef_dict:
|
log(
|
||||||
modules_a_coefficienter.append((module, ue_coef_dict))
|
f"Warning: parcours {code_parcours} inexistant !"
|
||||||
# Fixe les coefs APC (à la fin pour que les UE soient crées)
|
)
|
||||||
for module, ue_coef_dict_ref in modules_a_coefficienter:
|
if import_tags and tag_names:
|
||||||
# remap ue ids:
|
sco_tag_module.module_tag_set(mod_id, tag_names)
|
||||||
ue_coef_dict = {ue_reference_to_id[k]: v for (k, v) in ue_coef_dict_ref.items()}
|
if module.is_apc() and ue_coef_dict:
|
||||||
module.set_ue_coef_dict(ue_coef_dict)
|
modules_a_coefficienter.append((module, ue_coef_dict))
|
||||||
db.session.commit()
|
# Fixe les coefs APC (à la fin pour que les UE soient créées)
|
||||||
|
for module, ue_coef_dict_ref in modules_a_coefficienter:
|
||||||
|
# remap ue ids:
|
||||||
|
ue_coef_dict = {
|
||||||
|
ue_reference_to_id[k]: v for (k, v) in ue_coef_dict_ref.items()
|
||||||
|
}
|
||||||
|
module.set_ue_coef_dict(ue_coef_dict)
|
||||||
|
db.session.commit()
|
||||||
return formation_id, modules_old2new, ues_old2new
|
return formation_id, modules_old2new, ues_old2new
|
||||||
|
|
||||||
|
|
||||||
@ -448,7 +475,9 @@ def formation_create_new_version(formation_id, redirect=True):
|
|||||||
formation = Formation.query.get_or_404(formation_id)
|
formation = Formation.query.get_or_404(formation_id)
|
||||||
resp = formation_export(formation_id, export_ids=True, format="xml")
|
resp = formation_export(formation_id, export_ids=True, format="xml")
|
||||||
xml_data = resp.get_data(as_text=True)
|
xml_data = resp.get_data(as_text=True)
|
||||||
new_id, modules_old2new, ues_old2new = formation_import_xml(xml_data)
|
new_id, modules_old2new, ues_old2new = formation_import_xml(
|
||||||
|
xml_data, use_local_refcomp=True
|
||||||
|
)
|
||||||
# news
|
# news
|
||||||
ScolarNews.add(
|
ScolarNews.add(
|
||||||
typ=ScolarNews.NEWS_FORM,
|
typ=ScolarNews.NEWS_FORM,
|
||||||
|
@ -1018,6 +1018,7 @@ a.discretelink:hover {
|
|||||||
|
|
||||||
.help {
|
.help {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.help_important {
|
.help_important {
|
||||||
|
@ -652,14 +652,6 @@ def formation_export(formation_id, export_ids=False, format=None):
|
|||||||
@scodoc7func
|
@scodoc7func
|
||||||
def formation_import_xml_form():
|
def formation_import_xml_form():
|
||||||
"form import d'une formation en XML"
|
"form import d'une formation en XML"
|
||||||
H = [
|
|
||||||
html_sco_header.sco_header(page_title="Import d'une formation"),
|
|
||||||
"""<h2>Import d'une formation</h2>
|
|
||||||
<p>Création d'une formation (avec UE, matières, modules)
|
|
||||||
à partir un fichier XML (réservé aux utilisateurs avertis)</p>
|
|
||||||
""",
|
|
||||||
]
|
|
||||||
footer = html_sco_header.sco_footer()
|
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
request.base_url,
|
request.base_url,
|
||||||
scu.get_request_args(),
|
scu.get_request_args(),
|
||||||
@ -668,7 +660,15 @@ def formation_import_xml_form():
|
|||||||
cancelbutton="Annuler",
|
cancelbutton="Annuler",
|
||||||
)
|
)
|
||||||
if tf[0] == 0:
|
if tf[0] == 0:
|
||||||
return "\n".join(H) + tf[1] + footer
|
return f"""
|
||||||
|
{ html_sco_header.sco_header(page_title="Import d'une formation") }
|
||||||
|
<h2>Import d'une formation</h2>
|
||||||
|
<p>Création d'une formation (avec UE, matières, modules)
|
||||||
|
à partir un fichier XML (réservé aux utilisateurs avertis)
|
||||||
|
</p>
|
||||||
|
{ tf[1] }
|
||||||
|
{ html_sco_header.sco_footer() }
|
||||||
|
"""
|
||||||
elif tf[0] == -1:
|
elif tf[0] == -1:
|
||||||
return flask.redirect(scu.NotesURL())
|
return flask.redirect(scu.NotesURL())
|
||||||
else:
|
else:
|
||||||
@ -676,13 +676,14 @@ def formation_import_xml_form():
|
|||||||
tf[2]["xmlfile"].read()
|
tf[2]["xmlfile"].read()
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return f"""
|
||||||
"\n".join(H)
|
{ html_sco_header.sco_header(page_title="Import d'une formation") }
|
||||||
+ """<p>Import effectué !</p>
|
<h2>Import effectué !</h2>
|
||||||
<p><a class="stdlink" href="ue_list?formation_id=%s">Voir la formation</a></p>"""
|
<p><a class="stdlink" href="{
|
||||||
% formation_id
|
url_for("notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=formation_id
|
||||||
+ footer
|
)}">Voir la formation</a></p>
|
||||||
)
|
{ html_sco_header.sco_footer() }
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
sco_publish(
|
sco_publish(
|
||||||
|
Loading…
Reference in New Issue
Block a user