From 4199eb6ac54c4858a8778e0582d9509f3aa870cc Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Rouby Date: Fri, 20 Sep 2024 16:10:00 +0200 Subject: [PATCH] Geometry: Add scenario support. --- src/Model/Geometry/PointXYZ.py | 2 +- src/Model/Geometry/Profile.py | 66 ++++++++++-------- src/Model/Geometry/ProfileXYZ.py | 63 ++++++++++++----- src/Model/Geometry/Reach.py | 86 ++++++++++-------------- src/View/Geometry/Profile/UndoCommand.py | 10 ++- src/View/Geometry/UndoCommand.py | 20 +++--- 6 files changed, 133 insertions(+), 114 deletions(-) diff --git a/src/Model/Geometry/PointXYZ.py b/src/Model/Geometry/PointXYZ.py index 69131bef..38303df1 100644 --- a/src/Model/Geometry/PointXYZ.py +++ b/src/Model/Geometry/PointXYZ.py @@ -203,7 +203,7 @@ class PointXYZ(Point, SQLSubModel): owner_scenario=owner_scenario ) if deleted: - nd.set_as_deleted() + new.set_as_deleted() if sl == -1 or sl is None: new._sl = None diff --git a/src/Model/Geometry/Profile.py b/src/Model/Geometry/Profile.py index 3713473d..4638b2b5 100644 --- a/src/Model/Geometry/Profile.py +++ b/src/Model/Geometry/Profile.py @@ -32,10 +32,11 @@ class Profile(object): rk: float = 0.0, name: str = "", code1: int = 0, code2: int = 0, _type: str = "", reach=None, - status=None): - super(Profile, self).__init__(id) - - self._status = status + status=None, owner_scenario=-1): + super(Profile, self).__init__( + id=id, status=status, + owner_scenario=owner_scenario + ) self._num = int(num) self._code1 = int(code1) @@ -64,7 +65,12 @@ class Profile(object): if not isinstance(self._points, list): self._points = self._get_points_list() - return self._points + return list( + filter( + lambda p: not p.is_deleted(), + self._points + ) + ) def point(self, index): return self.points[index] @@ -84,7 +90,7 @@ class Profile(object): @num.setter def num(self, value: int): self._num = int(value) - self._status.modified() + self.modified() @property def code1(self): @@ -97,7 +103,7 @@ class Profile(object): @code1.setter def code1(self, value: int): self._code1 = int(value) - self._status.modified() + self.modified() @property def code2(self): @@ -110,7 +116,7 @@ class Profile(object): @code2.setter def code2(self, value: int): self._code2 = int(value) - self._status.modified() + self.modified() @property def nb_points(self): @@ -127,7 +133,7 @@ class Profile(object): @rk.setter def rk(self, value: float): self._rk = float(value) - self._status.modified() + self.modified() @property def name(self): @@ -140,7 +146,7 @@ class Profile(object): @name.setter def name(self, value: str): self._name = value.strip() - self._status.modified() + self.modified() @property def sl(self): @@ -153,7 +159,7 @@ class Profile(object): @sl.setter def sl(self, value): self._sl = value - self._status.modified() + self.modified() @property def profile_type(self): @@ -166,7 +172,7 @@ class Profile(object): @profile_type.setter def profile_type(self, value: str): self._profile_type = value - self._status.modified() + self.modified() def point(self, i: int): if i < len(self.points): @@ -193,21 +199,25 @@ class Profile(object): Returns: Nothing. """ - self.points.insert(index, point) - self._status.modified() + if point in self._points: + point.set_as_not_deleted() + else: + self.points.insert(index, point) + + self.modified() def delete_i(self, indexes: list): - self._points = list( + list( map( - lambda e: e[1], + lambda e: e[1].set_as_deleted(), filter( - lambda e: e[0] not in indexes, + lambda e: e[0] in indexes, enumerate(self.points) ) ) ) - self._status.modified() + self.modified() def delete_points(self, points): """Delete some elements in profile list @@ -218,13 +228,13 @@ class Profile(object): Returns: Nothing. """ - self._points = list( - filter( - lambda p: p not in points, - self.points + list( + map( + lambda p: p.set_as_deleted(), + points ) ) - self._status.modified() + self.modified() # Move @@ -234,7 +244,7 @@ class Profile(object): p = self.points p[index], p[next] = p[next], p[index] - self._status.modified() + self.modified() def move_down_point(self, index: int): if index >= 0: @@ -242,7 +252,7 @@ class Profile(object): p = self.points p[index], p[prev] = p[prev], p[index] - self._status.modified() + self.modified() # Sort @@ -259,7 +269,7 @@ class Profile(object): key=predicate, reverse=is_reversed ) - self._status.modified() + self.modified() @timer def sort_with_indexes(self, indexes: list): @@ -275,12 +285,12 @@ class Profile(object): ) ) ) - self._status.modified() + self.modified() @timer def reverse(self): self._points.reverse() - self._status.modified() + self.modified() # Sediment Layers diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py index ebc7aaf1..e8f4097d 100644 --- a/src/Model/Geometry/ProfileXYZ.py +++ b/src/Model/Geometry/ProfileXYZ.py @@ -47,7 +47,7 @@ class ProfileXYZ(Profile, SQLSubModel): num=0, nb_point: int = 0, code1: int = 0, code2: int = 0, - status=None): + status=None, owner_scenario=-1): """ProfileXYZ constructor Args: @@ -69,6 +69,7 @@ class ProfileXYZ(Profile, SQLSubModel): _type="XYZ", reach=reach, status=status, + owner_scenario=owner_scenario ) @classmethod @@ -76,6 +77,7 @@ class ProfileXYZ(Profile, SQLSubModel): execute(f""" CREATE TABLE geometry_profileXYZ{ext} ( {cls.create_db_add_pamhyr_id()}, + deleted BOOLEAN NOT NULL DEFAULT FALSE, ind INTEGER NOT NULL, name TEXT, reach INTEGER NOT NULL, @@ -121,6 +123,13 @@ class ProfileXYZ(Profile, SQLSubModel): cls._db_update_to_0_1_0(execute, data) + if major == "0" and minor == "1": + if int(release) < 2: + execute( + "ALTER TABLE geometry_profileXYZ " + + "ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE" + ) + return cls._update_submodel(execute, version, data) @classmethod @@ -194,35 +203,47 @@ class ProfileXYZ(Profile, SQLSubModel): @classmethod def _db_load(cls, execute, data=None): - profiles = [] status = data["status"] + scenario = data["scenario"] + loaded = data['loaded_pid'] reach = data["reach"] + if scenario is None: + return + table = execute( - "SELECT pamhyr_id, name, rk, num, code1, code2, sl " + + "SELECT pamhyr_id, deleted, name, rk, num, " + + "code1, code2, sl, scenario " + "FROM geometry_profileXYZ " + f"WHERE reach = {reach.id} " + + f"AND scenario = {scenario.id} " + + f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " + "ORDER BY ind ASC" ) for row in table: it = iter(row) - id = next(it) + pid = next(it) + deleted = (next(it) == 1) name = next(it) rk = next(it) num = next(it) code1 = next(it) code2 = next(it) sl = next(it) + owner_scenario = next(it) new = cls( - id=id, num=num, + id=pid, num=num, name=name, rk=rk, code1=code1, code2=code2, reach=reach, - status=status + status=status, + owner_scenario=owner_scenario ) + if deleted: + new.set_as_deleted() if sl == -1 or sl is None: new._sl = None @@ -237,9 +258,17 @@ class ProfileXYZ(Profile, SQLSubModel): data["profile"] = new new._points = PointXYZ._db_load(execute, data.copy()) + loaded.add(pid) yield new + data["scenario"] = scenario.parent + yield from cls._db_load(execute, data) + data["scenario"] = scenario + def _db_save(self, execute, data=None): + if not self.must_be_saved(): + return True + ok = True ind = data["ind"] @@ -247,25 +276,25 @@ class ProfileXYZ(Profile, SQLSubModel): execute( "INSERT OR REPLACE INTO " + - "geometry_profileXYZ(pamhyr_id, ind, name, reach, " + - "rk, num, code1, code2, sl) " + + "geometry_profileXYZ(pamhyr_id, deleted, ind, name, reach, " + + "rk, num, code1, code2, sl, scenario) " + "VALUES (" + - f"{self.pamhyr_id}, {ind}, '{self._db_format(self._name)}', " + + f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " + + f"{ind}, '{self._db_format(self._name)}', " + f"{self.reach.pamhyr_id}, {self.rk}, {self.num}, " + - f"{self.code1}, {self.code1}, {sl}" + + f"{self.code1}, {self.code1}, {sl}, {self._status.scenario_id}" + ")" ) - points = self.points - data["profile"] = self execute( "DELETE FROM geometry_pointXYZ " + - f"WHERE profile = {self.pamhyr_id}" + f"WHERE profile = {self.pamhyr_id} " + + f"AND scenario = {self._status.scenario_id}" ) ind = 0 - for point in points: + for point in self._points: data["ind"] = ind ok &= point._db_save(execute, data) ind += 1 @@ -380,7 +409,7 @@ class ProfileXYZ(Profile, SQLSubModel): for point in list_points: pt = PointXYZ(*point, profile=self, status=self._status) self.points.append(pt) - self._status.modified() + self.modified() def get_point_i(self, index: int) -> PointXYZ: """Get point at index. @@ -441,7 +470,7 @@ class ProfileXYZ(Profile, SQLSubModel): """ point_xyz = PointXYZ(0., 0., 0., profile=self, status=self._status) self.points.append(point_xyz) - self._status.modified() + self.modified() def insert(self, index: int): """Insert a new point at index. @@ -454,7 +483,7 @@ class ProfileXYZ(Profile, SQLSubModel): """ point = PointXYZ(0., 0., 0., profile=self, status=self._status) self.points.insert(index, point) - self._status.modified() + self.modified() return point def filter_isnan(self, lst): diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py index 59fc396c..66c98af1 100644 --- a/src/Model/Geometry/Reach.py +++ b/src/Model/Geometry/Reach.py @@ -80,15 +80,14 @@ class Reach(SQLSubModel): return new def _db_save(self, execute, data=None): - profiles = self.profiles - execute( "DELETE FROM geometry_profileXYZ " + - f"WHERE reach = {self.pamhyr_id}" + f"WHERE reach = {self.pamhyr_id} " + + f"AND scenario = {self._status.scenario_id}" ) ind = 0 - for profile in profiles: + for profile in self._profiles: data["ind"] = ind profile._db_save(execute, data) ind += 1 @@ -121,17 +120,14 @@ class Reach(SQLSubModel): return list(self._profiles) def __len__(self): - if not isinstance(self._profiles, list): - self._profiles = self._get_profiles_list() - - return len(self._profiles) + return len(self.profiles) @property def profiles(self): if not isinstance(self._profiles, list): self._profiles = self._get_profiles_list() - return self._profiles + return list(filter(lambda p: not p.is_deleted(), self._profiles)) @profiles.setter def profiles(self, profiles): @@ -159,15 +155,6 @@ class Reach(SQLSubModel): """ return self.profiles - def _update_profile_numbers(self): - """Update profiles index - - Returns: - Nothing. - """ - for ind, profile in enumerate(self.get_geometry()): - profile.num = ind + 1 - def insert(self, index: int): """Insert new profile in list @@ -180,9 +167,8 @@ class Reach(SQLSubModel): profile = ProfileXYZ(reach=self, status=self._status) self.profiles.insert(index, profile) - self._update_profile_numbers() - self._status.modified() + self.modified() return profile @@ -195,9 +181,12 @@ class Reach(SQLSubModel): Returns: Nothing. """ - self.profiles.insert(index, profile) - self._update_profile_numbers() - self._status.modified() + if profile in self._profiles: + self.undelete([profile]) + else: + self.profiles.insert(index, profile) + + self.modified() def delete(self, indexes): """Delete some elements in profile list @@ -208,24 +197,16 @@ class Reach(SQLSubModel): Returns: Nothing. """ - profiles = set( + list( map( - lambda e: e[1], + lambda p: p[1].set_as_deleted(), filter( lambda e: e[0] in indexes, enumerate(self.profiles) ) ) ) - - self._profiles = list( - filter( - lambda p: p not in profiles, - self.profiles - ) - ) - self._update_profile_numbers() - self._status.modified() + self.modified() def delete_profiles(self, profiles): """Delete some elements in profile list @@ -236,19 +217,24 @@ class Reach(SQLSubModel): Returns: Nothing. """ - self._profiles = list( - filter( - lambda p: p not in profiles, - self.profiles + list( + map( + lambda p: p.set_as_deleted(), + profiles ) ) - self._update_profile_numbers() - self._status.modified() + self.modified() + + def undelete(self, lst): + for el in lst: + el.set_as_not_deleted() + + self.modified() def purge(self): - self._profiles = [] - self._update_profile_numbers() - self._status.modified() + for el in self._profiles: + el.set_as_deleted() + self.modified() def move_up_profile(self, index: int): if index < len(self.profiles): @@ -256,7 +242,7 @@ class Reach(SQLSubModel): p = self.profiles p[index], p[next] = p[next], p[index] - self._status.modified() + self.modified() def move_down_profile(self, index: int): if index >= 0: @@ -264,7 +250,7 @@ class Reach(SQLSubModel): p = self.profiles p[index], p[prev] = p[prev], p[index] - self._status.modified() + self.modified() def get_x(self): return list( @@ -489,7 +475,7 @@ class Reach(SQLSubModel): self._compute_guidelines_cache(guide_set, named_points, complete, incomplete) - self._status.modified() + self.modified() return (complete, incomplete) def _map_guidelines_points(self, func, full=False): @@ -535,7 +521,7 @@ class Reach(SQLSubModel): key=lambda profile: profile.rk, reverse=is_reversed ) - self._status.modified() + self.modified() @timer def sort_with_indexes(self, indexes: list): @@ -551,7 +537,7 @@ class Reach(SQLSubModel): ) ) ) - self._status.modified() + self.modified() # Import/Export @@ -608,7 +594,6 @@ class Reach(SQLSubModel): profiles.append(prof) self.profiles = profiles + self.profiles - self._update_profile_numbers() self._recompute_rk() return profiles @@ -643,7 +628,7 @@ class Reach(SQLSubModel): prof.import_points(profile) imported_profiles.append(prof) - self._status.modified() + self.modified() except FileNotFoundError as e: logger.error(e) exception_message_box(e) @@ -653,7 +638,6 @@ class Reach(SQLSubModel): finally: self.profiles = imported_profiles + self.profiles - self._update_profile_numbers() return imported_profiles @timer diff --git a/src/View/Geometry/Profile/UndoCommand.py b/src/View/Geometry/Profile/UndoCommand.py index a381feee..17fc4cfc 100644 --- a/src/View/Geometry/Profile/UndoCommand.py +++ b/src/View/Geometry/Profile/UndoCommand.py @@ -93,7 +93,7 @@ class AddCommand(QUndoCommand): self._point = None def undo(self): - self._profile.delete_i([self._index]) + self._profile.delete_point([self._point]) def redo(self): if self._point is None: @@ -111,15 +111,13 @@ class DelCommand(QUndoCommand): self._points = [] for row in rows: - self._points.append((row, self._profile.point(row))) - self._points.sort() # Sort by row index + self._points.append(self._profile.point(row)) def undo(self): - for row, point in self._points: - self._profile.insert_point(row, point) + self._profile.undelete(self._points) def redo(self): - self._profile.delete_i(self._rows) + self._profile.delete(self._points) class SortCommand(QUndoCommand): diff --git a/src/View/Geometry/UndoCommand.py b/src/View/Geometry/UndoCommand.py index d3cc29ef..b8ef2697 100644 --- a/src/View/Geometry/UndoCommand.py +++ b/src/View/Geometry/UndoCommand.py @@ -95,15 +95,13 @@ class DelCommand(QUndoCommand): self._profiles = [] for row in rows: - self._profiles.append((row, self._reach.profile(row))) - self._profiles.sort() + self._profiles.append(self._reach.profile(row)) def undo(self): - for row, profile in self._profiles: - self._reach.insert_profile(row, profile) + self._reach.undelete(self._profiles) def redo(self): - self._reach.delete(self._rows) + self._reach.delete_profiles(self._profiles) class SortCommand(QUndoCommand): @@ -300,13 +298,13 @@ class ShiftCommand(QUndoCommand): def undo(self): for i in self._rows: profile = self._reach.profiles[i] - self._reach.profiles[i].shift(-self._dx, - -self._dy, - -self._dz) + self._reach.profiles[i].shift( + -self._dx, -self._dy, -self._dz + ) def redo(self): for i in self._rows: profile = self._reach.profiles[i] - self._reach.profiles[i].shift(self._dx, - self._dy, - self._dz) + self._reach.profiles[i].shift( + self._dx, self._dy, self._dz + )