diff --git a/src/Model/BoundaryCondition/BoundaryCondition.py b/src/Model/BoundaryCondition/BoundaryCondition.py index c09639e0..91be5f81 100644 --- a/src/Model/BoundaryCondition/BoundaryCondition.py +++ b/src/Model/BoundaryCondition/BoundaryCondition.py @@ -38,9 +38,12 @@ class Data(SQLSubModel): def __init__(self, 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] @@ -50,6 +53,7 @@ class Data(SQLSubModel): execute(f""" CREATE TABLE boundary_condition_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, @@ -70,6 +74,13 @@ class Data(SQLSubModel): if major == "0" and int(minor) < 1: cls._db_update_to_0_1_0(execute, data) + if major == "0" and minor == "1": + if release < 2: + execute( + "ALTER TABLE boundary_condition_data " + + "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" + ) + return cls._update_submodel(execute, version, data) @classmethod @@ -117,11 +128,19 @@ class Data(SQLSubModel): def _db_load(cls, execute, data=None): new = [] bc = data["bc"] + 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, ind, " + + "data0, data1, scenario " + "FROM boundary_condition_data " + f"WHERE bc = {bc._pamhyr_id} " + + f"AND scenario = {scenario.id} " + + f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " + "ORDER BY ind ASC" ) @@ -129,19 +148,29 @@ class Data(SQLSubModel): it = iter(v) pid = next(it) + deleted = (next(it) == 1) ind = 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=bc._types, - status=data['status'] + status=data['status'], + owner_scenario=owner_scenario ) + if deleted: + nd.set_as_deleted() + loaded.add(pid) new.append(nd) + data["scenario"] = scenario.parent + new += cls._db_load(execute, data) + data["scenario"] = scenario + return new def _db_save(self, execute, data=None): @@ -153,10 +182,17 @@ class Data(SQLSubModel): execute( "INSERT INTO " + - "boundary_condition_data(pamhyr_id, ind, data0, data1, bc) " + - f"VALUES ({pid}, {ind}, '{data0}', {data1}, {bc._pamhyr_id})" + "boundary_condition_data(pamhyr_id, deleted, ind, " + + "data0, data1, bc, scenario) " + + f"VALUES (" + + f"{pid}, {self._db_format(self.is_deleted())}, " + + f"{ind}, '{self._db_format(data0)}', {self._db_format(data1)}, " + + f"{bc._pamhyr_id}, {self._status.scenario_id}" + + ")" ) + return True + def __getitem__(self, key): return self._types[key](self._data[key]) @@ -168,10 +204,13 @@ class BoundaryCondition(SQLSubModel): _sub_classes = [Data] def __init__(self, id: int = -1, - name: str = "", status=None): - super(BoundaryCondition, self).__init__(id) - - self._status = status + name: str = "", + status=None, + owner_scenario=-1): + super(BoundaryCondition, self).__init__( + id=id, status=status, + owner_scenario=owner_scenario + ) self._name = name self._type = "" @@ -185,6 +224,7 @@ class BoundaryCondition(SQLSubModel): execute(f""" CREATE TABLE boundary_condition{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, @@ -208,6 +248,13 @@ class BoundaryCondition(SQLSubModel): if major == minor == "0": cls._db_update_to_0_1_0(execute, data) + if major == "0" and minor == "1": + if release < 2: + execute( + "ALTER TABLE friction " + + "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" + ) + return cls._update_submodel(execute, version, data) @classmethod @@ -258,43 +305,67 @@ class BoundaryCondition(SQLSubModel): def _db_load(cls, execute, data=None): new = [] tab = data["tab"] + nodes = data["nodes"] + scenario = data["scenario"] + loaded = data['loaded_pid'] + + if scenario is None: + return new table = execute( - "SELECT pamhyr_id, name, type, node " + + "SELECT pamhyr_id, deleted, name, type, node, scenario " + "FROM boundary_condition " + - f"WHERE tab = '{tab}'" + 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) == 1) + name = next(it) + t = next(it) + node = next(it) + owner_scenario = next(it) + ctor = cls._get_ctor_from_type(t) + bc = ctor( - id=row[0], - name=row[1], - status=data['status'] + id=pid, name=name, + status=data['status'], + owner_scenario=owner_scenario ) + if deleted: + f.set_as_deleted() bc.node = None - if row[3] != -1: - bc.node = next(filter(lambda n: n.id == row[3], data["nodes"])) + if node != -1: + bc.node = next(filter(lambda n: n.id == node, nodes)) data["bc"] = bc bc._data = Data._db_load(execute, data=data) + loaded.add(pid) new.append(bc) + 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"] - execute( - "DELETE FROM boundary_condition " + - f"WHERE pamhyr_id = {self._pamhyr_id}" - ) execute( "DELETE FROM boundary_condition_data " + - f"WHERE bc = {self._pamhyr_id}" + f"WHERE bc = {self._pamhyr_id} " + + f"AND scenario = {self._status.scenario_id}" ) node = -1 @@ -303,10 +374,14 @@ class BoundaryCondition(SQLSubModel): execute( "INSERT INTO " + - "boundary_condition(pamhyr_id, name, type, tab, node) " + + "boundary_condition(" + + "pamhyr_id, deleted, name, type, tab, node, scenario" + + ") " + "VALUES (" + - f"{self._pamhyr_id}, '{self._db_format(self._name)}', " + - f"'{self._db_format(self._type)}', '{tab}', {node}" + + f"{self._pamhyr_id}, {self._db_format(self.is_deleted())}, " + + f"'{self._db_format(self._name)}', " + + f"'{self._db_format(self._type)}', '{tab}', {node}, " + + f"{self._status.scenario_id}" + ")" ) @@ -323,7 +398,14 @@ class BoundaryCondition(SQLSubModel): 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): @@ -355,7 +437,7 @@ class BoundaryCondition(SQLSubModel): @name.setter def name(self, name): self._name = name - self._status.modified() + self.modified() @property def bctype(self): @@ -368,7 +450,7 @@ class BoundaryCondition(SQLSubModel): @node.setter def node(self, node): self._node = node - self._status.modified() + self.modified() def has_node(self): return self._node is not None @@ -379,7 +461,12 @@ class BoundaryCondition(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: @@ -418,40 +505,53 @@ class BoundaryCondition(SQLSubModel): def add(self, index: int): value = Data(self._default_0, self._default_1) 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() + if value in self._data: + value.set_as_not_deleted() + else: + self._data.insert(index, value) - def delete_i(self, indexes): + self.modified() + + def hard_delete_i(self, indexes): self._data = list( map( lambda e: e[1], filter( lambda e: e[0] not in indexes, + enumerate(self._data) + ) + ) + ) + self.modified() + + def delete_i(self, indexes): + list( + map( + lambda e: e[1].set_as_deleted(), + filter( + lambda e: e[0] in indexes, enumerate(self.data) ) ) ) - self._status.modified() + self.modified() def delete(self, els): - self._data = list( - filter( - lambda e: e not in els, - self.data - ) - ) - self._status.modified() + for el in els: + el.set_as_deleted() + + 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 index(self, bc): self._data.index(bc) @@ -469,7 +569,7 @@ class BoundaryCondition(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) @@ -501,14 +601,14 @@ class BoundaryCondition(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() def reach(self, river): r = [] diff --git a/src/Model/BoundaryCondition/BoundaryConditionList.py b/src/Model/BoundaryCondition/BoundaryConditionList.py index f4600ac6..7040e27e 100644 --- a/src/Model/BoundaryCondition/BoundaryConditionList.py +++ b/src/Model/BoundaryCondition/BoundaryConditionList.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.BoundaryCondition.BoundaryCondition import BoundaryCondition @@ -50,7 +50,10 @@ class BoundaryConditionList(PamhyrModelListWithTab): return new def _db_save(self, execute, data=None): - execute("DELETE FROM boundary_condition") + execute( + "DELETE FROM boundary_condition " + f"WHERE scenario = {self._status.scenario_id}" + ) for tab in self._tabs: data["tab"] = tab diff --git a/src/Model/BoundaryCondition/BoundaryConditionTypes.py b/src/Model/BoundaryCondition/BoundaryConditionTypes.py index 10cccb13..b919a831 100644 --- a/src/Model/BoundaryCondition/BoundaryConditionTypes.py +++ b/src/Model/BoundaryCondition/BoundaryConditionTypes.py @@ -22,8 +22,12 @@ from Model.BoundaryCondition.BoundaryCondition import BoundaryCondition class NotDefined(BoundaryCondition): - 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,9 +38,12 @@ class NotDefined(BoundaryCondition): class PonctualContribution(BoundaryCondition): - def __init__(self, id: int = -1, name: str = "", status=None): + def __init__(self, id: int = -1, name: str = "", + status=None, owner_scenario=-1): super(PonctualContribution, self).__init__( - id=id, name=name, status=status) + id=id, name=name, status=status, + owner_scenario=owner_scenario + ) self._type = "PC" self._header = ["time", "discharge"] @@ -48,8 +55,12 @@ class PonctualContribution(BoundaryCondition): class TimeOverZ(BoundaryCondition): - def __init__(self, id: int = -1, name: str = "", status=None): - super(TimeOverZ, self).__init__(id=id, name=name, status=status) + def __init__(self, id: int = -1, name: str = "", + status=None, owner_scenario=-1): + super(TimeOverZ, self).__init__( + id=id, name=name, status=status, + owner_scenario=owner_scenario + ) self._type = "TZ" self._header = ["time", "z"] @@ -61,9 +72,12 @@ class TimeOverZ(BoundaryCondition): class TimeOverDischarge(BoundaryCondition): - def __init__(self, id: int = -1, name: str = "", status=None): + def __init__(self, id: int = -1, name: str = "", + status=None, owner_scenario=-1): super(TimeOverDischarge, self).__init__( - id=id, name=name, status=status) + id=id, name=name, status=status, + owner_scenario=owner_scenario + ) self._type = "TD" self._header = ["time", "discharge"] @@ -75,8 +89,12 @@ class TimeOverDischarge(BoundaryCondition): class ZOverDischarge(BoundaryCondition): - def __init__(self, id: int = -1, name: str = "", status=None): - super(ZOverDischarge, self).__init__(id=id, name=name, status=status) + def __init__(self, id: int = -1, name: str = "", + status=None, owner_scenario=-1): + super(ZOverDischarge, self).__init__( + id=id, name=name, status=status, + owner_scenario=owner_scenario + ) self._type = "ZD" self._header = ["z", "discharge"] @@ -92,8 +110,12 @@ class ZOverDischarge(BoundaryCondition): class Solid(BoundaryCondition): - def __init__(self, id: int = -1, name: str = "", status=None): - super(Solid, self).__init__(id=id, name=name, status=status) + def __init__(self, id: int = -1, name: str = "", + status=None, owner_scenario=-1): + super(Solid, self).__init__( + id=id, name=name, status=status, + owner_scenario=owner_scenario + ) self.d50 = 0.002 self.sigma = 1 diff --git a/src/Model/Friction/Friction.py b/src/Model/Friction/Friction.py index 30fe5d59..793dbfee 100644 --- a/src/Model/Friction/Friction.py +++ b/src/Model/Friction/Friction.py @@ -30,7 +30,8 @@ logger = logging.getLogger() class Friction(SQLSubModel): def __init__(self, id: int = -1, - status=None, owner_scenario=-1): + status=None, + owner_scenario=-1): super(Friction, self).__init__( id=id, status=status, owner_scenario=owner_scenario @@ -149,7 +150,7 @@ class Friction(SQLSubModel): table = execute( "SELECT pamhyr_id, deleted, begin_rk, end_rk, " + "begin_strickler, end_strickler, scenario " + - f"FROM friction " + + "FROM friction " + f"WHERE reach = {reach.pamhyr_id} " + f"AND scenario = {scenario.id} " + f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " + diff --git a/src/Model/Tools/PamhyrListExt.py b/src/Model/Tools/PamhyrListExt.py index ecf42459..e9d3c151 100644 --- a/src/Model/Tools/PamhyrListExt.py +++ b/src/Model/Tools/PamhyrListExt.py @@ -222,7 +222,7 @@ class PamhyrModelListWithTab(SQLSubModel): Returns: The size of the tab list """ - return len(self._tabs[lst]) + return len(self.get_tab(lst)) def get_tab(self, lst): """Get tab list (copy) from name @@ -233,14 +233,16 @@ class PamhyrModelListWithTab(SQLSubModel): Returns: Nothing """ - return self._tabs[lst].copy() + # return self._tabs[lst].copy() + return list( + filter( + lambda el: not el.is_deleted(), + self._tabs[lst] + ) + ) def get(self, lst, index): - return self._tabs[lst][index] - - def set(self, lst, index, new): - self._tabs[lst][index] = new - self._status.modified() + return self.get_tab(lst)[index] def new(self, lst, index): """Create new elements and add it to list @@ -255,32 +257,38 @@ class PamhyrModelListWithTab(SQLSubModel): raise NotImplementedMethodeError(self, self.new) def insert(self, lst, index, new): - """Insert element in tab + if new in self._tabs[lst]: + new.set_as_not_deleted() + else: + self._tabs[lst].insert(index, new) - Args: - lst: The tab name - index: The index of new element - new: The new elements - - Returns: - Nothing - """ - self._tabs[lst].insert(index, new) self._status.modified() - def delete(self, lst, els): - """Delete elements from specific tab + def undelete(self, lst, els): + """Delete a list of elements Args: lst: The tab name - els: The elements list + els: The list of elements Returns: Nothing """ for el in els: - self._tabs[lst].remove(el) - self._status.modified() + el.set_as_not_deleted() + + def delete(self, lst, els): + """Delete a list of elements + + Args: + lst: The tab name + els: The list of elements + + Returns: + Nothing + """ + for el in els: + el.set_as_deleted() def delete_i(self, lst, indexes): """Delete elements from specific tab diff --git a/src/View/BoundaryCondition/UndoCommand.py b/src/View/BoundaryCondition/UndoCommand.py index ee3b45df..81160f2f 100644 --- a/src/View/BoundaryCondition/UndoCommand.py +++ b/src/View/BoundaryCondition/UndoCommand.py @@ -94,10 +94,11 @@ class AddCommand(QUndoCommand): self._bcs = bcs self._tab = tab self._index = index + self._old = self._bcs.get(self._tab, self._index) self._new = None def undo(self): - self._bcs.delete_i(self._tab, [self._index]) + self._bcs.delete(self._tab, self._old) def redo(self): if self._new is None: