diff --git a/sepal_ui/scripts/gee.py b/sepal_ui/scripts/gee.py index 25756cc9..175a33e7 100644 --- a/sepal_ui/scripts/gee.py +++ b/sepal_ui/scripts/gee.py @@ -84,28 +84,30 @@ def is_running(task_descripsion: str) -> ee.batch.Task: @sd.need_ee -def get_assets(folder: Union[str, Path] = "", asset_list: List[str] = []) -> List[str]: +def get_assets(folder: Union[str, Path] = "") -> List[dict]: """Get all the assets from the parameter folder. every nested asset will be displayed. Args: folder: the initial GEE folder - asset_list: extra element that you would like to add to the asset list Returns: the asset list. each asset is a dict with 3 keys: 'type', 'name' and 'id' """ - # set the folder + # set the folder and init the list + asset_list = [] folder = str(folder) or ee.data.getAssetRoots()[0]["id"] - # loop in the assets - for asset in ee.data.listAssets({"parent": folder})["assets"]: - if asset["type"] == "FOLDER": - asset_list += [asset] - asset_list = get_assets(asset["name"], asset_list) - else: + def _recursive_get(folder, asset_list): + + # loop in the assets + for asset in ee.data.listAssets({"parent": folder})["assets"]: asset_list += [asset] + if asset["type"] == "FOLDER": + asset_list = _recursive_get(asset["name"], asset_list) - return asset_list + return asset_list + + return _recursive_get(folder, asset_list) @sd.need_ee @@ -133,3 +135,55 @@ def is_asset(asset_name: str, folder: Union[str, Path] = "") -> bool: break return exist + + +@sd.need_ee +def delete_assets(asset_id: str, dry_run: bool = True) -> None: + """Delete the selected asset and all its content. + + This method will delete all the files and folders existing in an asset folder. By default a dry run will be launched and if you are satisfyed with the displayed names, change the ``dry_run`` variable to ``False``. No other warnng will be displayed. + + .. warning:: + + If this method is used on the root directory you will loose all your data, it's highly recommended to use a dry run first and carefully review the destroyed files. + + Args: + asset_id: the Id of the asset or a folder + dry_run: whether or not a dry run should be launched. dry run will only display the files name without deleting them. + """ + # define the action to execute for each asset based on the dry run mode + def delete(id: str) -> None: + if dry_run is True: + print(f"to be deleted: {id}") + else: + print(f"deleting: {id}") + ee.data.deleteAsset(id) + + return + + # identify the type of asset + asset_info = ee.data.getAsset(asset_id) + + if asset_info["type"] == "FOLDER": + + # get all the assets + asset_list = get_assets(folder=asset_id) + + # split the files by nesting levels + # we will need to delete the more nested files first + assets_ordered = {} + for asset in asset_list: + lvl = len(asset["id"].split("/")) + assets_ordered.setdefault(lvl, []) + assets_ordered[lvl].append(asset) + + # delete all items starting from the more nested one but not folders + assets_ordered = dict(sorted(assets_ordered.items(), reverse=True)) + for lvl in assets_ordered: + for i in assets_ordered[lvl]: + delete(i["name"]) + + # delete the initial folder/asset + delete(asset_id) + + return diff --git a/tests/conftest.py b/tests/conftest.py index 6109f459..fb099ea0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -181,11 +181,7 @@ def gee_dir(_hash: str) -> Optional[Path]: yield gee_dir # flush the directory and it's content - ee.data.deleteAsset(str(subfolder / subfolder_fc)) - ee.data.deleteAsset(str(subfolder)) - ee.data.deleteAsset(str(gee_dir / fc)) - ee.data.deleteAsset(str(gee_dir / rand_image)) - ee.data.deleteAsset(str(gee_dir)) + gee.delete_assets(str(gee_dir), False) return diff --git a/tests/test_mapping/test_SepalMap.py b/tests/test_mapping/test_SepalMap.py index b6f4f220..2d66f2cf 100644 --- a/tests/test_mapping/test_SepalMap.py +++ b/tests/test_mapping/test_SepalMap.py @@ -7,6 +7,7 @@ import ee import pytest +from ee.ee_exception import EEException from ipyleaflet import GeoJSON from sepal_ui import mapping as sm @@ -190,7 +191,7 @@ def test_add_ee_layer_exceptions() -> None: ) ) - with pytest.raises(AttributeError): + with pytest.raises(EEException): map_.addLayer(geometry, {"invalid_propery": "red", "fillColor": None}) return diff --git a/tests/test_scripts/test_gee.py b/tests/test_scripts/test_gee.py index 0f86c2e8..9224158c 100644 --- a/tests/test_scripts/test_gee.py +++ b/tests/test_scripts/test_gee.py @@ -152,6 +152,6 @@ def fake_task(gee_dir: Path, _hash: str, alert: sw.Alert) -> str: # delete the task asset gee.wait_for_completion(name, alert) - ee.data.deleteAsset(asset_id) + gee.delete_assets(asset_id, False) return