From 79f07deac0ce6f9315baa871bcd804faeceb5c97 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 26 Jan 2024 14:57:50 +0100 Subject: [PATCH] =?UTF-8?q?Clonage=20formation:=20conserve=20ue=5Fcode,=20?= =?UTF-8?q?m=C3=AAme=20si=20vide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/ues.py | 23 +++++++++++++++++++++-- app/scodoc/notesdb.py | 5 ++++- app/scodoc/sco_edit_ue.py | 27 ++++++++++++++------------- app/scodoc/sco_formation_versions.py | 16 +++++++--------- app/scodoc/sco_formations.py | 5 ++++- app/views/notes.py | 1 - 6 files changed, 50 insertions(+), 27 deletions(-) diff --git a/app/models/ues.py b/app/models/ues.py index 1daf1273..c51d83ae 100644 --- a/app/models/ues.py +++ b/app/models/ues.py @@ -5,6 +5,7 @@ from flask import g import pandas as pd from app import db, log +from app import models from app.models import APO_CODE_STR_LEN from app.models import SHORT_STR_LEN from app.models.but_refcomp import ApcNiveau, ApcParcours @@ -12,7 +13,7 @@ from app.models.modules import Module from app.scodoc import sco_utils as scu -class UniteEns(db.Model): +class UniteEns(models.ScoDocModel): """Unité d'Enseignement (UE)""" __tablename__ = "notes_ue" @@ -81,7 +82,7 @@ class UniteEns(db.Model): 'EXTERNE' if self.is_external else ''})>""" def clone(self): - """Create a new copy of this ue. + """Create a new copy of this ue, add to session. Ne copie pas le code, ni le code Apogée, ni les liens au réf. de comp. (parcours et niveau). """ @@ -100,8 +101,26 @@ class UniteEns(db.Model): coef_rcue=self.coef_rcue, color=self.color, ) + db.session.add(ue) return ue + @classmethod + def convert_dict_fields(cls, args: dict) -> dict: + """Convert fields from the given dict to model's attributes values. No side effect. + + args: dict with args in application. + returns: dict to store in model's db. + """ + args = args.copy() + if "type" in args: + args["type"] = int(args["type"] or 0) + if "is_external" in args: + args["is_external"] = scu.to_bool(args["is_external"]) + if "ects" in args: + args["ects"] = float(args["ects"]) + + return args + def to_dict(self, convert_objects=False, with_module_ue_coefs=True): """as a dict, with the same conversions as in ScoDoc7. If convert_objects, convert all attributes to native types diff --git a/app/scodoc/notesdb.py b/app/scodoc/notesdb.py index 3aba1848..57f03bd3 100644 --- a/app/scodoc/notesdb.py +++ b/app/scodoc/notesdb.py @@ -300,6 +300,7 @@ class EditableTable(object): output_formators={}, input_formators={}, aux_tables=[], + convert_empty_to_nulls=True, # les arguments vides sont traduits en NULL convert_null_outputs_to_empty=True, html_quote=False, # changed in 9.0.10 fields_creators={}, # { field : [ sql_command_to_create_it ] } @@ -321,6 +322,7 @@ class EditableTable(object): self.output_formators = output_formators self.input_formators = input_formators self.convert_null_outputs_to_empty = convert_null_outputs_to_empty + self.convert_empty_to_nulls = convert_empty_to_nulls self.html_quote = html_quote self.fields_creators = fields_creators self.filter_nulls = filter_nulls @@ -351,6 +353,7 @@ class EditableTable(object): self.table_name, vals, commit=True, + convert_empty_to_nulls=self.convert_empty_to_nulls, return_id=(self.id_name is not None), ignore_conflicts=self.insert_ignore_conflicts, ) @@ -444,7 +447,7 @@ def dictfilter(d, fields, filter_nulls=True): """returns a copy of d with only keys listed in "fields" and non null values""" r = {} for f in fields: - if f in d and (d[f] != None or not filter_nulls): + if f in d and (d[f] is not None or not filter_nulls): try: val = d[f].strip() except: diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 06975ba3..f33eeacd 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -89,6 +89,7 @@ _ueEditor = ndb.EditableTable( "color", "niveau_competence_id", ), + convert_empty_to_nulls=False, # necessaire pour ue_code == "" sortkey="numero", input_formators={ "type": ndb.int_null_is_zero, @@ -110,7 +111,7 @@ def ue_list(*args, **kw): return _ueEditor.list(cnx, *args, **kw) -def do_ue_create(args): +def do_ue_create(args, allow_empty_ue_code=False): "create an ue" cnx = ndb.GetDBConnexion() # check duplicates @@ -120,18 +121,18 @@ def do_ue_create(args): f"""Acronyme d'UE "{args['acronyme']}" déjà utilisé ! (chaque UE doit avoir un acronyme unique dans la formation)""" ) - if ( - (not "ue_code" in args) - or (args["ue_code"] is None) - or (not args["ue_code"].strip()) - ): - # évite les conflits de code - while True: - cursor = db.session.execute(sa.text("select notes_newid_ucod();")) - code = cursor.fetchone()[0] - if UniteEns.query.filter_by(ue_code=code).count() == 0: - break - args["ue_code"] = code + if "ue_code" not in args or args["ue_code"] is None or not args["ue_code"].strip(): + if allow_empty_ue_code: + args["ue_code"] = "" + else: + # évite les conflits: génère nouveau ue_code + while True: + cursor = db.session.execute(sa.text("select notes_newid_ucod();")) + code = cursor.fetchone()[0] + if UniteEns.query.filter_by(ue_code=code).count() == 0: + break + args["ue_code"] = code + # create ue_id = _ueEditor.create(cnx, args) log(f"do_ue_create: created {ue_id} with {args}") diff --git a/app/scodoc/sco_formation_versions.py b/app/scodoc/sco_formation_versions.py index 8c74ad25..8fbf3931 100644 --- a/app/scodoc/sco_formation_versions.py +++ b/app/scodoc/sco_formation_versions.py @@ -168,16 +168,14 @@ def formsemestre_associate_new_version( formation_id=new_formation_id, ) ) - else: - return flask.redirect( - url_for( - "notes.formsemestre_status", - scodoc_dept=g.scodoc_dept, - formsemestre_id=formsemestre_id, - ) + return flask.redirect( + url_for( + "notes.formsemestre_status", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre_id, ) - else: - raise ScoValueError("Méthode invalide") + ) + raise ScoValueError("Méthode invalide") def do_formsemestres_associate_new_version( diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py index 1939d7f1..b0090819 100644 --- a/app/scodoc/sco_formations.py +++ b/app/scodoc/sco_formations.py @@ -117,6 +117,7 @@ def formation_export_dict( ues = ues.all() ues.sort(key=lambda u: (u.semestre_idx or 0, u.numero or 0, u.acronyme)) f_dict["ue"] = [] + ue: UniteEns for ue in ues: ue_dict = ue.to_dict() f_dict["ue"].append(ue_dict) @@ -363,7 +364,9 @@ def formation_import_xml(doc: str, import_tags=True, use_local_refcomp=False): 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]) + # Note: si le code est indiqué "" dans le xml, il faut le conserver vide + # pour la comparaison ultérieure des formations XXX + ue_id = sco_edit_ue.do_ue_create(ue_info[1], allow_empty_ue_code=True) ue: UniteEns = db.session.get(UniteEns, ue_id) assert ue if xml_ue_id: diff --git a/app/views/notes.py b/app/views/notes.py index 43ea7933..7544e50f 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -824,7 +824,6 @@ def ue_clone(): ue_id = int(request.form.get("ue_id")) ue = UniteEns.query.get_or_404(ue_id) ue2 = ue.clone() - db.session.add(ue2) db.session.commit() flash(f"UE {ue.acronyme} dupliquée") return flask.redirect(