From 28859b08582bb842530200ed597aae5c0952e7c3 Mon Sep 17 00:00:00 2001 From: paulnoirel <87332996+paulnoirel@users.noreply.github.com> Date: Sat, 15 Feb 2025 00:12:12 +0000 Subject: [PATCH 1/2] Add name support for get_catalog_slice() and get_catalog_slices() --- libs/labelbox/src/labelbox/client.py | 73 +++++++++-- libs/labelbox/tests/integration/test_slice.py | 115 ++++++++++++++++++ 2 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 libs/labelbox/tests/integration/test_slice.py diff --git a/libs/labelbox/src/labelbox/client.py b/libs/labelbox/src/labelbox/client.py index af3b5b3fc..37b2590ff 100644 --- a/libs/labelbox/src/labelbox/client.py +++ b/libs/labelbox/src/labelbox/client.py @@ -1808,18 +1808,16 @@ def _format_failed_rows( def get_catalog(self) -> Catalog: return Catalog(client=self) + - def get_catalog_slice(self, slice_id) -> CatalogSlice: + def get_catalog_slices(self) -> List[CatalogSlice]: """ - Fetches a Catalog Slice by ID. - - Args: - slice_id (str): The ID of the Slice + Fetches all slices of the given entity type. Returns: - CatalogSlice + List[CatalogSlice]: A list of CatalogSlice objects. """ - query_str = """query getSavedQueryPyApi($id: ID!) { - getSavedQuery(id: $id) { + query_str = """query GetCatalogSavedQueriesPyApi { + catalogSavedQueries { id name description @@ -1828,9 +1826,64 @@ def get_catalog_slice(self, slice_id) -> CatalogSlice: updatedAt } } + """ + res = self.execute(query_str) + return [CatalogSlice(self, sl) for sl in res["catalogSavedQueries"]] + + + def get_catalog_slice( + self, + slice_id: Optional[str] = None, + slice_name: Optional[str] = None + ) -> Union[CatalogSlice, List[CatalogSlice]]: """ - res = self.execute(query_str, {"id": slice_id}) - return Entity.CatalogSlice(self, res["getSavedQuery"]) + Fetches a Slice using either the slice ID or the slice name. + + Args: + slice_id (Optional[str]): The ID of the Slice. + slice_name (Optional[str]): The name of the Slice. + + Returns: + Union[CatalogSlice, List[CatalogSlice], ModelSlice, List[ModelSlice]]: + The corresponding Slice object or list of Slice objects. + + Raises: + ValueError: If neither or both id and name are provided. + ResourceNotFoundError: If the slice is not found. + """ + if (slice_id is None and slice_name is None) or (slice_id is not None and slice_name is not None): + raise ValueError("Provide exactly one of id or name") + + if slice_id is not None: + query_str = """query getSavedQueryPyApi($id: ID!) { + getSavedQuery(id: $id) { + id + name + description + filter + createdAt + updatedAt + } + } + """ + + res = self.execute(query_str, {"id": slice_id}) + if res is None: + raise ResourceNotFoundError(CatalogSlice, {"id": slice_id}) + + return CatalogSlice(self, res["getSavedQuery"]) + + else: + slices = self.get_catalog_slices() + matches = [s for s in slices if s.name == slice_name] + + if not matches: + raise ResourceNotFoundError(CatalogSlice, {"name": slice_name}) + elif len(matches) > 1: + return matches + else: + return matches[0] + def is_feature_schema_archived( self, ontology_id: str, feature_schema_id: str diff --git a/libs/labelbox/tests/integration/test_slice.py b/libs/labelbox/tests/integration/test_slice.py new file mode 100644 index 000000000..ad7c95694 --- /dev/null +++ b/libs/labelbox/tests/integration/test_slice.py @@ -0,0 +1,115 @@ +import pytest +from typing import Optional +from labelbox import Client, CatalogSlice + + +def _create_catalog_slice(client: Client, name: str, description: Optional[str] = None) -> str: + """Creates a catalog slice for testing purposes. + + Args: + client (Client): Labelbox client instance + name (str): Name of the catalog slice + description (str): Description of the catalog slice + + Returns: + str: ID of the created catalog slice + """ + + mutation = """mutation CreateCatalogSlicePyApi($name: String!, $description: String, $query: SearchServiceQuery!, $sorting: [SearchServiceSorting!]) { + createCatalogSavedQuery( + args: {name: $name, description: $description, filter: $query, sorting: $sorting} + ) { + id + name + description + filter + sorting + catalogCount { + count + } + } + } + """ + + params = { + "description": description, + "name": name, + "query": [ + { + "type": "media_attribute_asset_type", + "assetType": { + "type": "asset_type", + "assetTypes": [ + "image" + ] + } + } + ], + "sorting": [ + { + "field": { + "field": "dataRowCreatedAt", + "verboseName": "Created At" + }, + "direction": "DESC", + "metadataSchemaId": None + } + ] + } + + result = client.execute(mutation, params, experimental=True) + + return result["createCatalogSavedQuery"].get("id") + + +def _delete_catalog_slice(client, slice_id: str) -> bool: + + mutation = """mutation DeleteCatalogSlicePyApi($id: ID!) { + deleteSavedQuery(args: { id: $id }) { + success + } + } + """ + + params = { + "id": slice_id + } + + operation_done = True + try: + client.execute(mutation, params, experimental=True) + except Exception as ex: + operation_done = False + + return operation_done + + +def test_get_slice(client): + + # Pre-cleaning + slices = ( s for s in client.get_catalog_slices() + if s.name in ["Test Slice 1", "Test Slice 2"]) + for slice in slices: + _delete_catalog_slice(client, slice.id) + + # Create slices + slice_id_1 = _create_catalog_slice(client, "Test Slice 1", "Slice created for SDK test.") + slice_id_2 = _create_catalog_slice(client, "Test Slice 2", "Slice created for SDK test.") + # Create slice 2b - with the same name as slice 2 + slice_id_2b = _create_catalog_slice(client, "Test Slice 2", "Slice created for SDK test.") + + # Assert get slice 1 by ID + slice_1 = client.get_catalog_slice(slice_id_1) + assert isinstance(slice_1, CatalogSlice) + + slice_1 = client.get_catalog_slice(slice_name="Test Slice 1") + assert isinstance(slice_1, CatalogSlice) + + slices_2 = client.get_catalog_slice(slice_name="Test Slice 2") + assert len(slices_2) == 2 + assert isinstance(slices_2, list) and all([isinstance(item, CatalogSlice) for item in slices_2]) + + # Cleaning - Delete slices + _delete_catalog_slice(client, slice_id_1) + _delete_catalog_slice(client, slice_id_2) + _delete_catalog_slice(client, slice_id_2b) From 4df79c5f1773a9d5f6ddb69ef760ae3bbe3de185 Mon Sep 17 00:00:00 2001 From: paulnoirel <87332996+paulnoirel@users.noreply.github.com> Date: Sun, 16 Feb 2025 13:48:30 +0000 Subject: [PATCH 2/2] Fix lint --- libs/labelbox/src/labelbox/client.py | 21 ++-- libs/labelbox/tests/integration/test_slice.py | 117 +++++++++--------- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/libs/labelbox/src/labelbox/client.py b/libs/labelbox/src/labelbox/client.py index 37b2590ff..a216bb0d3 100644 --- a/libs/labelbox/src/labelbox/client.py +++ b/libs/labelbox/src/labelbox/client.py @@ -1808,7 +1808,6 @@ def _format_failed_rows( def get_catalog(self) -> Catalog: return Catalog(client=self) - def get_catalog_slices(self) -> List[CatalogSlice]: """ @@ -1829,12 +1828,9 @@ def get_catalog_slices(self) -> List[CatalogSlice]: """ res = self.execute(query_str) return [CatalogSlice(self, sl) for sl in res["catalogSavedQueries"]] - def get_catalog_slice( - self, - slice_id: Optional[str] = None, - slice_name: Optional[str] = None + self, slice_id: Optional[str] = None, slice_name: Optional[str] = None ) -> Union[CatalogSlice, List[CatalogSlice]]: """ Fetches a Slice using either the slice ID or the slice name. @@ -1844,17 +1840,19 @@ def get_catalog_slice( slice_name (Optional[str]): The name of the Slice. Returns: - Union[CatalogSlice, List[CatalogSlice], ModelSlice, List[ModelSlice]]: + Union[CatalogSlice, List[CatalogSlice], ModelSlice, List[ModelSlice]]: The corresponding Slice object or list of Slice objects. Raises: ValueError: If neither or both id and name are provided. ResourceNotFoundError: If the slice is not found. """ - if (slice_id is None and slice_name is None) or (slice_id is not None and slice_name is not None): + if (slice_id is None and slice_name is None) or ( + slice_id is not None and slice_name is not None + ): raise ValueError("Provide exactly one of id or name") - if slice_id is not None: + if slice_id is not None: query_str = """query getSavedQueryPyApi($id: ID!) { getSavedQuery(id: $id) { id @@ -1866,13 +1864,13 @@ def get_catalog_slice( } } """ - + res = self.execute(query_str, {"id": slice_id}) if res is None: raise ResourceNotFoundError(CatalogSlice, {"id": slice_id}) - + return CatalogSlice(self, res["getSavedQuery"]) - + else: slices = self.get_catalog_slices() matches = [s for s in slices if s.name == slice_name] @@ -1883,7 +1881,6 @@ def get_catalog_slice( return matches else: return matches[0] - def is_feature_schema_archived( self, ontology_id: str, feature_schema_id: str diff --git a/libs/labelbox/tests/integration/test_slice.py b/libs/labelbox/tests/integration/test_slice.py index ad7c95694..0521d24dd 100644 --- a/libs/labelbox/tests/integration/test_slice.py +++ b/libs/labelbox/tests/integration/test_slice.py @@ -1,21 +1,22 @@ -import pytest from typing import Optional from labelbox import Client, CatalogSlice -def _create_catalog_slice(client: Client, name: str, description: Optional[str] = None) -> str: - """Creates a catalog slice for testing purposes. +def _create_catalog_slice( + client: Client, name: str, description: Optional[str] = None +) -> str: + """Creates a catalog slice for testing purposes. - Args: - client (Client): Labelbox client instance - name (str): Name of the catalog slice - description (str): Description of the catalog slice + Args: + client (Client): Labelbox client instance + name (str): Name of the catalog slice + description (str): Description of the catalog slice - Returns: - str: ID of the created catalog slice - """ + Returns: + str: ID of the created catalog slice + """ - mutation = """mutation CreateCatalogSlicePyApi($name: String!, $description: String, $query: SearchServiceQuery!, $sorting: [SearchServiceSorting!]) { + mutation = """mutation CreateCatalogSlicePyApi($name: String!, $description: String, $query: SearchServiceQuery!, $sorting: [SearchServiceSorting!]) { createCatalogSavedQuery( args: {name: $name, description: $description, filter: $query, sorting: $sorting} ) { @@ -31,72 +32,72 @@ def _create_catalog_slice(client: Client, name: str, description: Optional[str] } """ - params = { - "description": description, - "name": name, - "query": [ - { - "type": "media_attribute_asset_type", - "assetType": { - "type": "asset_type", - "assetTypes": [ - "image" - ] - } - } - ], - "sorting": [ - { - "field": { - "field": "dataRowCreatedAt", - "verboseName": "Created At" - }, - "direction": "DESC", - "metadataSchemaId": None - } - ] - } + params = { + "description": description, + "name": name, + "query": [ + { + "type": "media_attribute_asset_type", + "assetType": {"type": "asset_type", "assetTypes": ["image"]}, + } + ], + "sorting": [ + { + "field": { + "field": "dataRowCreatedAt", + "verboseName": "Created At", + }, + "direction": "DESC", + "metadataSchemaId": None, + } + ], + } - result = client.execute(mutation, params, experimental=True) + result = client.execute(mutation, params, experimental=True) - return result["createCatalogSavedQuery"].get("id") + return result["createCatalogSavedQuery"].get("id") def _delete_catalog_slice(client, slice_id: str) -> bool: - - mutation = """mutation DeleteCatalogSlicePyApi($id: ID!) { + mutation = """mutation DeleteCatalogSlicePyApi($id: ID!) { deleteSavedQuery(args: { id: $id }) { success } } """ - params = { - "id": slice_id - } + params = {"id": slice_id} - operation_done = True - try: - client.execute(mutation, params, experimental=True) - except Exception as ex: - operation_done = False + operation_done = True + try: + client.execute(mutation, params, experimental=True) + except Exception as ex: + operation_done = False - return operation_done + return operation_done def test_get_slice(client): - # Pre-cleaning - slices = ( s for s in client.get_catalog_slices() - if s.name in ["Test Slice 1", "Test Slice 2"]) + slices = ( + s + for s in client.get_catalog_slices() + if s.name in ["Test Slice 1", "Test Slice 2"] + ) for slice in slices: _delete_catalog_slice(client, slice.id) - + # Create slices - slice_id_1 = _create_catalog_slice(client, "Test Slice 1", "Slice created for SDK test.") - slice_id_2 = _create_catalog_slice(client, "Test Slice 2", "Slice created for SDK test.") + slice_id_1 = _create_catalog_slice( + client, "Test Slice 1", "Slice created for SDK test." + ) + slice_id_2 = _create_catalog_slice( + client, "Test Slice 2", "Slice created for SDK test." + ) # Create slice 2b - with the same name as slice 2 - slice_id_2b = _create_catalog_slice(client, "Test Slice 2", "Slice created for SDK test.") + slice_id_2b = _create_catalog_slice( + client, "Test Slice 2", "Slice created for SDK test." + ) # Assert get slice 1 by ID slice_1 = client.get_catalog_slice(slice_id_1) @@ -107,7 +108,9 @@ def test_get_slice(client): slices_2 = client.get_catalog_slice(slice_name="Test Slice 2") assert len(slices_2) == 2 - assert isinstance(slices_2, list) and all([isinstance(item, CatalogSlice) for item in slices_2]) + assert isinstance(slices_2, list) and all( + [isinstance(item, CatalogSlice) for item in slices_2] + ) # Cleaning - Delete slices _delete_catalog_slice(client, slice_id_1)