From 1375c195ca0f3679a23ed1b246461028b24c0394 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sun, 8 Aug 2021 16:01:10 +0200 Subject: [PATCH] WIP: definition base en SQLAlchemy --- app/__init__.py | 3 --- app/models/formations.py | 28 +++++++++++------------ app/models/groups.py | 1 + app/scodoc/notesdb.py | 35 ++++++++++++++++------------- app/scodoc/sco_cache.py | 12 ++++------ app/scodoc/sco_edit_ue.py | 3 ++- app/scodoc/sco_etud.py | 2 +- app/scodoc/sco_formsemestre_edit.py | 2 +- app/scodoc/sco_groups.py | 6 ++--- app/scodoc/sco_preferences.py | 2 +- app/scodoc/sco_tag_module.py | 15 ++++++++----- 11 files changed, 56 insertions(+), 53 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index c270c4e5..e44391b2 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -128,6 +128,3 @@ def create_app(config_class=Config): app.logger.info("ScoDoc8 startup") return app - - -# from app import models diff --git a/app/models/formations.py b/app/models/formations.py index 9767c1db..a73e6b83 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -17,17 +17,17 @@ class NotesFormation(db.Model): formation_id = db.synonym("id") acronyme = db.Column(db.String(SHORT_STR_LEN), nullable=False) titre = db.Column(db.Text(), nullable=False) + titre_officiel = db.Column(db.Text(), nullable=False) version = db.Column(db.Integer, default=1) - formation_code = db.Column(db.String(SHORT_STR_LEN), nullable=False) + formation_code = db.Column( + db.String(SHORT_STR_LEN), + server_default=db.text("notes_newid_fcod()"), + nullable=False, + ) + # nb: la fonction SQL notes_newid_fcod doit être créée à part type_parcours = db.Column(db.Integer, default=0) code_specialite = db.Column(db.String(SHORT_STR_LEN)) - def __init__(self, **kwargs): - super(NotesFormation, self).__init__(**kwargs) - if self.formation_code is None: - # génère formation_code à la création - self.formation_code = f"FCOD{self.id:03d}" - class NotesUE(db.Model): """Unité d'Enseignement""" @@ -43,7 +43,13 @@ class NotesUE(db.Model): # Type d'UE: 0 normal ("fondamentale"), 1 "sport", 2 "projet et stage (LP)", # 4 "élective" type = db.Column(db.Integer, default=0) - ue_code = db.Column(db.String(SHORT_STR_LEN), nullable=False) + # Les UE sont "compatibles" (pour la capitalisation) ssi elles ont ^m code + # note: la fonction SQL notes_newid_ucod doit être créée à part + ue_code = db.Column( + db.String(SHORT_STR_LEN), + server_default=db.text("notes_newid_ucod()"), + nullable=False, + ) ects = db.Column(db.Float) # nombre de credits ECTS is_external = db.Column(db.Boolean(), nullable=False, default=False) # id de l'element pedagogique Apogee correspondant: @@ -51,12 +57,6 @@ class NotesUE(db.Model): # coef UE, utilise seulement si l'option use_ue_coefs est activée: coefficient = db.Column(db.Float) - def __init__(self, **kwargs): - super(NotesUE, self).__init__(**kwargs) - if self.ue_code is None: - # génère code à la création - self.ue_code = f"UCOD{self.ue_id:03d}" - class NotesMatiere(db.Model): """Matières: regroupe les modules d'une UE diff --git a/app/models/groups.py b/app/models/groups.py index 0fd6c756..312c1d00 100644 --- a/app/models/groups.py +++ b/app/models/groups.py @@ -54,6 +54,7 @@ class GroupDescr(db.Model): group_membership = db.Table( "group_membership", + db.Column("id", db.Integer, primary_key=True), # was group_membership_id db.Column("etudid", db.Integer, db.ForeignKey("identite.id")), db.Column("group_id", db.Integer, db.ForeignKey("group_descr.id")), db.UniqueConstraint("etudid", "group_id"), diff --git a/app/scodoc/notesdb.py b/app/scodoc/notesdb.py index eca758b9..72441eaa 100644 --- a/app/scodoc/notesdb.py +++ b/app/scodoc/notesdb.py @@ -93,7 +93,9 @@ def SimpleDictFetch(query, args, cursor=None): def DBInsertDict(cnx, table, vals, commit=0, convert_empty_to_nulls=1): - "insert into table values in dict 'vals'" + """insert into table values in dict 'vals' + Return: id de l'object créé + """ cursor = cnx.cursor(cursor_factory=ScoDocCursor) if convert_empty_to_nulls: for col in vals.keys(): @@ -112,7 +114,8 @@ def DBInsertDict(cnx, table, vals, commit=0, convert_empty_to_nulls=1): ) else: cursor.execute("insert into %s default values" % table) - oid = cursor.lastrowid + cursor.execute(f"SELECT CURRVAL('{table}_id_seq')") # id créé + oid = cursor.fetchone()[0] except: log("DBInsertDict: EXCEPTION !") log("DBInsertDict: table=%s, vals=%s" % (str(table), str(vals))) @@ -272,7 +275,6 @@ class EditableTable(object): input_formators={}, aux_tables=[], convert_null_outputs_to_empty=True, - allow_set_id=False, html_quote=True, fields_creators={}, # { field : [ sql_command_to_create_it ] } filter_nulls=True, # dont allow to set fields to null @@ -281,11 +283,16 @@ class EditableTable(object): self.id_name = id_name self.aux_tables = aux_tables self.dbfields = dbfields + # DB remove object_id ("id" in db) + try: + i = self.dbfields.index(id_name) + self.dbfields = self.dbfields[:i] + self.dbfields[i + 1 :] + except ValueError: + pass self.sortkey = sortkey self.output_formators = output_formators self.input_formators = input_formators self.convert_null_outputs_to_empty = convert_null_outputs_to_empty - self.allow_set_id = allow_set_id self.html_quote = html_quote self.fields_creators = fields_creators self.filter_nulls = filter_nulls @@ -294,7 +301,7 @@ class EditableTable(object): def create(self, cnx, args): "create object in table" vals = dictfilter(args, self.dbfields, self.filter_nulls) - if self.id_name in vals and not self.allow_set_id: + if self.id_name in vals: del vals[self.id_name] if self.html_quote: quote_dict(vals) # quote all HTML markup @@ -303,14 +310,7 @@ class EditableTable(object): if title in self.input_formators: vals[title] = self.input_formators[title](vals[title]) # insert - oid = DBInsertDict(cnx, self.table_name, vals, commit=True) - # get back new object id - cursor = cnx.cursor(cursor_factory=ScoDocCursor) - cursor.execute( - "select %(id_name)s from %(table_name)s where oid=%(oid)s" - % {"id_name": self.id_name, "table_name": self.table_name, "oid": oid} - ) - new_id = cursor.fetchone()[0] + new_id = DBInsertDict(cnx, self.table_name, vals, commit=True) return new_id def delete(self, cnx, oid, commit=True): @@ -346,6 +346,8 @@ class EditableTable(object): ) for r in res: self.format_output(r, disable_formatting=disable_formatting) + # Add ScoDoc7 id: + r[self.id_name] = r["id"] return res def format_output(self, r, disable_formatting=False): @@ -365,7 +367,10 @@ class EditableTable(object): def edit(self, cnx, args, html_quote=None): """Change fields""" + # assert self.id_name in args + oid = args[self.id_name] vals = dictfilter(args, self.dbfields, self.filter_nulls) + vals["id"] = oid html_quote = html_quote or self.html_quote if html_quote: quote_dict(vals) # quote HTML @@ -381,7 +386,7 @@ class EditableTable(object): cnx, self.table_name, vals, - where="%s=%%(%s)s" % (self.id_name, self.id_name), + where="id=%(id)s", commit=True, ) @@ -575,4 +580,4 @@ def copy_tuples_changing_attribute( t[column] = new_value for c in to_exclude: del t[c] - DBInsertDict(cnx, table, t, convert_empty_to_nulls=False) + _ = DBInsertDict(cnx, table, t, convert_empty_to_nulls=False) diff --git a/app/scodoc/sco_cache.py b/app/scodoc/sco_cache.py index 49ee2d95..c2352f95 100644 --- a/app/scodoc/sco_cache.py +++ b/app/scodoc/sco_cache.py @@ -127,9 +127,9 @@ class EvaluationCache(ScoDocCache): @classmethod def invalidate_sem(cls, formsemestre_id): "delete evaluations in this formsemestre from cache" - req = """SELECT e.evaluation_id + req = """SELECT e.id FROM notes_formsemestre s, notes_evaluation e, notes_moduleimpl m - WHERE s.formsemestre_id = %(formsemestre_id)s and s.formsemestre_id=m.formsemestre_id and e.moduleimpl_id=m.moduleimpl_id; + WHERE s.id = %(formsemestre_id)s and s.id=m.id and e.moduleimpl_id=m.id; """ evaluation_ids = [ x[0] for x in ndb.SimpleQuery(req, {"formsemestre_id": formsemestre_id}) @@ -140,8 +140,7 @@ class EvaluationCache(ScoDocCache): def invalidate_all_sems(cls): "delete all evaluations from cache" evaluation_ids = [ - x[0] - for x in ndb.SimpleQuery("SELECT evaluation_id FROM notes_evaluation", "") + x[0] for x in ndb.SimpleQuery("SELECT id FROM notes_evaluation", "") ] cls.delete_many(evaluation_ids) @@ -243,10 +242,7 @@ def invalidate_formsemestre( # was inval_cache( context, formsemestre_id=None, # clear all caches log("----- invalidate_formsemestre: clearing all caches -----") formsemestre_ids = [ - x[0] - for x in ndb.SimpleQuery( - "SELECT formsemestre_id FROM notes_formsemestre", "" - ) + x[0] for x in ndb.SimpleQuery("SELECT id FROM notes_formsemestre", "") ] else: formsemestre_ids = [ diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 43c37212..7f04bcf8 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -824,9 +824,10 @@ Si vous souhaitez modifier cette formation (par exemple pour y ajouter un module return "".join(H) -def ue_sharing_code(context, ue_code=None, ue_id=None, hide_ue_id=None): +def ue_sharing_code(context, ue_code=None, ue_id=None, hide_ue_id=False): """HTML list of UE sharing this code Either ue_code or ue_id may be specified. + Si hide_ue_id, ne montre pas l'UE d'origine dans la liste """ from app.scodoc import sco_formations diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index 002fe5ad..88159d9a 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -267,7 +267,7 @@ _identiteEditor = ndb.EditableTable( }, output_formators={"date_naissance": ndb.DateISOtoDMY}, convert_null_outputs_to_empty=True, - allow_set_id=True, # car on specifie le code Apogee a la creation + # allow_set_id=True, # car on specifie le code Apogee a la creation #sco8 ) identite_delete = _identiteEditor.delete diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 6258a65f..0da5be2a 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -1375,7 +1375,7 @@ def do_formsemestre_delete(context, formsemestre_id): e, ) ndb.SimpleQuery( - "DELETE FROM notes_evaluation WHERE evaluation_id=%(evaluation_id)s", + "DELETE FROM notes_evaluation WHERE id=%(evaluation_id)s", e, ) diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index a9e6a793..4a001153 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -113,7 +113,7 @@ def group_delete(context, group, force=False): # remove memberships: ndb.SimpleQuery("DELETE FROM group_membership WHERE group_id=%(group_id)s", group) # delete group: - ndb.SimpleQuery("DELETE FROM group_descr WHERE group_id=%(group_id)s", group) + ndb.SimpleQuery("DELETE FROM group_descr WHERE id=%(group_id)s", group) def get_partition(context, partition_id): @@ -556,8 +556,8 @@ def change_etud_group_in_partition( partition = get_partition(context, group["partition_id"]) # 1- Supprime membership dans cette partition ndb.SimpleQuery( - """DELETE FROM group_membership WHERE group_membership_id IN - (SELECT gm.group_membership_id + """DELETE FROM group_membership WHERE id IN + (SELECT gm.id FROM group_membership gm, group_descr gd WHERE gm.etudid=%(etudid)s AND gm.group_id=gd.group_id AND gd.partition_id=%(partition_id)s)""", {"etudid": etudid, "partition_id": partition["partition_id"]}, diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index be89c03b..e713e601 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -201,7 +201,7 @@ class BasePreferences(object): ("pref_id", "name", "value", "formsemestre_id"), sortkey="name", convert_null_outputs_to_empty=False, - allow_set_id=True, + # allow_set_id=True, #sco8 html_quote=False, # car markup pdf reportlab ( etc) filter_nulls=False, ) diff --git a/app/scodoc/sco_tag_module.py b/app/scodoc/sco_tag_module.py index b11123b2..681e5d53 100644 --- a/app/scodoc/sco_tag_module.py +++ b/app/scodoc/sco_tag_module.py @@ -82,13 +82,9 @@ class ScoTag(object): # Create new tag: # log("creating new tag: %s" % self.title) cnx = ndb.GetDBConnexion() - oid = ndb.DBInsertDict( + self.tag_id = ndb.DBInsertDict( cnx, self.tag_table, {"title": self.title}, commit=True ) - self.tag_id = ndb.SimpleDictFetch( - "SELECT tag_id FROM " + self.tag_table + " WHERE oid=%(oid)s", - {"oid": oid}, - )[0]["tag_id"] if object_id: self.tag_object(object_id) @@ -123,7 +119,14 @@ class ScoTag(object): if not r: # log("tag %s with %s" % (object_id, self.title)) cnx = ndb.GetDBConnexion() - ndb.DBInsertDict(cnx, self.assoc_table, args, commit=True) + query = f"""INSERT INTO {self.assoc_table} + (tag_id, f{self.obj_colname}) + VALUES""" + ndb.SimpleQuery( + query + " (%(tag_id)s, %(object_id)s)", + {"tag_id": self.tag_id, "object_id": object_id}, + ) + cnx.commit() def remove_tag_from_object(self, object_id): """Remove tag from module.