From 7f87d222496514e6000d86ade1793d65dbdb549c Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Wed, 21 Aug 2024 11:31:13 +0200 Subject: [PATCH] HS: Use section ID instead of RK. --- src/Model/Geometry/ProfileXYZ.py | 3 + .../HydraulicStructures.py | 129 ++++++++++++------ src/Model/Study.py | 15 +- src/Model/Tools/PamhyrDB.py | 48 ++++++- src/SQL.py | 2 +- src/View/HydraulicStructures/Table.py | 27 +++- src/View/HydraulicStructures/UndoCommand.py | 12 +- 7 files changed, 176 insertions(+), 60 deletions(-) diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index 3469b4ac..1bab9b1b 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -335,6 +335,9 @@ class ProfileXYZ(Profile, SQLSubModel): return point + def display_name(self): + return f"{self.name} ({self.rk:.4f})" + def x(self): return [point.x for point in self.points] diff --git a/src/Model/HydraulicStructures/HydraulicStructures.py b/src/Model/HydraulicStructures/HydraulicStructures.py index d29a070e..dadc1507 100644 --- a/src/Model/HydraulicStructures/HydraulicStructures.py +++ b/src/Model/HydraulicStructures/HydraulicStructures.py @@ -46,8 +46,8 @@ class HydraulicStructure(SQLSubModel): self._status = status self._name = name - self._input_rk = None - self._output_rk = None + self._input_section = None + self._output_section = None self._input_reach = None self._output_reach = None self._enabled = True @@ -60,14 +60,18 @@ class HydraulicStructure(SQLSubModel): {cls.create_db_add_pamhyr_id()}, name TEXT NOT NULL, enabled BOOLEAN NOT NULL, - input_rk REAL NOT NULL, - output_rk REAL NOT NULL, input_reach INTEGER, output_reach INTEGER, + input_section INTEGER, + output_section INTEGER, {Scenario.create_db_add_scenario()}, {Scenario.create_db_add_scenario_fk()}, FOREIGN KEY(input_reach) REFERENCES river_reach(pamhyr_id), FOREIGN KEY(output_reach) REFERENCES river_reach(pamhyr_id), + FOREIGN KEY(input_section) + REFERENCES geometry_profileXYZ(pamhyr_id), + FOREIGN KEY(output_section) + REFERENCES geometry_profileXYZ(pamhyr_id), PRIMARY KEY(pamhyr_id, scenario) ) """) @@ -98,13 +102,38 @@ class HydraulicStructure(SQLSubModel): cls._db_update_to_0_1_0(execute, data) + if major == "0" and minor == "1": + if int(release) < 1: + cls._db_update_to_0_1_1(execute, data) + return cls._update_submodel(execute, version, data) + @classmethod + def _db_update_to_0_1_1(cls, execute, data, + origin_version="0.1.0"): + for v in ["input", "output"]: + execute( + "ALTER TABLE hydraulic_structures " + + f"ADD COLUMN {v}_section INTEGER" + ) + + cls._db_update_to_0_1_1_assoc_section_from_rk( + execute, "hydraulic_structures", + reach_column=f"{v}_reach", + rk_column=f"{v}_rk", + section_column=f"{v}_section", + origin_version=origin_version + ) + @classmethod def _db_update_to_0_1_0(cls, execute, data): table = "hydraulic_structures" reachs = data['id2pid']['river_reach'] + cls._db_update_to_0_1_1( + execute, data, + origin_version="0.0.*" + ) cls.update_db_add_pamhyr_id(execute, table, data) Scenario.update_db_add_scenario(execute, table) @@ -112,9 +141,9 @@ class HydraulicStructure(SQLSubModel): execute( f"INSERT INTO {table}_tmp " + - "(pamhyr_id, name, enabled, input_rk, output_rk, " + + "(pamhyr_id, name, enabled, input_section, output_section, " + "input_reach, output_reach, scenario) " + - "SELECT pamhyr_id, name, enabled, input_rk, output_rk, " + + "SELECT pamhyr_id, name, enabled, input_section, output_section, " + "input_reach, output_reach, scenario " + f"FROM {table}" ) @@ -155,7 +184,7 @@ class HydraulicStructure(SQLSubModel): table = execute( "SELECT pamhyr_id, name, enabled, " + - "input_rk, output_rk, " + + "input_section, output_section, " + "input_reach, output_reach " + "FROM hydraulic_structures" ) @@ -166,29 +195,44 @@ class HydraulicStructure(SQLSubModel): hs_id = next(it) name = next(it) enabled = (next(it) == 1) - input_rk = next(it) - output_rk = next(it) + input_section_id = next(it) + input_section_id = ( + -1 if input_section_id is None else input_section_id + ) + output_section_id = next(it) + output_section_id = ( + -1 if output_section_id is None else output_section_id + ) input_reach_id = next(it) output_reach_id = next(it) hs = cls( - id=hs_id, - name=name, - status=data['status'] + id=hs_id, name=name, status=data['status'] ) hs.enabled = enabled - hs.input_rk = input_rk if input_rk != -1 else None - hs.output_rk = output_rk if output_rk != -1 else None - hs.input_reach, hs.output_reach = reduce( lambda acc, n: ( - n if n.id == input_reach_id else acc[0], - n if n.id == output_reach_id else acc[1] + n if n.pamhyr_id == input_reach_id else acc[0], + n if n.pamhyr_id == output_reach_id else acc[1] ), data["edges"], [None, None] ) + sections = [] + if hs.input_reach is not None: + sections += hs.input_reach.reach.profiles + if hs.output_reach is not None: + sections += hs.output_reach.reach.profiles + + hs.input_section, hs.output_section = reduce( + lambda acc, s: ( + s if s.pamhyr_id == input_section_id else acc[0], + s if s.pamhyr_id == output_section_id else acc[1] + ), + sections, + [None, None] + ) data['hs_id'] = hs_id hs._data = BasicHS._db_load(execute, data) @@ -211,24 +255,24 @@ class HydraulicStructure(SQLSubModel): if self._output_reach is not None: output_reach_id = self._output_reach.pamhyr_id - input_rk = -1 - if self.input_rk is not None: - input_rk = self.input_rk + input_section = 'NULL' + if self.input_section is not None: + input_section = self.input_section.pamhyr_id - output_rk = -1 - if self.output_rk is not None: - output_rk = self.output_rk + output_section = 'NULL' + if self.output_section is not None: + output_section = self.output_section.pamhyr_id execute( "INSERT INTO " + "hydraulic_structures(" + - " pamhyr_id, name, enabled, input_rk, output_rk, " + + " pamhyr_id, name, enabled, input_section, output_section, " + " input_reach, output_reach" + ") " + "VALUES (" + f"{self.pamhyr_id}, '{self._db_format(self._name)}', " + f"{self._db_format(self.enabled)}, " + - f"{input_rk}, {output_rk}, " + + f"{input_section}, {output_section}, " + f"{input_reach_id}, {output_reach_id}" + ")" ) @@ -261,23 +305,32 @@ class HydraulicStructure(SQLSubModel): @property def input_rk(self): - return self._input_rk - - @input_rk.setter - def input_rk(self, input_rk): - if input_rk is not None: - input_rk = float(input_rk) - - self._input_rk = input_rk - self._status.modified() + if self._input_section is None: + return None + return self._input_section.rk @property def output_rk(self): - return self._output_rk + if self._output_section is None: + return None + return self._output_section.rk - @output_rk.setter - def output_rk(self, output_rk): - self._output_rk = output_rk + @property + def input_section(self): + return self._input_section + + @input_section.setter + def input_section(self, input_section): + self._input_section = input_section + self._status.modified() + + @property + def output_section(self): + return self._output_section + + @output_section.setter + def output_section(self, output_section): + self._output_section = output_section self._status.modified() @property diff --git a/src/Model/Study.py b/src/Model/Study.py index b2b2d910..dda0a7f2 100644 --- a/src/Model/Study.py +++ b/src/Model/Study.py @@ -44,7 +44,7 @@ class Study(SQLModel): def __init__(self, filename=None, init_new=True): # Metadata - self._version = "0.1.0" + self._version = "0.1.1" self.creation_date = datetime.now() self.last_modification_date = datetime.now() self.last_save_date = datetime.now() @@ -223,13 +223,12 @@ class Study(SQLModel): def _create(self): # Info (metadata) - self.execute( - "INSERT INTO info VALUES ('study_release', '0')" - ) - self.execute( "CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)" ) + self.execute( + "INSERT INTO info VALUES ('study_release', '0')" + ) self.execute( "INSERT INTO info VALUES ('version', " + f"'{self._db_format(self._version)}')", @@ -272,7 +271,7 @@ class Study(SQLModel): "INSERT INTO info VALUES ('study_release', '0')" ) - if int(minor) < 1: + if major == "0" and int(minor) < 1: # Need to temporary disable the sqlite foreign keys # checking to update db dans change the table id fk to # table pamhyr_id fk @@ -282,7 +281,7 @@ class Study(SQLModel): ok = self._update_submodel(version[0], data={}) - if int(minor) < 1: + if major == "0" and int(minor) < 1: # Reactivate foreign keys checking self.execute( "PRAGMA foreign_keys = ON;" @@ -355,7 +354,7 @@ class Study(SQLModel): progress = progress if progress is not None else lambda: None self.execute( - "INSERT INTO info VALUES ('study_release', " + + "INSERT OR REPLACE INTO info VALUES ('study_release', " + f"'{self.status.version}')" ) diff --git a/src/Model/Tools/PamhyrDB.py b/src/Model/Tools/PamhyrDB.py index 0bbb1f2c..c23c9198 100644 --- a/src/Model/Tools/PamhyrDB.py +++ b/src/Model/Tools/PamhyrDB.py @@ -44,7 +44,7 @@ class SQLModel(SQL): if exists and is_new: os.remove(db) - self._db = sqlite3.connect(db) + self._db = sqlite3.connect(db, check_same_thread=False) self._cur = self._db.cursor() if is_new: @@ -244,6 +244,52 @@ class SQLSubModel(PamhyrID): f"WHERE pamhyr_id = {pid}" ) + @classmethod + def _db_update_to_0_1_1_assoc_section_from_rk( + cls, execute, table, + reach_column="reach", + rk_column="rk", + section_column="section", + origin_version="0.1.0"): + kid = "pamhyr_id" + if origin_version == "0.0.*": + kid = "id" + + els = execute( + "SELECT " + + f"{kid}, {reach_column}, {rk_column} " + + f"FROM {table}" + ) + + for row in els: + it = iter(row) + + pid = next(it) + reach_id = next(it) + rk = next(it) + + if reach_id == -1 or reach_id is None: + continue + + section_id = -1 + section = execute( + f"SELECT pamhyr_id FROM geometry_profileXYZ " + + f"WHERE reach == {reach_id} AND rk == {rk}" + ) + + if len(section) != 0: + section_id = section[0][0] + + logger.info( + f"Update reach rk {rk}({reach_id}) to pid {section_id}" + ) + + execute( + f"UPDATE {table} " + + f"SET {section_column} = {section_id} " + + f"WHERE {kid} = {pid}" + ) + @classmethod def _db_load(cls, execute, data=None): """Load instance of this class from SQL data base diff --git a/src/SQL.py b/src/SQL.py index ea7c637d..2b3aa292 100644 --- a/src/SQL.py +++ b/src/SQL.py @@ -36,7 +36,7 @@ class SQL(object): exist_ok=True ) - self._db = sqlite3.connect(db) + self._db = sqlite3.connect(db, check_same_thread=False) self._cur = self._db.cursor() if not exists: diff --git a/src/View/HydraulicStructures/Table.py b/src/View/HydraulicStructures/Table.py index e8530b1a..dfe041ad 100644 --- a/src/View/HydraulicStructures/Table.py +++ b/src/View/HydraulicStructures/Table.py @@ -20,6 +20,7 @@ import logging import traceback from tools import trace, timer +from functools import reduce from PyQt5.QtCore import ( Qt, QVariant, QAbstractTableModel, @@ -37,7 +38,7 @@ from PyQt5.QtWidgets import ( from View.Tools.PamhyrTable import PamhyrTableModel from View.HydraulicStructures.UndoCommand import ( - SetNameCommand, SetReachCommand, SetRkCommand, + SetNameCommand, SetReachCommand, SetSectionCommand, SetEnabledCommand, AddCommand, DelCommand, ) @@ -65,7 +66,8 @@ class ComboBoxDelegate(QItemDelegate): if reach is not None: val = list( map( - lambda rk: str(rk), reach.reach.get_rk() + lambda p: p.display_name(), + reach.reach.profiles ) ) else: @@ -89,7 +91,20 @@ class ComboBoxDelegate(QItemDelegate): def setModelData(self, editor, model, index): text = str(editor.currentText()) - model.setData(index, text) + + if self._mode == "rk": + reach = self._data.hydraulic_structures\ + .get(index.row())\ + .input_reach + val = reduce( + lambda acc, p: p if text == p.display_name() else acc, + reach.reach.profiles, + None + ) + else: + val = text + + model.setData(index, val) editor.close() editor.deleteLater() @@ -127,10 +142,10 @@ class TableModel(PamhyrTableModel): return self._trad['not_associated'] return n.name elif self._headers[column] == "rk": - n = self._lst.get(row).input_rk + n = self._lst.get(row).input_section if n is None: return self._trad['not_associated'] - return n + return n.display_name() return QVariant() @@ -163,7 +178,7 @@ class TableModel(PamhyrTableModel): value = None self._undo.push( - SetRkCommand( + SetSectionCommand( self._lst, row, value ) ) diff --git a/src/View/HydraulicStructures/UndoCommand.py b/src/View/HydraulicStructures/UndoCommand.py index f7b83b97..4dba1ec7 100644 --- a/src/View/HydraulicStructures/UndoCommand.py +++ b/src/View/HydraulicStructures/UndoCommand.py @@ -66,20 +66,20 @@ class SetReachCommand(QUndoCommand): i.input_rk = self._new_rk -class SetRkCommand(QUndoCommand): - def __init__(self, h_s_lst, index, rk): +class SetSectionCommand(QUndoCommand): + def __init__(self, h_s_lst, index, section): QUndoCommand.__init__(self) self._h_s_lst = h_s_lst self._index = index - self._old = self._h_s_lst.get(self._index).input_rk - self._new = rk + self._old = self._h_s_lst.get(self._index).input_section + self._new = section def undo(self): - self._h_s_lst.get(self._index).input_rk = self._old + self._h_s_lst.get(self._index).input_section = self._old def redo(self): - self._h_s_lst.get(self._index).input_rk = self._new + self._h_s_lst.get(self._index).input_section = self._new class SetEnabledCommand(QUndoCommand):