Améliorer édition et clonage des formations.

This commit is contained in:
Emmanuel Viennet 2023-11-19 22:06:36 +01:00
parent ce3452df73
commit 093ab253f3
5 changed files with 59 additions and 14 deletions

View File

@ -1040,6 +1040,33 @@ class FormSemestre(db.Model):
nb_recorded += 1 nb_recorded += 1
return nb_recorded return nb_recorded
def change_formation(self, formation_dest: Formation):
"""Associe ce formsemestre à une autre formation.
Ce n'est possible que si la formation destination possède des modules de
même code que ceux utilisés dans la formation d'origine du formsemestre.
S'il manque un module, l'opération est annulée.
Commit (or rollback) session.
"""
ok = True
for mi in self.modimpls:
dest_modules = formation_dest.modules.filter_by(code=mi.module.code).all()
match len(dest_modules):
case 1:
mi.module = dest_modules[0]
db.session.add(mi)
case 0:
print(f"Argh ! no module found with code={mi.module.code}")
ok = False
case _:
print(f"Arg ! several modules found with code={mi.module.code}")
ok = False
if ok:
self.formation_id = formation_dest.id
db.session.commit()
else:
db.session.rollback()
# Association id des utilisateurs responsables (aka directeurs des etudes) du semestre # Association id des utilisateurs responsables (aka directeurs des etudes) du semestre
notes_formsemestre_responsables = db.Table( notes_formsemestre_responsables = db.Table(

View File

@ -80,7 +80,7 @@ def formation_delete(formation_id=None, dialog_confirmed=False):
f"""<h2>Confirmer la suppression de la formation f"""<h2>Confirmer la suppression de la formation
{formation.titre} ({formation.acronyme}) ? {formation.titre} ({formation.acronyme}) ?
</h2> </h2>
<p><b>Attention:</b> la suppression d'une formation est <b>irréversible</b> <p><b>Attention:</b> la suppression d'une formation est <b>irréversible</b>
et implique la supression de toutes les UE, matières et modules de la formation ! et implique la supression de toutes les UE, matières et modules de la formation !
</p> </p>
""", """,
@ -273,7 +273,8 @@ def formation_edit(formation_id=None, create=False):
"\n".join(H) "\n".join(H)
+ tf_error_message( + tf_error_message(
f"""Valeurs incorrectes: il existe déjà <a href="{ f"""Valeurs incorrectes: il existe déjà <a href="{
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=other_formations[0].id) url_for('notes.ue_table',
scodoc_dept=g.scodoc_dept, formation_id=other_formations[0].id)
}">une formation</a> avec même titre, }">une formation</a> avec même titre,
acronyme et version. acronyme et version.
""" """
@ -285,11 +286,11 @@ def formation_edit(formation_id=None, create=False):
if create: if create:
formation = do_formation_create(tf[2]) formation = do_formation_create(tf[2])
else: else:
do_formation_edit(tf[2]) if do_formation_edit(tf[2]):
flash( flash(
f"""Création de la formation { f"""Modification de la formation {
formation.titre} ({formation.acronyme}) version {formation.version}""" formation.titre} ({formation.acronyme}) version {formation.version}"""
) )
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id
@ -335,8 +336,8 @@ def do_formation_create(args: dict) -> Formation:
return formation return formation
def do_formation_edit(args): def do_formation_edit(args) -> bool:
"edit a formation" "edit a formation, returns True if modified"
# On ne peut jamais supprimer le code formation: # On ne peut jamais supprimer le code formation:
if "formation_code" in args and not args["formation_code"]: if "formation_code" in args and not args["formation_code"]:
@ -350,11 +351,16 @@ def do_formation_edit(args):
if "type_parcours" in args: if "type_parcours" in args:
del args["type_parcours"] del args["type_parcours"]
modified = False
for field in formation.__dict__: for field in formation.__dict__:
if field in args: if field in args:
value = args[field].strip() if isinstance(args[field], str) else args[field] value = args[field].strip() if isinstance(args[field], str) else args[field]
if field and field[0] != "_": if field and field[0] != "_" and getattr(formation, field, None) != value:
setattr(formation, field, value) setattr(formation, field, value)
modified = True
if not modified:
return False
db.session.add(formation) db.session.add(formation)
try: try:
@ -370,6 +376,7 @@ def do_formation_edit(args):
), ),
) from exc ) from exc
formation.invalidate_cached_sems() formation.invalidate_cached_sems()
return True
def module_move(module_id, after=0, redirect=True): def module_move(module_id, after=0, redirect=True):

View File

@ -307,7 +307,7 @@ def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False):
D = sco_xml.xml_to_dicts(f) D = sco_xml.xml_to_dicts(f)
except Exception as exc: except Exception as exc:
raise ScoFormatError( raise ScoFormatError(
"""Ce document xml ne correspond pas à un programme exporté par ScoDoc. """Ce document xml ne correspond pas à un programme exporté par ScoDoc.
(élément 'formation' inexistant par exemple).""" (élément 'formation' inexistant par exemple)."""
) from exc ) from exc
assert D[0] == "formation" assert D[0] == "formation"
@ -322,8 +322,13 @@ def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False):
referentiel_competence_id = _formation_retreive_refcomp(f_dict) referentiel_competence_id = _formation_retreive_refcomp(f_dict)
f_dict["referentiel_competence_id"] = referentiel_competence_id f_dict["referentiel_competence_id"] = referentiel_competence_id
# find new version number # find new version number
acronyme_lower = f_dict["acronyme"].lower if f_dict["acronyme"] else ""
titre_lower = f_dict["titre"].lower if f_dict["titre"] else ""
formations: list[Formation] = Formation.query.filter_by( formations: list[Formation] = Formation.query.filter_by(
acronyme=f_dict["acronyme"], titre=f_dict["titre"], dept_id=f_dict["dept_id"] dept_id=f_dict["dept_id"]
).filter(
db.func.lower(Formation.acronyme) == acronyme_lower,
db.func(Formation.titre) == titre_lower,
) )
if formations.count(): if formations.count():
version = max(f.version or 0 for f in formations) version = max(f.version or 0 for f in formations)
@ -518,6 +523,7 @@ def formation_list_table() -> GenTable:
"_titre_link_class": "stdlink", "_titre_link_class": "stdlink",
"_titre_id": f"""titre-{acronyme_no_spaces}""", "_titre_id": f"""titre-{acronyme_no_spaces}""",
"version": formation.version or 0, "version": formation.version or 0,
"commentaire": formation.commentaire or "",
} }
# Ajoute les semestres associés à chaque formation: # Ajoute les semestres associés à chaque formation:
row["formsemestres"] = formation.formsemestres.order_by( row["formsemestres"] = formation.formsemestres.order_by(
@ -594,10 +600,12 @@ def formation_list_table() -> GenTable:
"formation_code", "formation_code",
"version", "version",
"titre", "titre",
"commentaire",
"sems_list_txt", "sems_list_txt",
) )
titles = { titles = {
"buttons": "", "buttons": "",
"commentaire": "Commentaire",
"acronyme": "Acro.", "acronyme": "Acro.",
"parcours_name": "Type", "parcours_name": "Type",
"titre": "Titre", "titre": "Titre",

View File

@ -2319,7 +2319,10 @@ table.formation_list_table td.buttons span.but_placeholder {
} }
.formation_list_table td.titre { .formation_list_table td.titre {
width: 50%; width: 45%;
}
.formation_list_table td.commentaire {
font-style: italic;
} }
.formation_list_table td.sems_list_txt { .formation_list_table td.sems_list_txt {

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.6.56" SCOVERSION = "9.6.57"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"