diff --git a/src/Model/Network/Edge.py b/src/Model/Network/Edge.py index 9865e95b..086902c6 100644 --- a/src/Model/Network/Edge.py +++ b/src/Model/Network/Edge.py @@ -24,10 +24,8 @@ class Edge(PamhyrID): def __init__(self, id: int, name: str, node1: Node = None, node2: Node = None, - status=None): - super(Edge, self).__init__(id) - - self._status = status + status=None, **kwargs): + super(Edge, self).__init__(id=id, status=status, **kwargs) self._name = name self.node1 = node1 @@ -63,7 +61,7 @@ class Edge(PamhyrID): elif name == "enable": self._enable = value - self._status.modified() + self.modified() @property def id(self): @@ -79,16 +77,19 @@ class Edge(PamhyrID): def is_enable(self): return self._enable + def is_deleted(self): + return False + def enable(self, enable=True): self._enable = enable - self._status.modified() + self.modified() def disable(self): self._enable = False - self._status.modified() + self.modified() def reverse(self): tmp = self.node1 self.node1 = self.node2 self.node2 = tmp - self._status.modified() + self.modified() diff --git a/src/Model/Network/Graph.py b/src/Model/Network/Graph.py index 69ded845..47bc632c 100644 --- a/src/Model/Network/Graph.py +++ b/src/Model/Network/Graph.py @@ -23,10 +23,8 @@ from Model.Network.Edge import Edge class Graph(object): - def __init__(self, status=None): - super(Graph, self).__init__() - - self._status = status + def __init__(self, **kwargs): + super(Graph, self).__init__(**kwargs) self._node_ctor = Node self._edge_ctor = Edge @@ -38,13 +36,13 @@ class Graph(object): return f"Graph {{nodes: {self._nodes}, edges: {self._edges}}}" def nodes(self): - return self._nodes + return list(filter(lambda n: not n.is_deleted(), self._nodes)) def nodes_names(self): - return list(map(lambda n: n.name, self._nodes)) + return list(map(lambda n: n.name, self.nodes())) def edges(self): - return self._edges + return list(filter(lambda e: not e.is_deleted(), self._edges)) def enable_edges(self): return list( @@ -55,43 +53,43 @@ class Graph(object): """Return a generator""" return filter( lambda e: e.is_enable(), - self._edges + self.edges() ) def edges_names(self): - return list(map(lambda e: e.name, self._edges)) + return list(map(lambda e: e.name, self.edges())) def nodes_counts(self): - return len(self._nodes) + return len(self.nodes()) def edges_counts(self): - return len(self._edges) + return len(self.edges()) def enable_nodes_counts(self): return reduce( lambda acc, n: acc + 1 if self.is_enable_node(n) else acc, - self._nodes, + self.nodes(), 0 ) def enable_edges_counts(self): return reduce( lambda acc, e: acc + 1 if e.is_enable() else acc, - self._edges, + self.edges(), 0 ) def is_node_exists(self, node_name): return reduce( lambda acc, n: (acc or (n.name == node_name)), - self._nodes, + self.nodes(), False ) def is_edge_exists(self, edge_name): return reduce( lambda acc, e: (acc or (e.name == edge_name)), - self._edges, + self.edges(), False ) @@ -99,7 +97,7 @@ class Graph(object): node = list( filter( lambda n: n.name == node_name, - self._nodes + self.nodes() ) ) @@ -112,7 +110,7 @@ class Graph(object): edge = list( filter( lambda e: e.name == edge_name, - self._edges + self.edges() ) ) @@ -123,16 +121,18 @@ class Graph(object): def _create_node(self, x: float, y: float): node = self._node_ctor( - -1, - "", - x=x, y=y, + id=-1, name="", x=x, y=y, status=self._status ) return node def _add_node(self, node): - self._nodes.append(node) - self._status.modified() + if node in self._nodes: + node.set_as_not_deleted() + else: + self._nodes.append(node) + + self.modified() return node def add_node(self, x: float = 0.0, y: float = 0.0): @@ -143,9 +143,9 @@ class Graph(object): return self._add_node(node) def remove_node(self, node): - self._nodes.remove(node) + node.set_as_deleted() self._remove_associated_edge(node) - self._status.modified() + self.modified() def _remove_associated_edge(self, node: str): edges = list( @@ -176,10 +176,12 @@ class Graph(object): # e.node2 == edge.node2), # self._edges)): # return None + if edge in self._edges: + edge.set_as_not_deleted() + else: + self._edges.append(edge) - self._edges.append(edge) - - self._status.modified() + self.modified() return edge def add_edge(self, n1: Node, n2: Node): @@ -193,8 +195,8 @@ class Graph(object): return self._create_edge(n1, n2) def remove_edge(self, edge): - self._edges.remove(edge) - self._status.modified() + edge.set_as_deleted() + self.modified() def is_upstream_node(self, node): return reduce( diff --git a/src/Model/Network/Node.py b/src/Model/Network/Node.py index ce595b90..1c66a522 100644 --- a/src/Model/Network/Node.py +++ b/src/Model/Network/Node.py @@ -23,7 +23,7 @@ from Model.Network.Point import Point class Node(PamhyrID): def __init__(self, id: int, name: str, x: float = 0.0, y: float = 0.0, - status=None): + status=None, **kwargs): super(Node, self).__init__(id) self._status = status @@ -55,6 +55,9 @@ class Node(PamhyrID): self._status.modified() + def is_deleted(self): + return False + @property def name(self): return self._name diff --git a/src/Model/River.py b/src/Model/River.py index 8c06bdde..f3d37f8d 100644 --- a/src/Model/River.py +++ b/src/Model/River.py @@ -54,15 +54,13 @@ logger = logging.getLogger() class RiverNode(Node, SQLSubModel): _sub_classes = [] - def __init__(self, id: int, name: str, - x: float, y: float, - status=None): - self._pamhyr_id = self.get_new_pamhyr_id(id) - + def __init__(self, id: int = -1, name: str = "", + x: float = 0.0, y: float = 0.0, + status=None, owner_scenario=-1): super(RiverNode, self).__init__( - id, name, - x, y, - status=status + id, name, x, y, + status=status, + owner_scenario=owner_scenario ) self._locker = None @@ -72,6 +70,7 @@ class RiverNode(Node, SQLSubModel): execute(f""" CREATE TABLE river_node{ext}( {cls.create_db_add_pamhyr_id()}, + deleted BOOLEAN NOT NULL DEFAULT FALSE, name TEXT NOT NULL, x REAL NOT NULL, y REAL NOT NULL, @@ -93,6 +92,13 @@ class RiverNode(Node, SQLSubModel): if major == minor == "0": cls._db_update_to_0_1_0(execute, data=data) + if major == "0" and minor == "1": + if int(release) < 2: + execute( + "ALTER TABLE river_node " + + "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" + ) + return cls._update_submodel(execute, version, data) @classmethod @@ -118,27 +124,65 @@ class RiverNode(Node, SQLSubModel): @classmethod def _db_load(cls, execute, data=None): nodes = [] + scenario = data["scenario"] + loaded = data['loaded_pid'] + + if scenario is None: + return nodes + + table = execute( + "SELECT pamhyr_id, deleted, name, x, y, scenario " + + "FROM river_node " + + f"WHERE scenario = {scenario.id} " + + f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " + ) - table = execute("SELECT pamhyr_id, name, x, y FROM river_node") for row in table: - nodes.append( - cls(*row, status=data["status"]) + it = iter(row) + + pid = next(it) + deleted = (next(it) == 1) + name = next(it) + x = next(it) + y = next(it) + owner_scenario = next(it) + + + node = cls( + id=pid, + name=name, x=x, y=y, + status=data["status"], + owner_scenario=owner_scenario ) + if deleted: + node.set_as_deleted() + + loaded.add(pid) + nodes.append(node) + + data["scenario"] = scenario.parent + nodes += cls._db_load(execute, data) + data["scenario"] = scenario return nodes def _db_save(self, execute, data=None): execute( - "INSERT OR REPLACE INTO river_node(pamhyr_id, name, x, y) " + + "INSERT OR REPLACE INTO river_node(" + + "pamhyr_id, deleted, name, x, y, scenario" + + ") " + "VALUES (" + - f"{self._pamhyr_id}, " + + f"{self._pamhyr_id}, {self._db_format(self.is_deleted())}, " + f"'{self._db_format(self.name)}', " + - f"{self.x}, {self.y}" + + f"{self.x}, {self.y}, {self._status.scenario_id}" + ")" ) return True + def is_deleted(self): + return self._deleted + @property def locker(self): return self._locker @@ -154,16 +198,15 @@ class RiverReach(Edge, SQLSubModel): FrictionList, ] - def __init__(self, id: str, name: str, + def __init__(self, id: str = -1, name: str = "", node1: RiverNode = None, node2: RiverNode = None, - status=None): - # self._pamhyr_id = self.get_new_pamhyr_id(id) - + status=None, owner_scenario=-1): super(RiverReach, self).__init__( id, name, node1, node2, - status=status + status=status, + owner_scenario=owner_scenario ) self._reach = Reach(status=self._status, parent=self) @@ -174,6 +217,7 @@ class RiverReach(Edge, SQLSubModel): execute(f""" CREATE TABLE river_reach{ext} ( {cls.create_db_add_pamhyr_id()}, + deleted BOOLEAN NOT NULL DEFAULT FALSE, name TEXT NOT NULL, enabled BOOLEAN NOT NULL, node1 INTEGER, @@ -198,6 +242,13 @@ class RiverReach(Edge, 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 river_reach " + + "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" + ) + return cls._update_submodel(execute, version, data) @classmethod @@ -246,20 +297,30 @@ class RiverReach(Edge, SQLSubModel): @classmethod def _db_load(cls, execute, data=None): reachs = [] + scenario = data["scenario"] + loaded = data['loaded_pid'] + + if scenario is None: + return reachs table = execute( - "SELECT pamhyr_id, name, enabled, node1, node2 FROM river_reach" + "SELECT pamhyr_id, deleted, name, enabled, " + + "node1, node2, scenario " + + "FROM river_reach " + + f"WHERE scenario = {scenario.id} " + + f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})" ) for row in table: it = iter(row) - # Create new reach pid = next(it) + deleted = (next(it) == 1) name = next(it) enabled = (next(it) == 1) node1_pid = next(it) node2_pid = next(it) + owner_scenario = next(it) # Get nodes corresponding to db foreign key id node1 = next( @@ -273,26 +334,41 @@ class RiverReach(Edge, SQLSubModel): ) ) - new = cls(pid, name, node1, node2, status=data["status"]) + new = cls( + id=pid, name=name, node1=node1, node2=node2, + status=data["status"], + owner_scenario=owner_scenario + ) new.enable(enable=enabled) + if deleted: + nd.set_as_deleted() data["reach"] = new - new._reach = Reach._db_load(execute, data) new._frictions = FrictionList._db_load(execute, data) + loaded.add(pid) reachs.append(new) + data["scenario"] = scenario.parent + reachs += cls._db_load(execute, data) + data["scenario"] = scenario + return reachs def _db_save(self, execute, data=None): execute( "INSERT OR REPLACE INTO " + - "river_reach(pamhyr_id, name, enabled, node1, node2) " + + "river_reach(" + + "pamhyr_id, deleted, name, enabled, " + + "node1, node2, scenario" + + ") " + "VALUES (" + - f"{self.pamhyr_id}, '{self._db_format(self._name)}', " + + f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " + + f"'{self._db_format(self._name)}', " + f"{self._db_format(self.is_enable())}," - f"{self.node1.pamhyr_id}, {self.node2.pamhyr_id}" + + f"{self.node1.pamhyr_id}, {self.node2.pamhyr_id}, " + + f"{self._status.scenario_id}" + ")" ) @@ -301,6 +377,9 @@ class RiverReach(Edge, SQLSubModel): objs = [self._reach, self._frictions] return self._save_submodel(execute, objs, data) + def is_deleted(self): + return self._deleted + @property def reach(self): return self._reach @@ -327,7 +406,9 @@ class River(Graph, SQLSubModel): ] def __init__(self, status=None): - super(River, self).__init__(status=status) + super(River, self).__init__( + status=status + ) # Replace Node and Edge ctor by custom ctor self._node_ctor = RiverNode @@ -359,7 +440,7 @@ class River(Graph, SQLSubModel): @classmethod def _db_load(cls, execute, data=None): - new = cls(data["status"]) + new = cls(status=data["status"]) # Stricklers (Stricklers is load in first because it's needed # for reachs) @@ -462,12 +543,13 @@ class River(Graph, SQLSubModel): execute( f"DELETE FROM {table} " + "WHERE " + + f"scenario = {self._status.scenario_id} AND (" + " OR ".join( map( lambda n: f"( pamhyr_id <> {n.pamhyr_id} )", els ) - ) + ) + ")" ) except Exception as e: logger_exception(e)