diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py index 924c7280..0c401b75 100644 --- a/src/Model/BoundaryCondition/BoundaryCondition.py +++ b/src/Model/BoundaryCondition/BoundaryCondition.py @@ -252,7 +252,7 @@ class BoundaryCondition(SQLSubModel): cls._db_update_to_0_1_0(execute, data) if major == "0" and minor == "1": - if release < 2: + if int(release) < 2: execute( "ALTER TABLE friction " + "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" diff --git a/src/Model/BoundaryCondition/BoundaryConditionList.py b/src/Model/BoundaryCondition/BoundaryConditionList.py index 7040e27e..af36eb8b 100644 --- a/src/Model/BoundaryCondition/BoundaryConditionList.py +++ b/src/Model/BoundaryCondition/BoundaryConditionList.py @@ -51,7 +51,7 @@ class BoundaryConditionList(PamhyrModelListWithTab): def _db_save(self, execute, data=None): execute( - "DELETE FROM boundary_condition " + "DELETE FROM boundary_condition " + f"WHERE scenario = {self._status.scenario_id}" ) diff --git a/src/Model/LateralContribution/LateralContribution.py b/src/Model/LateralContribution/LateralContribution.py index 3d5337c6..b600e7b8 100644 --- a/src/Model/LateralContribution/LateralContribution.py +++ b/src/Model/LateralContribution/LateralContribution.py @@ -39,9 +39,12 @@ class Data(SQLSubModel): data0, data1, id: int = -1, types=[float, float], - status=None): - super(Data, self).__init__(id) - self._status = status + status=None, + owner_scenario=-1): + super(Data, self).__init__( + id=id, status=status, + owner_scenario=owner_scenario + ) self._types = types self._data = [data0, data1] @@ -51,6 +54,7 @@ class Data(SQLSubModel): execute(f""" CREATE TABLE lateral_contribution_data{ext} ( {cls.create_db_add_pamhyr_id()}, + deleted BOOLEAN NOT NULL DEFAULT FALSE, ind INTEGER NOT NULL, data0 TEXT NOT NULL, data1 TEXT NOT NULL, @@ -71,6 +75,13 @@ class Data(SQLSubModel): if major == minor == "0": cls._db_update_to_0_1_0(execute, data) + if major == "0" and minor == "1": + if int(release) < 2: + execute( + "ALTER TABLE boundary_condition_data " + + "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" + ) + return cls._update_submodel(execute, version, data) @classmethod @@ -115,11 +126,20 @@ class Data(SQLSubModel): def _db_load(cls, execute, data=None): new = [] lc = data["lc"] + status = data['status'] + scenario = data["scenario"] + loaded = data['loaded_pid'] + + if scenario is None: + return new values = execute( - "SELECT pamhyr_id, ind, data0, data1 " + + "SELECT pamhyr_id, deleted, " + + "data0, data1, scenario " + "FROM lateral_contribution_data " + f"WHERE lc = {lc._pamhyr_id} " + + f"AND scenario = {scenario.id} " + + f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " + "ORDER BY ind ASC" ) @@ -127,18 +147,27 @@ class Data(SQLSubModel): it = iter(v) pid = next(it) - ind = next(it) + delete = next(it) data0 = bc._types[0](next(it)) data1 = bc._types[1](next(it)) + owner_scenario = next(it) nd = cls( + data0, data1, id=pid, types=lc._types, - status=data['status'] + status=status, + owner_scenario=owner_scenario ) - nd._data = [data0, data1] + if deleted: + nd.set_as_deleted() - new.append((ind, nd)) + loaded.add(pid) + new.append(nd) + + data["scenario"] = scenario.parent + new += cls._db_load(execute, data) + data["scenario"] = scenario return new @@ -149,12 +178,17 @@ class Data(SQLSubModel): data1 = self._db_format(str(self[1])) lc = data["lc"] - sql = ( + execute( "INSERT INTO " + - "lateral_contribution_data (pamhyr_id, ind, data0, data1, bc) " + - f"VALUES ({pid}, {ind}, '{data0}', {data1}, {lc._pamhyr_id})" + "lateral_contribution_data (pamhyr_id, deleted, ind, " + + "data0, data1, lc, scenario) " + + f"VALUES ({pid}, {self._db_format(self.is_deleted())}, " + + f"{ind}, '{self._db_format(data0)}', {self._db_format(data1)}, " + + f"{lc._pamhyr_id}, {self._status.scenario_id}" + + ")" ) - execute(sql) + + return True def __getitem__(self, key): return self._types[key](self._data[key]) @@ -165,17 +199,20 @@ class Data(SQLSubModel): class LateralContribution(SQLSubModel): _sub_classes = [Data] - _id_cnt = 0 - def __init__(self, id: int = -1, name: str = "", status=None): - super(LateralContribution, self).__init__(id) - self._status = status + def __init__(self, id: int = -1, + name: str = "", + status=None, owner_scenario=-1): + super(LateralContribution, self).__init__( + id=id, status=status, + owner_scenario=owner_scenario + ) self._name = name self._type = "" self._reach = None - self._begin_rk = None - self._end_rk = None + self._begin_section = None + self._end_section = None self._data = [] self._header = [] self._types = [float, float] @@ -185,6 +222,7 @@ class LateralContribution(SQLSubModel): execute(f""" CREATE TABLE lateral_contribution{ext}( {cls.create_db_add_pamhyr_id()}, + deleted BOOLEAN NOT NULL DEFAULT FALSE, name TEXT NOT NULL, type TEXT NOT NULL, tab TEXT NOT NULL, @@ -234,6 +272,13 @@ class LateralContribution(SQLSubModel): cls._db_update_to_0_1_0(execute, data) + if major == "0" and minor == "1": + if int(release) < 2: + execute( + "ALTER TABLE lateral_contribution " + + "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" + ) + return cls._update_submodel(execute, version, data) @classmethod @@ -277,20 +322,40 @@ class LateralContribution(SQLSubModel): def _db_load(cls, execute, data=None): new = [] tab = data["tab"] + status = data['status'] + edges = data["edges"] + scenario = data["scenario"] + loaded = data['loaded_pid'] + + if scenario is None: + return new table = execute( - "SELECT pamhyr_id, name, type, " + - "reach, begin_section, end_section " + - f"FROM lateral_contribution WHERE tab = '{tab}'" + "SELECT pamhyr_id, deleted, name, type, " + + "reach, begin_section, end_section, scenario " + + "FROM lateral_contribution " + + f"WHERE tab = '{tab}'" + + f"AND scenario = {scenario.id} " + + f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " ) for row in table: - t = row[2] + it = iter(row) + + pid = next(it) + deleted = next(it) + name = next(it) + t = next(it) + reach = next(it) + b_section = next(it) + e_section = next(it) + owner_scenario = next(it) + ctor = cls._get_ctor_from_type(t) lc = ctor( - id=row[0], - name=row[1], - status=data['status'] + id=pid, name=name, + status=status, + owner_scenario=owner_scenario ) lc.reach = None lc._begin_section = None @@ -299,28 +364,46 @@ class LateralContribution(SQLSubModel): if row[3] != -1: lc.reach = next( filter( - lambda e: e.id == row[3], - data["edges"] + lambda e: e.id == reach, + edges + ) + ) + lc._begin_section = next( + filter( + lambda p: p.id == b_section, + lc.reach.reach.profiles + ) + ) + lc._end_section = next( + filter( + lambda p: p.id == e_section, + lc.reach.reach.profiles ) ) - lc._begin_section = lc.reach.profile(row[4]) - lc._end_section = lc.reach.profile(row[5]) data["lc"] = lc lc._data = Data._db_load(execute, data=data) + loaded.add(pid) + new.append(lc) + + data["scenario"] = scenario.parent + new += cls._db_load(execute, data) + data["scenario"] = scenario + return new def _db_save(self, execute, data=None): + if not self.must_be_saved(): + return True + tab = data["tab"] + data["lc"] = self - execute( - "DELETE FROM lateral_contribution " + - f"WHERE pamhyr_id = {self._pamhyr_id}" - ) execute( "DELETE FROM lateral_contribution_data " + - f"WHERE lc = {self._pamhyr_id}" + f"WHERE lc = {self._pamhyr_id} " + + f"AND scenario = {self._status.scenario_id}" ) reach = -1 @@ -332,35 +415,39 @@ class LateralContribution(SQLSubModel): begin_section = self._begin_section._pamhyr_id end_section = self._end_section._pamhyr_id - sql = ( + execute( "INSERT INTO " + - "lateral_contribution(pamhyr_id, name, type, tab, " + - "reach, begin_section, end_section) " + + "lateral_contribution(" + + "pamhyr_id, deleted, name, type, tab, " + + "reach, begin_section, end_section, scenario) " + "VALUES (" + - f"{self.id}, '{self._db_format(self._name)}', " + + f"{self.id}, {self._db_format(self.is_deleted())}, " + + f"'{self._db_format(self._name)}', " + f"'{self._db_format(self._type)}', '{tab}', {reach}, " + - f"{self._begin_rk}, {self._end_rk}" + + f"{begin_section}, {end_section}, " + + f"{self._status.scenario_id}" + ")" ) - execute(sql) ind = 0 for d in self._data: - data0 = self._db_format(str(d[0])) - data1 = self._db_format(str(d[1])) + data["ind"] = ind + + d._db_save(execute, data) - sql = ( - "INSERT INTO " + - "lateral_contribution_data(ind, data0, data1, lc) " + - f"VALUES ({ind}, '{data0}', {data1}, {self.id})" - ) - execute(sql) ind += 1 return True def __len__(self): - return len(self._data) + return len( + list( + filter( + lambda el: not el.is_deleted(), + self._data + ) + ) + ) @classmethod def compatibility(cls): @@ -392,7 +479,7 @@ class LateralContribution(SQLSubModel): @name.setter def name(self, name): self._name = name - self._status.modified() + self.modified() @property def lctype(self): @@ -406,46 +493,45 @@ class LateralContribution(SQLSubModel): def reach(self, reach): self._reach = reach if reach is not None: - self._begin_rk = self._reach.reach.get_rk_min() - self._end_rk = self._reach.reach.get_rk_max() - self._status.modified() + self._begin_section = self._reach.reach.profiles[0] + self._end_section = self._reach.reach.profiles[-1] + + self.modified() def has_reach(self): return self._reach is not None @property def begin_rk(self): - return self._begin_rk + if self._begin_section is None: + return 0 - @begin_rk.setter - def begin_rk(self, begin_rk): - if self._reach is None: - self._begin_rk = begin_rk - else: - _min = self._reach.reach.get_rk_min() - _max = self._reach.reach.get_rk_max() + return self._begin_section.rk - if _min <= begin_rk <= _max: - self._begin_rk = begin_rk + @property + def begin_section(self): + return self._begin_section - self._status.modified() + @begin_section.setter + def begin_section(self, section): + self._begin_section = section + self.modified() @property def end_rk(self): - return self._end_rk + if self._end_section is None: + return 0 - @end_rk.setter - def end_rk(self, end_rk): - if self._reach is None: - self._end_rk = end_rk - else: - _min = self._reach.reach.get_rk_min() - _max = self._reach.reach.get_rk_max() + return self._end_section.rk - if _min <= end_rk <= _max: - self._end_rk = end_rk + @property + def end_section(self): + return self._end_section - self._status.modified() + @end_section.setter + def end_section(self, section): + self._end_section = section + self.modified() @property def header(self): @@ -453,7 +539,12 @@ class LateralContribution(SQLSubModel): @property def data(self): - return self._data.copy() + return list( + filter( + lambda el: not el.is_deleted(), + self._data + ) + ) def get_type_column(self, column): if 0 <= column < 2: @@ -487,17 +578,17 @@ class LateralContribution(SQLSubModel): new_0 = self._types[0](data[0].replace(",", ".")) new_1 = self._types[1](data[1].replace(",", ".")) - return Data(new_0, new_1) + return Data(new_0, new_1, status=self._status) def add(self, index: int): - value = Data(self._default_0, self._default_1) + value = Data(self._default_0, self._default_1, status=self._status) self._data.insert(index, value) - self._status.modified() + self.modified() return value def insert(self, index: int, value): self._data.insert(index, value) - self._status.modified() + self.modified() def delete_i(self, indexes): self._data = list( @@ -509,7 +600,7 @@ class LateralContribution(SQLSubModel): ) ) ) - self._status.modified() + self.modified() def delete(self, els): self._data = list( @@ -518,14 +609,14 @@ class LateralContribution(SQLSubModel): self.data ) ) - self._status.modified() + self.modified() def sort(self, _reverse=False, key=None): if key is None: self._data.sort(reverse=_reverse) else: self._data.sort(reverse=_reverse, key=key) - self._status.modified() + self.modified() def get_i(self, index): return self.data[index] @@ -540,7 +631,7 @@ class LateralContribution(SQLSubModel): v = self._data[index] v[column] = self._types[column](value) self._data[index] = v - self._status.modified() + self.modified() def set_i_0(self, index: int, value): self._set_i_c_v(index, 0, value) @@ -552,8 +643,8 @@ class LateralContribution(SQLSubModel): def convert(self, cls): new = cls(name=self.name, status=self._status) new.reach = self.reach - new.begin_rk = self.begin_rk - new.end_rk = self.end_rk + new.begin_section = self.begin_section + new.end_section = self.end_section for i, _ in enumerate(self.data): new.add(i) @@ -567,7 +658,7 @@ class LateralContribution(SQLSubModel): except Exception as e: logger.info(e) - self._status.modified() + self.modified() return new def move_up(self, index): @@ -575,11 +666,11 @@ class LateralContribution(SQLSubModel): next = index - 1 d = self._data d[index], d[next] = d[next], d[index] - self._status.modified() + self.modified() def move_down(self, index): if index >= 0: prev = index + 1 d = self._data d[index], d[prev] = d[prev], d[index] - self._status.modified() + self.modified() diff --git a/src/Model/LateralContribution/LateralContributionList.py b/src/Model/LateralContribution/LateralContributionList.py index b186da83..4aa417aa 100644 --- a/src/Model/LateralContribution/LateralContributionList.py +++ b/src/Model/LateralContribution/LateralContributionList.py @@ -19,7 +19,7 @@ from copy import copy from tools import trace, timer -from Model.Tools.PamhyrList import PamhyrModelListWithTab +from Model.Tools.PamhyrListExt import PamhyrModelListWithTab from Model.Except import NotImplementedMethodeError from Model.LateralContribution.LateralContribution import LateralContribution @@ -50,7 +50,10 @@ class LateralContributionList(PamhyrModelListWithTab): return new def _db_save(self, execute, data=None): - execute("DELETE FROM lateral_contribution") + execute( + "DELETE FROM lateral_contribution " + + f"WHERE scenario = {self._status.scenario_id}" + ) if data is None: data = {} diff --git a/src/Model/LateralContribution/LateralContributionTypes.py b/src/Model/LateralContribution/LateralContributionTypes.py index a5b2bcd2..32097358 100644 --- a/src/Model/LateralContribution/LateralContributionTypes.py +++ b/src/Model/LateralContribution/LateralContributionTypes.py @@ -22,8 +22,12 @@ from Model.LateralContribution.LateralContribution import LateralContribution class NotDefined(LateralContribution): - def __init__(self, id: int = -1, name: str = "", status=None): - super(NotDefined, self).__init__(id=id, name=name, status=status) + def __init__(self, id: int = -1, name: str = "", + status=None, owner_scenario=-1): + super(NotDefined, self).__init__( + id=id, name=name, status=status, + owner_scenario=owner_scenario + ) self._type = "ND" self._header = ["x", "y"] @@ -34,8 +38,12 @@ class NotDefined(LateralContribution): class LateralContrib(LateralContribution): - def __init__(self, id: int = -1, name: str = "", status=None): - super(LateralContrib, self).__init__(id=id, name=name, status=status) + def __init__(self, id: int = -1, name: str = "", + status=None, owner_scenario=-1): + super(LateralContrib, self).__init__( + id=id, name=name, status=status, + owner_scenario=owner_scenario + ) self._type = "LC" self._header = ["time", "discharge"] @@ -47,8 +55,12 @@ class LateralContrib(LateralContribution): class Rain(LateralContribution): - def __init__(self, id: int = -1, name: str = "", status=None): - super(Rain, self).__init__(id=id, name=name, status=status) + def __init__(self, id: int = -1, name: str = "", + status=None, owner_scenario=-1): + super(Rain, self).__init__( + id=id, name=name, status=status, + owner_scenario=owner_scenario + ) self._type = "RA" self._header = ["time", "discharge"] @@ -60,8 +72,12 @@ class Rain(LateralContribution): class Evaporation(LateralContribution): - def __init__(self, id: int = -1, name: str = "", status=None): - super(Evaporation, self).__init__(id=id, name=name, status=status) + def __init__(self, id: int = -1, name: str = "", + status=None, owner_scenario=-1): + super(Evaporation, self).__init__( + id=id, name=name, status=status, + owner_scenario=owner_scenario + ) self._type = "EV" self._header = ["time", "discharge"] diff --git a/src/Model/Tools/PamhyrDB.py b/src/Model/Tools/PamhyrDB.py index ee18b00c..1d7d505d 100644 --- a/src/Model/Tools/PamhyrDB.py +++ b/src/Model/Tools/PamhyrDB.py @@ -233,6 +233,8 @@ class SQLSubModel(PamhyrID): value = value.replace("'", "'") elif type(value) is bool: value = 'TRUE' if value else 'FALSE' + elif value is None: + value = "NULL" return value @classmethod diff --git a/src/Model/Tools/PamhyrListExt.py b/src/Model/Tools/PamhyrListExt.py index e9d3c151..fe10b794 100644 --- a/src/Model/Tools/PamhyrListExt.py +++ b/src/Model/Tools/PamhyrListExt.py @@ -241,6 +241,13 @@ class PamhyrModelListWithTab(SQLSubModel): ) ) + def set(self, lst, index, new): + old = self.get_tab(lst)[index] + old.set_as_deleted() + + self._tabs[lst][index] = new + self._status.modified() + def get(self, lst, index): return self.get_tab(lst)[index] diff --git a/src/SQL.py b/src/SQL.py index 2b3aa292..ad62c678 100644 --- a/src/SQL.py +++ b/src/SQL.py @@ -101,6 +101,8 @@ class SQL(object): # Replace ''' by ''' to preserve SQL injection if type(value) is str: value = value.replace("'", "'") + elif value is None: + value = "NULL" return value @timer diff --git a/src/View/LateralContribution/Edit/Window.py b/src/View/LateralContribution/Edit/Window.py index 37813a2e..51a1ecc7 100644 --- a/src/View/LateralContribution/Edit/Window.py +++ b/src/View/LateralContribution/Edit/Window.py @@ -58,7 +58,7 @@ class EditLateralContributionWindow(PamhyrWindow): trad = LCETranslate() name = trad[self._pamhyr_name] if self._data is not None: - edge_name = (self._data.edge.name if self._data.edge is not None + edge_name = (self._data.reach.name if self._data.reach is not None else trad['not_associated']) name += ( f" - {study.name} " + diff --git a/src/View/LateralContribution/Table.py b/src/View/LateralContribution/Table.py index e5c533dd..44f43ce8 100644 --- a/src/View/LateralContribution/Table.py +++ b/src/View/LateralContribution/Table.py @@ -89,12 +89,15 @@ class ComboBoxDelegate(QItemDelegate): elif self._mode == "rk": if self._data is None: self.editor.addItems( - ["0"] + ["-"] ) else: self.editor.addItems( list( - map(str, self._data.reach.get_rk()) + map( + lambda p: p.display_name(), + self._data.reach.profiles + ) ) ) else: @@ -111,8 +114,17 @@ class ComboBoxDelegate(QItemDelegate): self.editor.currentTextChanged.connect(self.currentItemChanged) def setModelData(self, editor, model, index): - text = str(editor.currentText()) - model.setData(index, text) + value = str(editor.currentText()) + + if self._mode == "rk": + value = next( + filter( + lambda p: p.display_name() == value, + self._data.reach.profiles + ) + ) + + model.setData(index, value) editor.close() editor.deleteLater() @@ -150,7 +162,7 @@ class TableModel(PamhyrTableModel): t = self._lst.get(self._tab, row).lctype return self._long_types[t] elif self._headers[column] == "edge": - n = self._lst.get(self._tab, row).edge + n = self._lst.get(self._tab, row).reach if n is None: return self._trad['not_associated'] return n.name diff --git a/src/View/LateralContribution/UndoCommand.py b/src/View/LateralContribution/UndoCommand.py index e101f513..202360f6 100644 --- a/src/View/LateralContribution/UndoCommand.py +++ b/src/View/LateralContribution/UndoCommand.py @@ -53,14 +53,14 @@ class SetBeginCommand(QUndoCommand): self._lcs = lcs self._tab = tab self._index = index - self._old = self._lcs.get(self._tab, self._index).begin_rk - self._new = float(new_value) + self._old = self._lcs.get(self._tab, self._index).begin_section + self._new = new_value def undo(self): - self._lcs.get(self._tab, self._index).begin_rk = float(self._old) + self._lcs.get(self._tab, self._index).begin_section = self._old def redo(self): - self._lcs.get(self._tab, self._index).begin_rk = float(self._new) + self._lcs.get(self._tab, self._index).begin_section = self._new class SetEndCommand(QUndoCommand): @@ -70,14 +70,14 @@ class SetEndCommand(QUndoCommand): self._lcs = lcs self._tab = tab self._index = index - self._old = self._lcs.get(self._tab, self._index).end_rk - self._new = float(new_value) + self._old = self._lcs.get(self._tab, self._index).end_section + self._new = new_value def undo(self): - self._lcs.get(self._tab, self._index).end_rk = float(self._old) + self._lcs.get(self._tab, self._index).end_section = self._old def redo(self): - self._lcs.get(self._tab, self._index).end_rk = float(self._new) + self._lcs.get(self._tab, self._index).end_section = self._new class SetEdgeCommand(QUndoCommand): @@ -87,14 +87,14 @@ class SetEdgeCommand(QUndoCommand): self._lcs = lcs self._tab = tab self._index = index - self._old = self._lcs.get(self._tab, self._index).edge + self._old = self._lcs.get(self._tab, self._index).reach self._new = edge def undo(self): - self._lcs.get(self._tab, self._index).edge = self._old + self._lcs.get(self._tab, self._index).reach = self._old def redo(self): - self._lcs.get(self._tab, self._index).edge = self._new + self._lcs.get(self._tab, self._index).reach = self._new class SetTypeCommand(QUndoCommand): diff --git a/src/View/LateralContribution/Window.py b/src/View/LateralContribution/Window.py index 0c482433..05e6f3c0 100644 --- a/src/View/LateralContribution/Window.py +++ b/src/View/LateralContribution/Window.py @@ -211,7 +211,7 @@ class LateralContributionWindow(PamhyrWindow): edge = self._study.river\ .lateral_contribution\ .get(tab, rows[0])\ - .edge + .reach if edge: data = edge.reach lc = self._lcs.get(tab, rows[0]) diff --git a/src/View/MainWindowTabInfo.py b/src/View/MainWindowTabInfo.py index f655d799..85f358f0 100644 --- a/src/View/MainWindowTabInfo.py +++ b/src/View/MainWindowTabInfo.py @@ -144,7 +144,7 @@ class WidgetInfo(PamhyrWidget): for tab in lc._tabs_list: for c in lc.get_tab(tab): - if c.edge is not None: + if c.reach is not None: n_lc += 1 else: n_na_lc += 1