From 9ba94fc648e13dcc1746083dbea747dabad5625c Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Mon, 22 Dec 2025 08:36:20 -0800 Subject: [PATCH 01/14] add delete datasheet in library class --- pysyncrosim/helper.py | 26 ++++++++++++++++++- pysyncrosim/library.py | 59 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/pysyncrosim/helper.py b/pysyncrosim/helper.py index 0b22e4b..05d4194 100644 --- a/pysyncrosim/helper.py +++ b/pysyncrosim/helper.py @@ -290,7 +290,7 @@ def _delete_folder(library, fid, session=None, force=False): if answer == "Y": # Retrieve Folder DataFrame - args = ["--lib=%s" % library.location, "--list", "--folders"] + args = [f"--lib={library.location}", "--list", "--folders"] folder_data = session._Session__call_console(args, decode=True, csv=True) folder_df = pd.read_csv(io.StringIO(folder_data)) @@ -301,3 +301,27 @@ def _delete_folder(library, fid, session=None, force=False): args = ["--delete", "--folder", f"--lib={library.location}", f"--fid={fid}", "--force"] session._Session__call_console(args) + +def _delete_data(library, datasheet, pid=None, sid=None, ids=None, session=None, force=False): + + if session is None: + session = ps.Session() + + if force is False: + answer = input(f"Are you sure you want to delete data {ids} from {datasheet} (Y/N)?") + else: + answer = "Y" + + if answer == "Y": + + # Delete Data using Console + args = ["--delete", "--data", f"--lib={library.location}", f"--sheet={datasheet}", "--force"] + + if pid is not None: + args += [f"--pid={pid}"] + if sid is not None: + args += [f"--sid={sid}"] + if ids is not None: + args += [f"--ids={ids}"] + + session._Session__call_console(args) diff --git a/pysyncrosim/library.py b/pysyncrosim/library.py index b42aea2..02ad066 100644 --- a/pysyncrosim/library.py +++ b/pysyncrosim/library.py @@ -614,7 +614,10 @@ def datasheets(self, name=None, summary=True, optional=False, empty=False, return ds - def delete(self, project=None, scenario=None, folder=None, force=False, remove_backup=False, remove_publish=False, remove_custom_folders=False): + def delete(self, project=None, scenario=None, folder=None, data=None, + datasheet=None, pid=None, sid=None, ids=None, force=False, + remove_backup=False, remove_publish=False, + remove_custom_folders=False): """ Deletes a SyncroSim class instance. @@ -627,16 +630,38 @@ def delete(self, project=None, scenario=None, folder=None, force=False, remove_b If called from a Scenario class instance, specify the Scenario to delete. The default is None. folder : Folder, or Int, optional - If called from a Library class instance, specify the folder to delete. The default is None. + If called from a Library class instance, specify the folder to + delete. The default is None. + data : Logical, optional + If set to True, will delete data from a datasheet. The default is + None. + datasheet : string, optional + If called from the Library class instance, specify the Datasheet to + delete data. Required when data is True. The default is None. + pid : Int, optional + Project ID for the datasheet. Not required for a library-scoped + datasheet. The default is None. + sid : Int, optional + Scenario ID for the datasheet. Not required for a library-scoped + datasheet. The default is None. + ids : Str or Int, optional + Primary key IDs for the rows to delete. If None, deletes all data. + The default is None. force : Logical, optional If set to True, does not ask user before deleting SyncroSim class instance. The default is False. remove_backup : Logical, optional - If True, will remove the backup folder when deleting a Library. Default is False. + If True, will remove the backup folder when deleting a Library. + Default is False. remove_publish : Logical, optional - If True, will remove the publish folder when deleting a Library. Default is False. + If True, will remove the publish folder when deleting a Library. + Default is False. remove_custom_folders : Logical, optional - If True and custom folders have been configured for a Library, then will remove the custom publish and/or backup folders when deleting a Library. Note that the remove_publish and remove_backup arguments must also be set to True to remove the respective custom folders. Default is False. + If True and custom folders have been configured for a Library, then + will remove the custom publish and/or backup folders when deleting + a Library. Note that the remove_publish and remove_backup arguments + must also be set to True to remove the respective custom folders. + Default is False. Returns ------- @@ -672,11 +697,24 @@ def delete(self, project=None, scenario=None, folder=None, force=False, remove_b raise TypeError("remove_publish must be a Logical") if not isinstance(remove_custom_folders, bool): raise TypeError("remove_custom_folders must be a Logical") + + if data is not None and not isinstance(data, bool): + raise TypeError("data must be a Logical") + if datasheet is not None and not isinstance(datasheet, str): + raise TypeError("datasheet must be an String") + if pid is not None and not isinstance(pid, int) and not isinstance( + pid, np.int64): + raise TypeError("pid must be an Integer") + if sid is not None and not isinstance(sid, int) and not isinstance( + sid, np.int64): + raise TypeError("sid must be an Integer") if project is None and scenario is None and folder is None: helper._delete_library(name = self.location, session=self.session, - force=force, remove_backup=remove_backup, remove_publish=remove_publish, remove_custom_folders=remove_custom_folders) + force=force, remove_backup=remove_backup, + remove_publish=remove_publish, + remove_custom_folders=remove_custom_folders) elif project is not None and scenario is None: @@ -725,6 +763,15 @@ def delete(self, project=None, scenario=None, folder=None, force=False, remove_b raise TypeError("folder must be a Folder instance or Integer") helper._delete_folder(library=self, fid=fid, session=self.session, force=force) + + elif data is not None: + + if datasheet is None: + raise ValueError("datasheet name is required") + + helper._delete_data(library=self, datasheet=datasheet, pid=pid, + sid=sid, ids=ids, session=self.session, + force=force) def save_datasheet(self, name, data, append=False, force=False, scope="Library", *ids): From d44c0df37376ae4ee8b05d56caa2a9f1e7742fc3 Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Mon, 22 Dec 2025 11:56:23 -0800 Subject: [PATCH 02/14] updates to delete function --- pysyncrosim/helper.py | 2 +- pysyncrosim/library.py | 4 +-- tests/test_pysyncrosim.py | 69 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/pysyncrosim/helper.py b/pysyncrosim/helper.py index 05d4194..9f5eef3 100644 --- a/pysyncrosim/helper.py +++ b/pysyncrosim/helper.py @@ -322,6 +322,6 @@ def _delete_data(library, datasheet, pid=None, sid=None, ids=None, session=None, if sid is not None: args += [f"--sid={sid}"] if ids is not None: - args += [f"--ids={ids}"] + args += [f'--ids="{ids}"'] session._Session__call_console(args) diff --git a/pysyncrosim/library.py b/pysyncrosim/library.py index 02ad066..904e848 100644 --- a/pysyncrosim/library.py +++ b/pysyncrosim/library.py @@ -701,7 +701,7 @@ def delete(self, project=None, scenario=None, folder=None, data=None, if data is not None and not isinstance(data, bool): raise TypeError("data must be a Logical") if datasheet is not None and not isinstance(datasheet, str): - raise TypeError("datasheet must be an String") + raise TypeError("datasheet must be a String") if pid is not None and not isinstance(pid, int) and not isinstance( pid, np.int64): raise TypeError("pid must be an Integer") @@ -709,7 +709,7 @@ def delete(self, project=None, scenario=None, folder=None, data=None, sid, np.int64): raise TypeError("sid must be an Integer") - if project is None and scenario is None and folder is None: + if project is None and scenario is None and folder is None and datasheet is None: helper._delete_library(name = self.location, session=self.session, force=force, remove_backup=remove_backup, diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index 601498e..3870940 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -333,6 +333,7 @@ def test_library_delete(): mySession = ps.Session(session_path) myLibrary = ps.library(name=lib_path, overwrite=True, session=mySession) myProject = myLibrary.projects(name="test") + myScenario = myLibrary.scenarios(name="test") myFolder = myProject.folders(folder="test-folder") myFolder2 = myProject.folders(folder="test-folder2") fid = myFolder2.folder_id @@ -357,7 +358,9 @@ def test_library_delete(): with pytest.raises(TypeError, match="remove_publish must be a Logical"): myLibrary.delete(force=True, remove_publish="True") - with pytest.raises(TypeError, match="remove_custom_folders must be a Logical"): + with pytest.raises( + TypeError, + match="remove_custom_folders must be a Logical"): myLibrary.delete(force=True, remove_custom_folders="True") with pytest.raises(ValueError, match="Project ID 2 does not exist"): @@ -372,26 +375,86 @@ def test_library_delete(): with pytest.raises(ValueError, match="scenario dne does not exist"): myLibrary.delete(scenario="dne") - with pytest.raises(TypeError, match="folder must be a Folder instance or Integer"): + with pytest.raises( + TypeError, + match="folder must be a Folder instance or Integer"): myLibrary.delete(folder="folder") with pytest.raises(ValueError, match="Folder ID 50 does not exist"): myLibrary.delete(folder=50, force=True) + + with pytest.raises(TypeError, match="data must be a Logical"): + myLibrary.delete(data="True") + + with pytest.raises(TypeError, match="datasheet must be a String"): + myLibrary.delete(data=True, datasheet=1) + + with pytest.raises(TypeError, match="pid must be an Integer"): + myLibrary.delete(data=True, datasheet="core_Backup", pid="1") + with pytest.raises(TypeError, match="sid must be an Integer"): + myLibrary.delete(data=True, datasheet="core_Backup", sid="1") + + with pytest.raises(ValueError, match="datasheet name is required"): + myLibrary.delete(data=True, datasheet="") + # Test delete folder from folder name myLibrary.delete(folder=myFolder, force=True) assert myFolder.folder_id not in myLibrary.folders()["Id"].values + # Test delete folder from folder ID myLibrary.delete(folder=fid, force=True) assert fid not in myLibrary.folders()["Id"].values - + + # Test delete project myLibrary.delete(project="test", force=True) assert myLibrary._Library__projects.empty assert "test" not in myLibrary.projects().Name.values + # Test delete scenario myLibrary.scenarios(name="test") myLibrary.delete(scenario="test", force=True) assert "test" not in myLibrary.scenarios().Name.values + +def test_library_delete_datasheet(): + + mySession = ps.Session(session_path) + myLibrary = ps.library(name=lib_path, overwrite=True, + packages=["stsim"], session=mySession) + myProject = myLibrary.projects(name="test") + myScenario = myLibrary.scenarios(name="test") + test_data = pd.DataFrame({ + "Name": ["a1", "a2", "a3"], + "Id": [1, 2, 3], + "Description": ["test1", "test2", "test3"] + }) + + # Add datasheet to project + myProject.save_datasheet(name = "stsim_Stratum", data=test_data) + saved_data = myProject.datasheets(name="stsim_Stratum") + assert len(saved_data) == 3 + + # Test delete datasheet from project + myLibrary.delete(data=True, datasheet="stsim_Stratum", pid=myProject.pid, force=True) + assert myProject.datasheets(name="stsim_Stratum").empty + + # Add back datasheet for next test + myProject.save_datasheet(name = "stsim_Stratum", data=test_data) + saved_data = myProject.datasheets(name="stsim_Stratum") + assert len(saved_data) == 3 + + # Test delete datasheet by row ID + ids_to_delete = f"{saved_data.iloc[0]["Id"]},{saved_data.iloc[1]["Id"]}" + myLibrary.delete(data=True, datasheet="stsim_Stratum", pid=myProject.pid, + ids=ids_to_delete, force=True) + remaining_data = myProject.datasheets(name="stsim_Stratum") + assert len(remaining_data) == 1 + # assert remaining_data.iloc[0]["Name"] == "a3" + + # Test delete from scenario + myLibrary.delete(data=True, datasheet="stsim_RunControl", + sid=myScenario.sid, force=True) + assert myScenario.datasheets(name="stsim_RunControl").empty def test_library_save_datasheet(): From 6e869a87729e0ca701bbdfc485687d456e66cb0d Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Mon, 22 Dec 2025 13:39:01 -0800 Subject: [PATCH 03/14] test library delete --- pysyncrosim/library.py | 3 +- ...mple-2.ssimbak => spatial-example.ssimbak} | Bin tests/test_pysyncrosim.py | 26 ++++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) rename tests/{spatial-example-2.ssimbak => spatial-example.ssimbak} (100%) diff --git a/pysyncrosim/library.py b/pysyncrosim/library.py index 904e848..1ff718a 100644 --- a/pysyncrosim/library.py +++ b/pysyncrosim/library.py @@ -614,8 +614,7 @@ def datasheets(self, name=None, summary=True, optional=False, empty=False, return ds - def delete(self, project=None, scenario=None, folder=None, data=None, - datasheet=None, pid=None, sid=None, ids=None, force=False, + def delete(self, project=None, scenario=None, pid=None, force=False, remove_backup=False, remove_publish=False, remove_custom_folders=False): """ diff --git a/tests/spatial-example-2.ssimbak b/tests/spatial-example.ssimbak similarity index 100% rename from tests/spatial-example-2.ssimbak rename to tests/spatial-example.ssimbak diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index 3870940..2d0b18d 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -11,7 +11,7 @@ temp_path = tempfile.TemporaryDirectory() session_path = None test_lib_path = os.path.join(temp_path.name, "stsimLibrary.ssim") -lib_name = "spatial-example-2.ssim" +lib_name = "spatial-example.ssim" git_repo_path = "C:/Users/VickiZhang/Documents/GH_ApexRMS" lib_path = os.path.join(git_repo_path, "pysyncrosim/tests", lib_name) lib_backup_path = os.path.join(git_repo_path, "pysyncrosim/tests", "spatial-example.ssimbak") @@ -375,6 +375,7 @@ def test_library_delete(): with pytest.raises(ValueError, match="scenario dne does not exist"): myLibrary.delete(scenario="dne") + # folder should be moved but should work with pytest.raises( TypeError, match="folder must be a Folder instance or Integer"): @@ -383,20 +384,21 @@ def test_library_delete(): with pytest.raises(ValueError, match="Folder ID 50 does not exist"): myLibrary.delete(folder=50, force=True) - with pytest.raises(TypeError, match="data must be a Logical"): - myLibrary.delete(data="True") + # move + # with pytest.raises(TypeError, match="data must be a Logical"): + # myLibrary.delete(data="True") - with pytest.raises(TypeError, match="datasheet must be a String"): - myLibrary.delete(data=True, datasheet=1) + # with pytest.raises(TypeError, match="datasheet must be a String"): + # myLibrary.delete(data=True, datasheet=1) - with pytest.raises(TypeError, match="pid must be an Integer"): - myLibrary.delete(data=True, datasheet="core_Backup", pid="1") + # with pytest.raises(TypeError, match="pid must be an Integer"): + # myLibrary.delete(data=True, datasheet="core_Backup", pid="1") - with pytest.raises(TypeError, match="sid must be an Integer"): - myLibrary.delete(data=True, datasheet="core_Backup", sid="1") + # with pytest.raises(TypeError, match="sid must be an Integer"): + # myLibrary.delete(data=True, datasheet="core_Backup", sid="1") - with pytest.raises(ValueError, match="datasheet name is required"): - myLibrary.delete(data=True, datasheet="") + # with pytest.raises(ValueError, match="datasheet name is required"): + # myLibrary.delete(data=True, datasheet="") # Test delete folder from folder name myLibrary.delete(folder=myFolder, force=True) @@ -582,7 +584,7 @@ def test_library_compact(): size_before = os.path.getsize(myLibrary.location) compactLibrary = myLibrary.compact() - size_after = os.path.getsize(compactLibrary) + size_after = os.path.getsize(myLibrary.location) assert size_before > size_after From b02bacd3cfbbe894e93f32402b5aacf5ae2061fe Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Mon, 22 Dec 2025 15:19:34 -0800 Subject: [PATCH 04/14] add arguments to library, project, scenario scope --- pysyncrosim/helper.py | 8 ++++++-- pysyncrosim/library.py | 29 +++++++++++++++++------------ pysyncrosim/project.py | 20 ++++++++++++++++---- pysyncrosim/scenario.py | 17 ++++++++++++++--- tests/test_pysyncrosim.py | 19 ++----------------- 5 files changed, 55 insertions(+), 38 deletions(-) diff --git a/pysyncrosim/helper.py b/pysyncrosim/helper.py index 9f5eef3..86a5b61 100644 --- a/pysyncrosim/helper.py +++ b/pysyncrosim/helper.py @@ -302,13 +302,17 @@ def _delete_folder(library, fid, session=None, force=False): session._Session__call_console(args) -def _delete_data(library, datasheet, pid=None, sid=None, ids=None, session=None, force=False): +def _delete_data(library, datasheet, pid=None, sid=None, ids=None, + session=None, force=False): if session is None: session = ps.Session() if force is False: - answer = input(f"Are you sure you want to delete data {ids} from {datasheet} (Y/N)?") + if ids is not None: + answer = input(f"Are you sure you want to delete rows {ids} from {datasheet} (Y/N)?") + else: + answer = input(f"Are you sure you want to delete all data from {datasheet} (Y/N)?") else: answer = "Y" diff --git a/pysyncrosim/library.py b/pysyncrosim/library.py index 1ff718a..3c0fa8c 100644 --- a/pysyncrosim/library.py +++ b/pysyncrosim/library.py @@ -614,8 +614,9 @@ def datasheets(self, name=None, summary=True, optional=False, empty=False, return ds - def delete(self, project=None, scenario=None, pid=None, force=False, - remove_backup=False, remove_publish=False, + def delete(self, project=None, scenario=None, folder=None, + pid=None, sid=None, data=None, datasheet=None, ids=None, + force=False, remove_backup=False, remove_publish=False, remove_custom_folders=False): """ Deletes a SyncroSim class instance. @@ -631,21 +632,21 @@ def delete(self, project=None, scenario=None, pid=None, force=False, folder : Folder, or Int, optional If called from a Library class instance, specify the folder to delete. The default is None. - data : Logical, optional - If set to True, will delete data from a datasheet. The default is - None. - datasheet : string, optional - If called from the Library class instance, specify the Datasheet to - delete data. Required when data is True. The default is None. pid : Int, optional Project ID for the datasheet. Not required for a library-scoped datasheet. The default is None. sid : Int, optional Scenario ID for the datasheet. Not required for a library-scoped datasheet. The default is None. + data : Logical, optional + If set to True, will delete data from a datasheet. The default is + None. + datasheet : string, optional + If called from the Library class instance, specify the Datasheet to + delete data. Required when data is True. The default is None. ids : Str or Int, optional - Primary key IDs for the rows to delete. If None, deletes all data. - The default is None. + IDs of the rows to delete. If None, deletes all data. The default + is None. force : Logical, optional If set to True, does not ask user before deleting SyncroSim class instance. The default is False. @@ -707,8 +708,12 @@ def delete(self, project=None, scenario=None, pid=None, force=False, if sid is not None and not isinstance(sid, int) and not isinstance( sid, np.int64): raise TypeError("sid must be an Integer") + if ids is not None and not isinstance(ids, str) and not isinstance( + ids, int) and not isinstance(ids, np.int64): + raise TypeError("ids must be a String or Integer") - if project is None and scenario is None and folder is None and datasheet is None: + if project is None and scenario is None and folder is None and + datasheet is None: helper._delete_library(name = self.location, session=self.session, force=force, remove_backup=remove_backup, @@ -964,7 +969,7 @@ def compact(self): try: args = ["--compact", f"--lib={self.location}"] self.session._Session__call_console(args) - return self.location + return None except RuntimeError as e: print(e) diff --git a/pysyncrosim/project.py b/pysyncrosim/project.py index 52e3b7f..ca68c0e 100644 --- a/pysyncrosim/project.py +++ b/pysyncrosim/project.py @@ -297,25 +297,37 @@ def datasheets(self, name=None, summary=True, optional=False, empty=False, return_hidden, self.pid) return self.__datasheets - def delete(self, scenario=None, force=False): + def delete(self, scenario=None, data=None, datasheet=None, ids=None, + force=False): """ - Deletes a Project or Scenario. + Deletes a Project, Scenario, or data from a Project scope. Parameters ---------- scenario : Scenario, String, or Int, optional Scenario to delete. The default is None. + data : Logical, optional + If True, will delete data from a Project-scoped datasheet. The + default is None. + datasheet : String, optional + Name of the datasheet to delete data from. Required when data is + True. The default is None. + ids : Int or String, optional + IDs of the rows to delete. If None, deletes all data. The default is + None. force : Logical, optional If True, does not prompt the user to confirm deletion. The default is False. - + Returns ------- None. """ - self.library.delete(project=self, scenario=scenario, force=force) + self.library.delete(project=self, scenario=scenario, data=data, + datasheet=datasheet, ids=ids, pid=self.pid, + force=force) def save_datasheet(self, name, data, append=True, force=False): """ diff --git a/pysyncrosim/scenario.py b/pysyncrosim/scenario.py index 6d7c5b1..d5e56d2 100644 --- a/pysyncrosim/scenario.py +++ b/pysyncrosim/scenario.py @@ -513,12 +513,21 @@ def save_datasheet(self, name, data, append=False): """ self.library.save_datasheet(name, data, append, False, "Scenario", self.sid) - def delete(self, force=False): + def delete(self, data=None, datasheet=None, ids=None, force=False): """ - Deletes a Scenario. + Deletes a Scenario or data from a Scenario scope. Parameters ---------- + data : Logical, optional + If set to True, will delete data from a Scenario-scoped datasheet. + The default is None + datasheet : String, optional + Name of the datasheet to delete data from. Required when data is + True. The default is None + ids : Int or String, optional + IDs of the rows to delete. If None, deletes all data. The default is + None. force : Logical, optional If True, does not ask the user for permission to delete the Scenario. The default is False. @@ -529,7 +538,9 @@ def delete(self, force=False): """ - self.library.delete(project=self.project, scenario=self, force=force) + self.library.delete(project=self.project, scenario=self, + data=data, datasheet=datasheet, ids=ids, + sid=self.sid, force=force) def copy(self, name=None): """ diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index 2d0b18d..3948ecb 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -332,11 +332,7 @@ def test_library_delete(): mySession = ps.Session(session_path) myLibrary = ps.library(name=lib_path, overwrite=True, session=mySession) - myProject = myLibrary.projects(name="test") - myScenario = myLibrary.scenarios(name="test") - myFolder = myProject.folders(folder="test-folder") - myFolder2 = myProject.folders(folder="test-folder2") - fid = myFolder2.folder_id + myLibrary.projects(name="test") # Test delete method with pytest.raises( @@ -351,18 +347,7 @@ def test_library_delete(): with pytest.raises(TypeError, match="force must be a Logical"): myLibrary.delete(force="True") - - with pytest.raises(TypeError, match="remove_backup must be a Logical"): - myLibrary.delete(force=True, remove_backup="True") - with pytest.raises(TypeError, match="remove_publish must be a Logical"): - myLibrary.delete(force=True, remove_publish="True") - - with pytest.raises( - TypeError, - match="remove_custom_folders must be a Logical"): - myLibrary.delete(force=True, remove_custom_folders="True") - with pytest.raises(ValueError, match="Project ID 2 does not exist"): myLibrary.delete(project=2) @@ -584,7 +569,7 @@ def test_library_compact(): size_before = os.path.getsize(myLibrary.location) compactLibrary = myLibrary.compact() - size_after = os.path.getsize(myLibrary.location) + size_after = os.path.getsize(compactLibrary) assert size_before > size_after From e06694c8802bf926791782cb47c751a0ffea789f Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Mon, 22 Dec 2025 15:35:18 -0800 Subject: [PATCH 05/14] add testing --- tests/test_pysyncrosim.py | 70 ++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index 3948ecb..6c58c1e 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -369,22 +369,6 @@ def test_library_delete(): with pytest.raises(ValueError, match="Folder ID 50 does not exist"): myLibrary.delete(folder=50, force=True) - # move - # with pytest.raises(TypeError, match="data must be a Logical"): - # myLibrary.delete(data="True") - - # with pytest.raises(TypeError, match="datasheet must be a String"): - # myLibrary.delete(data=True, datasheet=1) - - # with pytest.raises(TypeError, match="pid must be an Integer"): - # myLibrary.delete(data=True, datasheet="core_Backup", pid="1") - - # with pytest.raises(TypeError, match="sid must be an Integer"): - # myLibrary.delete(data=True, datasheet="core_Backup", sid="1") - - # with pytest.raises(ValueError, match="datasheet name is required"): - # myLibrary.delete(data=True, datasheet="") - # Test delete folder from folder name myLibrary.delete(folder=myFolder, force=True) assert myFolder.folder_id not in myLibrary.folders()["Id"].values @@ -403,45 +387,69 @@ def test_library_delete(): myLibrary.delete(scenario="test", force=True) assert "test" not in myLibrary.scenarios().Name.values -def test_library_delete_datasheet(): +def test_delete_datasheet(): mySession = ps.Session(session_path) myLibrary = ps.library(name=lib_path, overwrite=True, packages=["stsim"], session=mySession) myProject = myLibrary.projects(name="test") myScenario = myLibrary.scenarios(name="test") + myScenario2 = myLibrary.scenarios(name="test2") test_data = pd.DataFrame({ "Name": ["a1", "a2", "a3"], "Id": [1, 2, 3], "Description": ["test1", "test2", "test3"] }) - # Add datasheet to project - myProject.save_datasheet(name = "stsim_Stratum", data=test_data) - saved_data = myProject.datasheets(name="stsim_Stratum") - assert len(saved_data) == 3 - # Test delete datasheet from project + with pytest.raises(TypeError, match="data must be a Logical"): + myLibrary.delete(data="True") + + with pytest.raises(TypeError, match="datasheet must be a String"): + myLibrary.delete(data=True, datasheet=1) + + with pytest.raises(TypeError, match="pid must be an Integer"): + myLibrary.delete(data=True, datasheet="core_Backup", pid="1") + + with pytest.raises(TypeError, match="sid must be an Integer"): + myLibrary.delete(data=True, datasheet="core_Backup", sid="1") + + with pytest.raises(ValueError, match="datasheet name is required"): + myLibrary.delete(data=True, datasheet="") + + # Add datasheet to project and test delete from project using Library class + myProject.save_datasheet(name="stsim_Stratum", data=test_data) + assert len(myProject.datasheets(name="stsim_Stratum")) == 3 myLibrary.delete(data=True, datasheet="stsim_Stratum", pid=myProject.pid, force=True) assert myProject.datasheets(name="stsim_Stratum").empty - # Add back datasheet for next test - myProject.save_datasheet(name = "stsim_Stratum", data=test_data) - saved_data = myProject.datasheets(name="stsim_Stratum") - assert len(saved_data) == 3 + # Test delete datasheet from scenario using Library class + myLibrary.delete(data=True, datasheet="stsim_RunControl", + sid=myScenario.sid, force=True) + assert myScenario.datasheets(name="stsim_RunControl").empty + + # Test delete datasheet from project using Project class + myProject.save_datasheet(name="stsim_Stratum", data=test_data) + assert len(myProject.datasheets(name="stsim_Stratum")) == 3 + myProject.delete(data=True, datasheet="stsim_Stratum", force=True) + assert myProject.datasheets(name="stsim_Stratum").empty + + # Test delete datasheet from scenario using Scenario class + myScenario2.save_datasheet(name = "stsim_Stratum", data=test_data) + assert len(myScenario2.datasheets(name="stsim_Stratum")) == 3 + myScenario2.delete(data=True, datasheet="stsim_Stratum", force=True) + assert myScenario2.datasheets(name="stsim_Stratum").empty # Test delete datasheet by row ID + myProject.save_datasheet(name="stsim_Stratum", data=test_data) + saved_data = myProject.datasheet(name="stsim_Stratum") + ids_to_delete = f"{saved_data.iloc[0]["Id"]},{saved_data.iloc[1]["Id"]}" myLibrary.delete(data=True, datasheet="stsim_Stratum", pid=myProject.pid, ids=ids_to_delete, force=True) remaining_data = myProject.datasheets(name="stsim_Stratum") assert len(remaining_data) == 1 # assert remaining_data.iloc[0]["Name"] == "a3" - - # Test delete from scenario - myLibrary.delete(data=True, datasheet="stsim_RunControl", - sid=myScenario.sid, force=True) - assert myScenario.datasheets(name="stsim_RunControl").empty def test_library_save_datasheet(): From acacf6db266872f882a2054e5dc02132f33c464f Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Tue, 23 Dec 2025 08:08:10 -0800 Subject: [PATCH 06/14] add testing function and test that the delete datasheet function works --- pysyncrosim/library.py | 12 ++++++------ pysyncrosim/project.py | 9 ++++++--- pysyncrosim/scenario.py | 12 ++++++++---- tests/test_pysyncrosim.py | 15 +++++++-------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/pysyncrosim/library.py b/pysyncrosim/library.py index 3c0fa8c..48acc35 100644 --- a/pysyncrosim/library.py +++ b/pysyncrosim/library.py @@ -641,10 +641,10 @@ def delete(self, project=None, scenario=None, folder=None, data : Logical, optional If set to True, will delete data from a datasheet. The default is None. - datasheet : string, optional + datasheet : String, optional If called from the Library class instance, specify the Datasheet to delete data. Required when data is True. The default is None. - ids : Str or Int, optional + ids : String or Int, optional IDs of the rows to delete. If None, deletes all data. The default is None. force : Logical, optional @@ -712,8 +712,8 @@ def delete(self, project=None, scenario=None, folder=None, ids, int) and not isinstance(ids, np.int64): raise TypeError("ids must be a String or Integer") - if project is None and scenario is None and folder is None and - datasheet is None: + if (project is None and scenario is None and folder is None and + data is None): helper._delete_library(name = self.location, session=self.session, force=force, remove_backup=remove_backup, @@ -768,9 +768,9 @@ def delete(self, project=None, scenario=None, folder=None, helper._delete_folder(library=self, fid=fid, session=self.session, force=force) - elif data is not None: + elif data is True: - if datasheet is None: + if not datasheet: raise ValueError("datasheet name is required") helper._delete_data(library=self, datasheet=datasheet, pid=pid, diff --git a/pysyncrosim/project.py b/pysyncrosim/project.py index ca68c0e..160b59c 100644 --- a/pysyncrosim/project.py +++ b/pysyncrosim/project.py @@ -324,10 +324,13 @@ def delete(self, scenario=None, data=None, datasheet=None, ids=None, None. """ + if data is True: + self.library.delete(data=data, datasheet=datasheet, ids=ids, + pid=self.pid, force=force) - self.library.delete(project=self, scenario=scenario, data=data, - datasheet=datasheet, ids=ids, pid=self.pid, - force=force) + else: + self.library.delete(project=self, scenario=self, force=True) + def save_datasheet(self, name, data, append=True, force=False): """ diff --git a/pysyncrosim/scenario.py b/pysyncrosim/scenario.py index d5e56d2..c67586f 100644 --- a/pysyncrosim/scenario.py +++ b/pysyncrosim/scenario.py @@ -537,10 +537,14 @@ def delete(self, data=None, datasheet=None, ids=None, force=False): None. """ - - self.library.delete(project=self.project, scenario=self, - data=data, datasheet=datasheet, ids=ids, - sid=self.sid, force=force) + + if data is True: + self.library.delete(data=data, datasheet=datasheet, ids=ids, + sid=self.sid, force=force) + + else: + self.library.delete(project=self.project, scenario=self, + force=True) def copy(self, name=None): """ diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index 6c58c1e..1216015 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -387,6 +387,7 @@ def test_library_delete(): myLibrary.delete(scenario="test", force=True) assert "test" not in myLibrary.scenarios().Name.values + def test_delete_datasheet(): mySession = ps.Session(session_path) @@ -415,7 +416,7 @@ def test_delete_datasheet(): myLibrary.delete(data=True, datasheet="core_Backup", sid="1") with pytest.raises(ValueError, match="datasheet name is required"): - myLibrary.delete(data=True, datasheet="") + myLibrary.delete(data=True) # Add datasheet to project and test delete from project using Library class myProject.save_datasheet(name="stsim_Stratum", data=test_data) @@ -435,21 +436,19 @@ def test_delete_datasheet(): assert myProject.datasheets(name="stsim_Stratum").empty # Test delete datasheet from scenario using Scenario class - myScenario2.save_datasheet(name = "stsim_Stratum", data=test_data) - assert len(myScenario2.datasheets(name="stsim_Stratum")) == 3 - myScenario2.delete(data=True, datasheet="stsim_Stratum", force=True) - assert myScenario2.datasheets(name="stsim_Stratum").empty + myScenario2.delete(data=True, datasheet="stsim_RunControl", force=True) + assert myScenario2.datasheets(name="stsim_RunControl").empty # Test delete datasheet by row ID myProject.save_datasheet(name="stsim_Stratum", data=test_data) - saved_data = myProject.datasheet(name="stsim_Stratum") + saved_data = myProject.datasheets(name="stsim_Stratum", include_key=True) - ids_to_delete = f"{saved_data.iloc[0]["Id"]},{saved_data.iloc[1]["Id"]}" + ids_to_delete = f"{saved_data.iloc[0]["StratumId"]},{saved_data.iloc[1]["StratumId"]}" myLibrary.delete(data=True, datasheet="stsim_Stratum", pid=myProject.pid, ids=ids_to_delete, force=True) remaining_data = myProject.datasheets(name="stsim_Stratum") assert len(remaining_data) == 1 - # assert remaining_data.iloc[0]["Name"] == "a3" + assert remaining_data.iloc[0]["Name"] == "a3" def test_library_save_datasheet(): From 09271cbd6c4ae6491766ed8ea560f383bad8ae9a Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Tue, 13 Jan 2026 15:44:20 -0500 Subject: [PATCH 07/14] add delete data functionality --- pysyncrosim/library.py | 52 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/pysyncrosim/library.py b/pysyncrosim/library.py index 3cd6d61..dca1c14 100644 --- a/pysyncrosim/library.py +++ b/pysyncrosim/library.py @@ -613,8 +613,8 @@ def datasheets(self, name=None, summary=True, optional=False, empty=False, return ds - def delete(self, project=None, scenario=None, - pid=None, sid=None, data=None, datasheet=None, ids=None, + def delete(self, project=None, scenario=None, folder=None, + pid=None, sid=None, data=None, datasheet=None, ids=None, force=False, remove_backup=False, remove_publish=False, remove_custom_folders=False): """ @@ -630,6 +630,19 @@ def delete(self, project=None, scenario=None, delete. The default is None. folder : Folder, or Int, optional If called from a Library class instance, specify the folder to delete. The default is None. + data : Logical, optional + If True, deletes data from a specified datasheet. The default is + None + datasheet : String, optional + Name of the datasheet to delete data from. Required when + data = True. The default is None. + ids : String, optional + The primary key IDs for the rows to delete from the datasheet. The + default is None. + pid : Int, optional + Project ID for datasheet deletion. The default is None. + sid : Int, optional + Scenario ID for datasheet deletion. The default is None. force : Logical, optional If set to True, does not ask user before deleting SyncroSim class instance. The default is False. @@ -666,6 +679,21 @@ def delete(self, project=None, scenario=None, scenario, str) and not isinstance(scenario, np.int64): raise TypeError( "scenario must be a Scenario instance, Integer, or String") + + if folder is not None and not isinstance(folder, ps.Folder): + if not isinstance(folder, int) and not isinstance(folder, np.int64): + raise TypeError("folder must be a Folder instance or Integer") + + if data is not None and not isinstance(data, bool): + raise TypeError("data must be a Logical") + if datasheet is not None and not isinstance(datasheet, str): + raise TypeError("datasheet must be a String") + if pid is not None and not isinstance(pid, int)\ + and not isinstance(pid, np.int64): + raise TypeError("pid must be an Integer") + if sid is not None and not isinstance(sid, int)\ + and not isinstance(sid, np.int64): + raise TypeError("sid must be an Integer") if not isinstance(force, bool): raise TypeError("force must be a Logical") @@ -675,14 +703,23 @@ def delete(self, project=None, scenario=None, raise TypeError("remove_publish must be a Logical") if not isinstance(remove_custom_folders, bool): raise TypeError("remove_custom_folders must be a Logical") + + # delete datasheet + if data is True: + if datasheet is None: + raise ValueError("datasheet name is required") + helper._delete_data(library=self, datasheet=datasheet, pid=pid, + sid=sid, ids=ids, session=self.session, + force=force) - if project is None and scenario is None and folder is None: - + # delete library + elif project is None and scenario is None and folder is None: helper._delete_library(name = self.location, session=self.session, force=force, remove_backup=remove_backup, remove_publish=remove_publish, remove_custom_folders=remove_custom_folders) + # delete project elif project is not None and scenario is None: # turn project into project class instance if str or int @@ -699,7 +736,8 @@ def delete(self, project=None, scenario=None, helper._delete_project(library=self, name=p.name, pid=p.pid, session=self.session, force=force) - + + # delete scenario elif scenario is not None: # turn scenario into scenario class instance if str or int @@ -718,6 +756,7 @@ def delete(self, project=None, scenario=None, session=self.session, force=force) + # delete folder elif folder is not None: # turn folder into folder ID if int @@ -729,7 +768,8 @@ def delete(self, project=None, scenario=None, else: raise TypeError("folder must be a Folder instance or Integer") - helper._delete_folder(library=self, fid=fid, session=self.session, force=force) + helper._delete_folder(library=self, fid=fid, session=self.session, + force=force) def save_datasheet(self, name, data, append=False, force=False, scope="Library", *ids): From fa03bb6f46f63dfd926be458ef86b92e81a67ce1 Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Tue, 13 Jan 2026 15:50:15 -0500 Subject: [PATCH 08/14] delete in project scope fixed --- pysyncrosim/project.py | 5 ++++- tests/test_pysyncrosim.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pysyncrosim/project.py b/pysyncrosim/project.py index 160b59c..b494909 100644 --- a/pysyncrosim/project.py +++ b/pysyncrosim/project.py @@ -328,8 +328,11 @@ def delete(self, scenario=None, data=None, datasheet=None, ids=None, self.library.delete(data=data, datasheet=datasheet, ids=ids, pid=self.pid, force=force) + elif scenario is not None: + self.library.delete(scenario = scenario, force = force) + else: - self.library.delete(project=self, scenario=self, force=True) + self.library.delete(project=self, force=True) def save_datasheet(self, name, data, append=True, force=False): diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index a158649..ff8c0c3 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -439,7 +439,7 @@ def test_delete_datasheet(): myProject.save_datasheet(name="stsim_Stratum", data=test_data) saved_data = myProject.datasheets(name="stsim_Stratum", include_key=True) - ids_to_delete = f"{saved_data.iloc[0]["StratumId"]},{saved_data.iloc[1]["StratumId"]}" + ids_to_delete = f"{saved_data.iloc[0]['StratumId']},{saved_data.iloc[1]["StratumId"]}" myLibrary.delete(data=True, datasheet="stsim_Stratum", pid=myProject.pid, ids=ids_to_delete, force=True) remaining_data = myProject.datasheets(name="stsim_Stratum") From df764dba09f40f6936fdd40651a9c55346685c9d Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Tue, 13 Jan 2026 16:05:45 -0500 Subject: [PATCH 09/14] test delete folder --- tests/test_pysyncrosim.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index ff8c0c3..fdab49a 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -367,7 +367,11 @@ def test_library_delete(): with pytest.raises(ValueError, match="Folder ID 50 does not exist"): myLibrary.delete(folder=50, force=True) - + myProject = myLibrary.projects(name="test") + myFolder = myProject.folders(folder="test_folder") + myFolder2 = myProject.folders(name="test_folder2") + fid = myFolder2.folder_id + myLibrary.delete(folder=myFolder, force=True) assert myFolder.folder_id not in myLibrary.folders()["Id"].values From 1ac35e3d95bd07cb5bf48cf57536a3601b3dd361 Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Tue, 13 Jan 2026 16:38:53 -0500 Subject: [PATCH 10/14] fix bugs from coderabbit --- pysyncrosim/library.py | 2 +- pysyncrosim/project.py | 2 +- tests/test_pysyncrosim.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pysyncrosim/library.py b/pysyncrosim/library.py index dca1c14..bb31843 100644 --- a/pysyncrosim/library.py +++ b/pysyncrosim/library.py @@ -761,7 +761,7 @@ def delete(self, project=None, scenario=None, folder=None, # turn folder into folder ID if int - if type(folder) is int: + if type(folder) is int or isinstance(folder, np.int64): fid = folder elif isinstance(folder, ps.Folder): fid = folder.folder_id diff --git a/pysyncrosim/project.py b/pysyncrosim/project.py index b494909..7516956 100644 --- a/pysyncrosim/project.py +++ b/pysyncrosim/project.py @@ -332,7 +332,7 @@ def delete(self, scenario=None, data=None, datasheet=None, ids=None, self.library.delete(scenario = scenario, force = force) else: - self.library.delete(project=self, force=True) + self.library.delete(project=self, force=force) def save_datasheet(self, name, data, append=True, force=False): diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index fdab49a..d89e4ae 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -369,7 +369,7 @@ def test_library_delete(): myProject = myLibrary.projects(name="test") myFolder = myProject.folders(folder="test_folder") - myFolder2 = myProject.folders(name="test_folder2") + myFolder2 = myProject.folders(folder="test_folder2") fid = myFolder2.folder_id myLibrary.delete(folder=myFolder, force=True) From 5f4831f24520c0b8a9369b1431424163a7b56ae3 Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Thu, 15 Jan 2026 00:23:16 -0500 Subject: [PATCH 11/14] updates to library delete function to reduce number of arguments --- pysyncrosim/helper.py | 4 +- pysyncrosim/library.py | 104 +++++++++++++++----------------------- pysyncrosim/project.py | 10 ++-- pysyncrosim/scenario.py | 10 ++-- tests/test_pysyncrosim.py | 13 +++-- 5 files changed, 58 insertions(+), 83 deletions(-) diff --git a/pysyncrosim/helper.py b/pysyncrosim/helper.py index 86a5b61..8f673ab 100644 --- a/pysyncrosim/helper.py +++ b/pysyncrosim/helper.py @@ -236,7 +236,7 @@ def _delete_project(library, name=None, pid=None, session=None, # Delete Project using console if pid is None: - pid = p["ID"].values[0] + pid = p["ProjectId"].values[0] args = ["--delete", "--project", "--lib=\"%s\"" % library.location, "--pid=%d" % pid, "--force"] session._Session__call_console(args) @@ -268,7 +268,7 @@ def _delete_scenario(library, project, name=None, sid=None, session=None, # Delete Scenario using console if sid is None: - sid = s["Scenario ID"].values[0] + sid = s["ScenarioId"].values[0] args = ["--delete", "--scenario", "--lib=\"%s\"" % library.location, "--sid=%d" % sid, "--force"] session._Session__call_console(args) diff --git a/pysyncrosim/library.py b/pysyncrosim/library.py index bb31843..4f6e927 100644 --- a/pysyncrosim/library.py +++ b/pysyncrosim/library.py @@ -614,9 +614,7 @@ def datasheets(self, name=None, summary=True, optional=False, empty=False, return ds def delete(self, project=None, scenario=None, folder=None, - pid=None, sid=None, data=None, datasheet=None, ids=None, - force=False, remove_backup=False, remove_publish=False, - remove_custom_folders=False): + datasheet=None, ids=None, force=False, remove_backup=False, remove_publish=False, remove_custom_folders=False): """ Deletes a SyncroSim class instance. @@ -626,23 +624,14 @@ def delete(self, project=None, scenario=None, folder=None, If called from a Library class instance, specify the Project to delete. The default is None. scenario : Scenario, String, or Int, optional - If called from a Scenario class instance, specify the Scenario to + If called from a Project class instance, specify the Scenario to delete. The default is None. folder : Folder, or Int, optional If called from a Library class instance, specify the folder to delete. The default is None. - data : Logical, optional - If True, deletes data from a specified datasheet. The default is - None datasheet : String, optional - Name of the datasheet to delete data from. Required when - data = True. The default is None. + Name of the datasheet to delete data from. The default is None. ids : String, optional - The primary key IDs for the rows to delete from the datasheet. The - default is None. - pid : Int, optional - Project ID for datasheet deletion. The default is None. - sid : Int, optional - Scenario ID for datasheet deletion. The default is None. + The primary key IDs for the rows to delete from the datasheet. Only used when a datasheet name is provided. The default is None. force : Logical, optional If set to True, does not ask user before deleting SyncroSim class instance. The default is False. @@ -669,31 +658,11 @@ def delete(self, project=None, scenario=None, folder=None, # Also, should have method to delete list of Projects or Scenarios? # type checks - if project is not None and not isinstance(project, ps.Project): - if not isinstance(project, int) and not isinstance( - project, str) and not isinstance(project, np.int64): - raise TypeError( - "project must be a Project instance, Integer, or String") - if scenario is not None and not isinstance(scenario, ps.Scenario): - if not isinstance(scenario, int) and not isinstance( - scenario, str) and not isinstance(scenario, np.int64): - raise TypeError( - "scenario must be a Scenario instance, Integer, or String") - if folder is not None and not isinstance(folder, ps.Folder): if not isinstance(folder, int) and not isinstance(folder, np.int64): raise TypeError("folder must be a Folder instance or Integer") - - if data is not None and not isinstance(data, bool): - raise TypeError("data must be a Logical") if datasheet is not None and not isinstance(datasheet, str): raise TypeError("datasheet must be a String") - if pid is not None and not isinstance(pid, int)\ - and not isinstance(pid, np.int64): - raise TypeError("pid must be an Integer") - if sid is not None and not isinstance(sid, int)\ - and not isinstance(sid, np.int64): - raise TypeError("sid must be an Integer") if not isinstance(force, bool): raise TypeError("force must be a Logical") @@ -703,70 +672,77 @@ def delete(self, project=None, scenario=None, folder=None, raise TypeError("remove_publish must be a Logical") if not isinstance(remove_custom_folders, bool): raise TypeError("remove_custom_folders must be a Logical") - - # delete datasheet - if data is True: - if datasheet is None: - raise ValueError("datasheet name is required") - helper._delete_data(library=self, datasheet=datasheet, pid=pid, - sid=sid, ids=ids, session=self.session, - force=force) + # delete datasheet + if datasheet is not None: + helper._delete_data(library=self, datasheet=datasheet, ids=ids, + session=self.session, force=force) + # delete library - elif project is None and scenario is None and folder is None: + if project is None and scenario is None and folder is None and\ + datasheet is None: helper._delete_library(name = self.location, session=self.session, force=force, remove_backup=remove_backup, remove_publish=remove_publish, remove_custom_folders=remove_custom_folders) - - # delete project + + # delete project scope elif project is not None and scenario is None: # turn project into project class instance if str or int - if type(project) is int: - p = self.projects(pid = project) - if type(project) is str: + if type(project) is int or isinstance(project, np.int64): + if project in self.__projects["ProjectId"].values: + p = self.projects(pid = project) + else: + raise ValueError(f"project {project} does not exist") + elif type(project) is str: if project in self.__projects["Name"].values: p = self.projects(name = project) else: - raise ValueError(f'project {project} does not exist') - if isinstance(project, ps.Project): + raise ValueError(f"project {project} does not exist") + elif isinstance(project, ps.Project): p = project + else: + raise TypeError(f"project must be a Project instance, " + f"Integer, or String") helper._delete_project(library=self, name=p.name, - pid=p.pid, session=self.session, - force=force) + session=self.session, force=force) - # delete scenario + # delete scenario elif scenario is not None: # turn scenario into scenario class instance if str or int - if type(scenario) is int: - s = self.scenarios(sid = scenario, project = project) - if type(scenario) is str: + if type(scenario) is int or isinstance(scenario, np.int64): + if scenario in self.__scenarios["ScenarioId"].values: + s = self.scenarios(sid = scenario, project = project) + else: + raise ValueError(f"scenario {scenario} does not exist") + elif type(scenario) is str: if scenario in self.__scenarios["Name"].values: s = self.scenarios(name = scenario, project = project) else: - raise ValueError(f'scenario {scenario} does not exist') - if isinstance(scenario, ps.Scenario): + raise ValueError(f"scenario {scenario} does not exist") + elif isinstance(scenario, ps.Scenario): s = scenario + else: + raise TypeError(f"scenario must be a Scenario instance, " + f"Integer, or String") - helper._delete_scenario(library=self, project=s.project, - name=s.name, sid=s.sid, - session=self.session, + helper._delete_scenario(library=self, project=s.project, + name=s.name, session=self.session, force=force) # delete folder elif folder is not None: # turn folder into folder ID if int - if type(folder) is int or isinstance(folder, np.int64): fid = folder elif isinstance(folder, ps.Folder): fid = folder.folder_id else: - raise TypeError("folder must be a Folder instance or Integer") + raise ValueError(f"folder {folder} does not exist") helper._delete_folder(library=self, fid=fid, session=self.session, force=force) diff --git a/pysyncrosim/project.py b/pysyncrosim/project.py index 7516956..eb40c07 100644 --- a/pysyncrosim/project.py +++ b/pysyncrosim/project.py @@ -297,7 +297,7 @@ def datasheets(self, name=None, summary=True, optional=False, empty=False, return_hidden, self.pid) return self.__datasheets - def delete(self, scenario=None, data=None, datasheet=None, ids=None, + def delete(self, scenario=None, datasheet=None, ids=None, force=False): """ Deletes a Project, Scenario, or data from a Project scope. @@ -306,9 +306,6 @@ def delete(self, scenario=None, data=None, datasheet=None, ids=None, ---------- scenario : Scenario, String, or Int, optional Scenario to delete. The default is None. - data : Logical, optional - If True, will delete data from a Project-scoped datasheet. The - default is None. datasheet : String, optional Name of the datasheet to delete data from. Required when data is True. The default is None. @@ -324,9 +321,8 @@ def delete(self, scenario=None, data=None, datasheet=None, ids=None, None. """ - if data is True: - self.library.delete(data=data, datasheet=datasheet, ids=ids, - pid=self.pid, force=force) + if datasheet is not None: + self.library.delete(datasheet=datasheet, ids=ids, force=force) elif scenario is not None: self.library.delete(scenario = scenario, force = force) diff --git a/pysyncrosim/scenario.py b/pysyncrosim/scenario.py index c67586f..2803345 100644 --- a/pysyncrosim/scenario.py +++ b/pysyncrosim/scenario.py @@ -513,15 +513,12 @@ def save_datasheet(self, name, data, append=False): """ self.library.save_datasheet(name, data, append, False, "Scenario", self.sid) - def delete(self, data=None, datasheet=None, ids=None, force=False): + def delete(self, datasheet=None, ids=None, force=False): """ Deletes a Scenario or data from a Scenario scope. Parameters ---------- - data : Logical, optional - If set to True, will delete data from a Scenario-scoped datasheet. - The default is None datasheet : String, optional Name of the datasheet to delete data from. Required when data is True. The default is None @@ -538,9 +535,8 @@ def delete(self, data=None, datasheet=None, ids=None, force=False): """ - if data is True: - self.library.delete(data=data, datasheet=datasheet, ids=ids, - sid=self.sid, force=force) + if datasheet is not None: + self.library.delete(datasheet=datasheet, ids=ids, force=force) else: self.library.delete(project=self.project, scenario=self, diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index d89e4ae..1fc19ec 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -349,13 +349,13 @@ def test_library_delete(): with pytest.raises(TypeError, match="force must be a Logical"): myLibrary.delete(force="True") - with pytest.raises(ValueError, match="Project ID 2 does not exist"): + with pytest.raises(ValueError, match="project 2 does not exist"): myLibrary.delete(project=2) with pytest.raises(ValueError, match="project dne does not exist"): myLibrary.delete(project="dne") - with pytest.raises(ValueError, match="Scenario ID 50 does not exist"): + with pytest.raises(ValueError, match="scenario 50 does not exist"): myLibrary.delete(scenario=50) with pytest.raises(ValueError, match="scenario dne does not exist"): @@ -382,11 +382,18 @@ def test_library_delete(): assert myLibrary._Library__projects.empty assert "test" not in myLibrary.projects().Name.values - # Test delete scenario myLibrary.scenarios(name="test") myLibrary.delete(scenario="test", force=True) assert "test" not in myLibrary.scenarios().Name.values + myLibrary.delete(force=True) + assert not os.path.exists(lib_path) + + with pytest.raises( + ValueError, + match="Library not found:"): + myLibrary.delete(force=True) + def test_delete_datasheet(): From f075f6c4e30153e25d7f6c21f3826519aa60b803 Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Thu, 15 Jan 2026 09:49:04 -0500 Subject: [PATCH 12/14] remove data argument in tests and docstring --- pysyncrosim/project.py | 5 ++--- pysyncrosim/scenario.py | 5 ++--- tests/test_pysyncrosim.py | 21 +++++++++------------ 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/pysyncrosim/project.py b/pysyncrosim/project.py index eb40c07..35d4d68 100644 --- a/pysyncrosim/project.py +++ b/pysyncrosim/project.py @@ -307,8 +307,7 @@ def delete(self, scenario=None, datasheet=None, ids=None, scenario : Scenario, String, or Int, optional Scenario to delete. The default is None. datasheet : String, optional - Name of the datasheet to delete data from. Required when data is - True. The default is None. + Name of the datasheet to delete data from. The default is None. ids : Int or String, optional IDs of the rows to delete. If None, deletes all data. The default is None. @@ -322,7 +321,7 @@ def delete(self, scenario=None, datasheet=None, ids=None, """ if datasheet is not None: - self.library.delete(datasheet=datasheet, ids=ids, force=force) + self.library.delete(datasheet=datasheet, pid=self.pid, force=force) elif scenario is not None: self.library.delete(scenario = scenario, force = force) diff --git a/pysyncrosim/scenario.py b/pysyncrosim/scenario.py index 2803345..3508eef 100644 --- a/pysyncrosim/scenario.py +++ b/pysyncrosim/scenario.py @@ -520,8 +520,7 @@ def delete(self, datasheet=None, ids=None, force=False): Parameters ---------- datasheet : String, optional - Name of the datasheet to delete data from. Required when data is - True. The default is None + Name of the datasheet to delete data from. The default is None ids : Int or String, optional IDs of the rows to delete. If None, deletes all data. The default is None. @@ -540,7 +539,7 @@ def delete(self, datasheet=None, ids=None, force=False): else: self.library.delete(project=self.project, scenario=self, - force=True) + force=force) def copy(self, name=None): """ diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index 1fc19ec..bca827d 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -409,21 +409,18 @@ def test_delete_datasheet(): "Description": ["test1", "test2", "test3"] }) - - with pytest.raises(TypeError, match="data must be a Logical"): - myLibrary.delete(data="True") with pytest.raises(TypeError, match="datasheet must be a String"): - myLibrary.delete(data=True, datasheet=1) + myLibrary.delete(datasheet=1) with pytest.raises(TypeError, match="pid must be an Integer"): - myLibrary.delete(data=True, datasheet="core_Backup", pid="1") + myLibrary.delete(datasheet="core_Backup", pid="1") with pytest.raises(TypeError, match="sid must be an Integer"): - myLibrary.delete(data=True, datasheet="core_Backup", sid="1") + myLibrary.delete(datasheet="core_Backup", sid="1") with pytest.raises(ValueError, match="datasheet name is required"): - myLibrary.delete(data=True) + myLibrary.delete(datasheet="") # Add datasheet to project and test delete from project using Library class myProject.save_datasheet(name="stsim_Stratum", data=test_data) @@ -432,26 +429,26 @@ def test_delete_datasheet(): assert myProject.datasheets(name="stsim_Stratum").empty # Test delete datasheet from scenario using Library class - myLibrary.delete(data=True, datasheet="stsim_RunControl", + myLibrary.delete(datasheet="stsim_RunControl", sid=myScenario.sid, force=True) assert myScenario.datasheets(name="stsim_RunControl").empty # Test delete datasheet from project using Project class myProject.save_datasheet(name="stsim_Stratum", data=test_data) assert len(myProject.datasheets(name="stsim_Stratum")) == 3 - myProject.delete(data=True, datasheet="stsim_Stratum", force=True) + myProject.delete(datasheet="stsim_Stratum", force=True) assert myProject.datasheets(name="stsim_Stratum").empty # Test delete datasheet from scenario using Scenario class - myScenario2.delete(data=True, datasheet="stsim_RunControl", force=True) + myScenario2.delete(datasheet="stsim_RunControl", force=True) assert myScenario2.datasheets(name="stsim_RunControl").empty # Test delete datasheet by row ID - myProject.save_datasheet(name="stsim_Stratum", data=test_data) + myProject.save_datasheet(name="stsim_Stratum", datasheet=test_data) saved_data = myProject.datasheets(name="stsim_Stratum", include_key=True) ids_to_delete = f"{saved_data.iloc[0]['StratumId']},{saved_data.iloc[1]["StratumId"]}" - myLibrary.delete(data=True, datasheet="stsim_Stratum", pid=myProject.pid, + myLibrary.delete(datasheet="stsim_Stratum", pid=myProject.pid, ids=ids_to_delete, force=True) remaining_data = myProject.datasheets(name="stsim_Stratum") assert len(remaining_data) == 1 From 725f19dd123c9be64f1c8f9a0fd958d4b93d9627 Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Thu, 15 Jan 2026 10:04:14 -0500 Subject: [PATCH 13/14] small bug fixes in delete and tsts --- pysyncrosim/project.py | 3 ++- pysyncrosim/scenario.py | 3 ++- tests/test_pysyncrosim.py | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pysyncrosim/project.py b/pysyncrosim/project.py index 35d4d68..915ac23 100644 --- a/pysyncrosim/project.py +++ b/pysyncrosim/project.py @@ -321,7 +321,8 @@ def delete(self, scenario=None, datasheet=None, ids=None, """ if datasheet is not None: - self.library.delete(datasheet=datasheet, pid=self.pid, force=force) + self.library.delete(datasheet=datasheet, pid=self.pid, + ids=ids, force=force) elif scenario is not None: self.library.delete(scenario = scenario, force = force) diff --git a/pysyncrosim/scenario.py b/pysyncrosim/scenario.py index 3508eef..6c764da 100644 --- a/pysyncrosim/scenario.py +++ b/pysyncrosim/scenario.py @@ -535,7 +535,8 @@ def delete(self, datasheet=None, ids=None, force=False): """ if datasheet is not None: - self.library.delete(datasheet=datasheet, ids=ids, force=force) + self.library.delete(datasheet=datasheet, sid=self.sid, + ids=ids, force=force) else: self.library.delete(project=self.project, scenario=self, diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index bca827d..cde36c6 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -425,7 +425,7 @@ def test_delete_datasheet(): # Add datasheet to project and test delete from project using Library class myProject.save_datasheet(name="stsim_Stratum", data=test_data) assert len(myProject.datasheets(name="stsim_Stratum")) == 3 - myLibrary.delete(data=True, datasheet="stsim_Stratum", pid=myProject.pid, force=True) + myLibrary.delete(datasheet="stsim_Stratum", pid=myProject.pid, force=True) assert myProject.datasheets(name="stsim_Stratum").empty # Test delete datasheet from scenario using Library class @@ -444,7 +444,7 @@ def test_delete_datasheet(): assert myScenario2.datasheets(name="stsim_RunControl").empty # Test delete datasheet by row ID - myProject.save_datasheet(name="stsim_Stratum", datasheet=test_data) + myProject.save_datasheet(name="stsim_Stratum", data=test_data) saved_data = myProject.datasheets(name="stsim_Stratum", include_key=True) ids_to_delete = f"{saved_data.iloc[0]['StratumId']},{saved_data.iloc[1]["StratumId"]}" From 34ca1fd8cc3e7a202787d5efeaa3852eccc486d5 Mon Sep 17 00:00:00 2001 From: vickimzhang Date: Thu, 15 Jan 2026 10:08:28 -0500 Subject: [PATCH 14/14] fix to f-string in test --- tests/test_pysyncrosim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pysyncrosim.py b/tests/test_pysyncrosim.py index cde36c6..da8fce3 100644 --- a/tests/test_pysyncrosim.py +++ b/tests/test_pysyncrosim.py @@ -447,7 +447,7 @@ def test_delete_datasheet(): myProject.save_datasheet(name="stsim_Stratum", data=test_data) saved_data = myProject.datasheets(name="stsim_Stratum", include_key=True) - ids_to_delete = f"{saved_data.iloc[0]['StratumId']},{saved_data.iloc[1]["StratumId"]}" + ids_to_delete = f"{saved_data.iloc[0]['StratumId']},{saved_data.iloc[1]['StratumId']}" myLibrary.delete(datasheet="stsim_Stratum", pid=myProject.pid, ids=ids_to_delete, force=True) remaining_data = myProject.datasheets(name="stsim_Stratum")