From 0bedbc20331e4c593e1ace5d4e7e9ee9a285c5d6 Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 15:37:24 -0700 Subject: [PATCH 01/15] upgraded documentation by collapsing example search code --- src/cript/api/api.py | 88 ++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/cript/api/api.py b/src/cript/api/api.py index 17e5db5f8..ff37f7953 100644 --- a/src/cript/api/api.py +++ b/src/cript/api/api.py @@ -868,50 +868,50 @@ def search( Examples -------- - ### Search by Node Type - ```python - materials_paginator = cript_api.search( - node_type=cript.Material, - search_mode=cript.SearchModes.NODE_TYPE, - value_to_search=None - ) - ``` - - ### Search by Contains name - ```python - contains_name_paginator = cript_api.search( - node_type=cript.Process, - search_mode=cript.SearchModes.CONTAINS_NAME, - value_to_search="poly" - ) - ``` - - ### Search by Exact Name - ```python - exact_name_paginator = cript_api.search( - node_type=cript.Project, - search_mode=cript.SearchModes.EXACT_NAME, - value_to_search="Sodium polystyrene sulfonate" - ) - ``` - - ### Search by UUID - ```python - uuid_paginator = cript_api.search( - node_type=cript.Collection, - search_mode=cript.SearchModes.UUID, - value_to_search="75fd3ee5-48c2-4fc7-8d0b-842f4fc812b7" - ) - ``` - - ### Search by BigSmiles - ```python - paginator = cript_api.search( - node_type=cript.Material, - search_mode=cript.SearchModes.BIGSMILES, - value_to_search="{[][$]CC(C)(C(=O)OCCCC)[$][]}" - ) - ``` + ???+ Example "Search by Node Type" + ```python + materials_paginator = cript_api.search( + node_type=cript.Material, + search_mode=cript.SearchModes.NODE_TYPE, + value_to_search=None + ) + ``` + + ??? Example "Search by Contains name" + ```python + contains_name_paginator = cript_api.search( + node_type=cript.Process, + search_mode=cript.SearchModes.CONTAINS_NAME, + value_to_search="poly" + ) + ``` + + ??? Example "Search by Exact Name" + ```python + exact_name_paginator = cript_api.search( + node_type=cript.Project, + search_mode=cript.SearchModes.EXACT_NAME, + value_to_search="Sodium polystyrene sulfonate" + ) + ``` + + ??? Example "Search by UUID" + ```python + uuid_paginator = cript_api.search( + node_type=cript.Collection, + search_mode=cript.SearchModes.UUID, + value_to_search="75fd3ee5-48c2-4fc7-8d0b-842f4fc812b7" + ) + ``` + + ??? Example "Search by BigSmiles" + ```python + paginator = cript_api.search( + node_type=cript.Material, + search_mode=cript.SearchModes.BIGSMILES, + value_to_search="{[][$]CC(C)(C(=O)OCCCC)[$][]}" + ) + ``` Parameters ---------- From 3d7ef2778351a41ae23f05e6ac92f8461c404499 Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 16:37:26 -0700 Subject: [PATCH 02/15] wrote new search mode feature `NODE_TYPE_WITHIN_PARENT` * wrote the new search mode in `cript.API.search()` * wrote documentation for it * added the search mode to `SearchModes` enum * wrote placeholder test for it, but note sure how to test this search mode yet for every environment --- src/cript/api/api.py | 26 ++++++++++++++++++++++---- src/cript/api/valid_search_modes.py | 2 +- tests/api/test_api.py | 24 ++++++++++++------------ 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/cript/api/api.py b/src/cript/api/api.py index ff37f7953..be0e74c16 100644 --- a/src/cript/api/api.py +++ b/src/cript/api/api.py @@ -33,6 +33,7 @@ from cript.api.vocabulary_categories import VocabCategories from cript.nodes.exceptions import CRIPTNodeSchemaError from cript.nodes.primary_nodes.project import Project +from cript.nodes.uuid_base import UUIDBaseNode # Do not use this directly! That includes devs. # Use the `_get_global_cached_api for access. @@ -853,12 +854,12 @@ def download_file(self, file_source: str, destination_path: str = ".") -> None: # the file is stored in cloud storage and must be retrieved via object_name self._s3_client.download_file(Bucket=self._BUCKET_NAME, Key=file_source, Filename=destination_path) # type: ignore - @beartype def search( self, - node_type, + node_type: UUIDBaseNode, search_mode: SearchModes, - value_to_search: Optional[str], + value_to_search: Optional[str] = None, + parent_node: Optional[UUIDBaseNode] = None ) -> Paginator: """ This method is used to perform search on the CRIPT platform. @@ -913,6 +914,15 @@ def search( ) ``` + ??? Example "Search node type within parent" + ```python + all_materials_in_project_paginator = cript_api.search( + node_type=cript.Material, # the node you want back + search_mode=cript.SearchModes.NODE_TYPE_WITHIN_PARENT, # type of search + parent_node=my_project_node # parent node to search through + ) + ``` + Parameters ---------- node_type : UUIDBaseNode @@ -922,7 +932,12 @@ def search( Refer to [valid search modes](../search_modes) value_to_search : Optional[str] What you are searching for can be either a value, and if you are only searching for - a `NODE_TYPE`, then this value can be empty or `None` + a `NODE_TYPE` or a search mode that does not take a value + then this value can be empty or `None` because it will not be used + > Not applicable for all search modes + parent_node: UUIDBaseNode default None + The parent that you are searching through. + > Not applicable for all search modes Returns ------- @@ -956,6 +971,9 @@ def search( elif search_mode == SearchModes.BIGSMILES: api_endpoint = f"{self._host}/search/bigsmiles/" + elif search_mode == SearchModes.NODE_TYPE_WITHIN_PARENT: + api_endpoint = f"{self._host}/{parent_node.node_type}/{parent_node.uuid}/{node_type}" + assert api_endpoint != "" # TODO error handling if none of the API endpoints got hit diff --git a/src/cript/api/valid_search_modes.py b/src/cript/api/valid_search_modes.py index c7ae2d09b..eb0cf8c64 100644 --- a/src/cript/api/valid_search_modes.py +++ b/src/cript/api/valid_search_modes.py @@ -34,5 +34,5 @@ class SearchModes(Enum): EXACT_NAME: str = "exact_name" CONTAINS_NAME: str = "contains_name" UUID: str = "uuid" - # UUID_CHILDREN = "uuid_children" BIGSMILES: str = "bigsmiles" + NODE_TYPE_WITHIN_PARENT: str = "node_type_within_parent" diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 9f7478bfc..853d3ae74 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -388,22 +388,22 @@ def test_api_search_uuid(cript_api: cript.API) -> None: @pytest.mark.skipif(not HAS_INTEGRATION_TESTS_ENABLED, reason="requires a real cript_api_token") -def test_api_search_bigsmiles(cript_api: cript.API) -> None: +def test_api_search_node_type_within_parent(cript_api: cript.API, simple_project_node) -> None: """ - tests search method with bigsmiles SearchMode to see if we just get at least one match - searches for material - "{[][<]C(C)C(=O)O[>][<]}{[$][$]CCC(C)C[$],[$]CC(C(C)C)[$],[$]CC(C)(CC)[$][]}" - - another good example can be "{[][$]CC(C)(C(=O)OCCCC)[$][]}" + tests search NODE_TYPE_WITHIN_PARENT + searches for all materials within a project node """ - bigsmiles_search_value = "{[][<]C(C)C(=O)O[>][<]}{[$][$]CCC(C)C[$],[$]CC(C(C)C)[$],[$]CC(C)(CC)[$][]}" - bigsmiles_paginator = cript_api.search(node_type=cript.Material, search_mode=cript.SearchModes.BIG_SMILES, value_to_search=bigsmiles_search_value) + all_materials_in_project_paginator = cript_api.search( + node_type=cript.Material, + search_mode=cript.SearchModes.NODE_TYPE_WITHIN_PARENT, + parent_node=simple_project_node + ) + + print(simple_project_node.uuid) - assert isinstance(bigsmiles_paginator, Paginator) - assert len(bigsmiles_paginator.current_page_results) >= 1 - # not sure if this will always be in this position in every server environment, so commenting it out for now - # assert bigsmiles_paginator.current_page_results[1]["name"] == "BCDB_Material_285" + assert isinstance(all_materials_in_project_paginator, Paginator) + assert len(all_materials_in_project_paginator.current_page_results) >= 1 def test_get_my_user_node_from_api(cript_api: cript.API) -> None: From f7d3d167b7abc5f8e5c6a9f49647af616b7fd020 Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 16:41:16 -0700 Subject: [PATCH 03/15] updated documentation for valid_search_modes.py `NODE_TYPE_WITHIN_PARENT` --- src/cript/api/valid_search_modes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cript/api/valid_search_modes.py b/src/cript/api/valid_search_modes.py index eb0cf8c64..a365f9f45 100644 --- a/src/cript/api/valid_search_modes.py +++ b/src/cript/api/valid_search_modes.py @@ -16,7 +16,10 @@ class SearchModes(Enum): UUID : str Search by node UUID. BIGSMILES: str - search materials by bigsmiles identifier. + Search materials by bigsmiles identifier. + NODE_TYPE_WITHIN_PARENT: str + Search for a node type within a parent. + > Example: Find all the materials within this specific project. Examples ------- From 4ce6aef29f0d5f730cc0a74b12a72cf485e5c77b Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 16:45:06 -0700 Subject: [PATCH 04/15] formatted with black --- src/cript/api/api.py | 8 +------- tests/api/test_api.py | 6 +----- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/cript/api/api.py b/src/cript/api/api.py index be0e74c16..e40606922 100644 --- a/src/cript/api/api.py +++ b/src/cript/api/api.py @@ -854,13 +854,7 @@ def download_file(self, file_source: str, destination_path: str = ".") -> None: # the file is stored in cloud storage and must be retrieved via object_name self._s3_client.download_file(Bucket=self._BUCKET_NAME, Key=file_source, Filename=destination_path) # type: ignore - def search( - self, - node_type: UUIDBaseNode, - search_mode: SearchModes, - value_to_search: Optional[str] = None, - parent_node: Optional[UUIDBaseNode] = None - ) -> Paginator: + def search(self, node_type: UUIDBaseNode, search_mode: SearchModes, value_to_search: Optional[str] = None, parent_node: Optional[UUIDBaseNode] = None) -> Paginator: """ This method is used to perform search on the CRIPT platform. diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 853d3ae74..836818c09 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -394,11 +394,7 @@ def test_api_search_node_type_within_parent(cript_api: cript.API, simple_project searches for all materials within a project node """ - all_materials_in_project_paginator = cript_api.search( - node_type=cript.Material, - search_mode=cript.SearchModes.NODE_TYPE_WITHIN_PARENT, - parent_node=simple_project_node - ) + all_materials_in_project_paginator = cript_api.search(node_type=cript.Material, search_mode=cript.SearchModes.NODE_TYPE_WITHIN_PARENT, parent_node=simple_project_node) print(simple_project_node.uuid) From 95291f1cd40acbc6cf31710e00da2ad9153adc47 Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 17:54:50 -0700 Subject: [PATCH 05/15] wrote `cript_project_node` fixture * wrote a new fixture that will get the cript project, turn it into a node, and give it to the test to use * the CRIPT project will exist on all environments of CRIPT --- tests/fixtures/primary_nodes.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/fixtures/primary_nodes.py b/tests/fixtures/primary_nodes.py index ebd7e7852..5de6af34f 100644 --- a/tests/fixtures/primary_nodes.py +++ b/tests/fixtures/primary_nodes.py @@ -1,11 +1,31 @@ import copy import json import uuid +from typing import Dict import pytest from util import strip_uid_from_dict import cript +from cript import load_nodes_from_json + + +@pytest.fixture(scope="function") +def cript_project_node(cript_api: cript.API) -> cript.Project: + """ + The CRIPT project node that exists on all server environements + + Notes + ----- + Good to use for when you need to test against a project but can't use any single project + because it might not be available on all CRIPT server environments, or any permission issues, that + the project is visible to you but not others running your tests so they get false errors. + """ + # get CRIPT project from API and convert to node + exact_name_paginator = cript_api.search(node_type=cript.Project, search_mode=cript.SearchModes.EXACT_NAME, value_to_search="cript") + cript_project_dict: Dict = exact_name_paginator.current_page_results[0] + cript_project_node: cript.Project = load_nodes_from_json(json.dumps(cript_project_dict)) + return cript_project_node @pytest.fixture(scope="function") From f317c0ac66f469f736a77f259a70ceca54a54426 Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 17:57:54 -0700 Subject: [PATCH 06/15] added new search mode enums * `CHILD_NODE_TYPE_WITHIN_PARENT` * `CHILD_CONTAINS_NAME_WITHIN_PARENT` * `CHILD_WITH_EXACT_NAME_WITHIN_PARENT` --- src/cript/api/valid_search_modes.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cript/api/valid_search_modes.py b/src/cript/api/valid_search_modes.py index a365f9f45..593a192f3 100644 --- a/src/cript/api/valid_search_modes.py +++ b/src/cript/api/valid_search_modes.py @@ -17,9 +17,15 @@ class SearchModes(Enum): Search by node UUID. BIGSMILES: str Search materials by bigsmiles identifier. - NODE_TYPE_WITHIN_PARENT: str + CHILD_NODE_TYPE_WITHIN_PARENT: str Search for a node type within a parent. > Example: Find all the materials within this specific project. + CHILD_CONTAINS_NAME_WITHIN_PARENT: str + Search for a node containing a value for an attribute within a parent + > Example: Search for all materials that contain a certain name for the field specified within a project. + CHILD_WITH_EXACT_NAME_WITHIN_PARENT: str + Search for an exact node name within a parent + > Example: Search for the materials with that exact name in the project or returns nothing. Examples ------- @@ -38,4 +44,6 @@ class SearchModes(Enum): CONTAINS_NAME: str = "contains_name" UUID: str = "uuid" BIGSMILES: str = "bigsmiles" - NODE_TYPE_WITHIN_PARENT: str = "node_type_within_parent" + CHILD_NODE_TYPE_WITHIN_PARENT: str = "child_node_type_within_parent" + CHILD_CONTAINS_NAME_WITHIN_PARENT: str = "child_contains_name_within_parent" + CHILD_WITH_EXACT_NAME_WITHIN_PARENT: str = "child_with_exact_name_within_parent" From 3196c69e48f6a5641390b2fbcc1a976dd1b5791f Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 17:58:37 -0700 Subject: [PATCH 07/15] added new search mode to `cript.API.search()` * `CHILD_NODE_TYPE_WITHIN_PARENT` * wrote documentation and test * `CHILD_WITH_EXACT_NAME_WITHIN_PARENT` * wrote documentation and test --- src/cript/api/api.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/cript/api/api.py b/src/cript/api/api.py index e40606922..4f2bf14e9 100644 --- a/src/cript/api/api.py +++ b/src/cript/api/api.py @@ -908,15 +908,25 @@ def search(self, node_type: UUIDBaseNode, search_mode: SearchModes, value_to_sea ) ``` - ??? Example "Search node type within parent" + ??? Example "Search child node type within parent" ```python all_materials_in_project_paginator = cript_api.search( - node_type=cript.Material, # the node you want back - search_mode=cript.SearchModes.NODE_TYPE_WITHIN_PARENT, # type of search + node_type=cript.Material, # the node type you want back + search_mode=cript.SearchModes.CHILD_NODE_TYPE_WITHIN_PARENT, # type of search parent_node=my_project_node # parent node to search through ) ``` + ??? Example "Search child node type within parent" + ```python + materials_exact_name_in_project_paginator = cript_api.search( + node_type=cript.Material, + search_mode=cript.SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT, + value_to_search="N-Butyl-2-chlorobenzamide", + parent_node=my_project_node + ) + ``` + Parameters ---------- node_type : UUIDBaseNode @@ -949,24 +959,31 @@ def search(self, node_type: UUIDBaseNode, search_mode: SearchModes, value_to_sea # requesting a page of some primary node if search_mode == SearchModes.NODE_TYPE: - api_endpoint = f"{self._host}/{node_type}" + api_endpoint = f"{self._host}/{node_type}/" + # not using `value_to_search` + value_to_search = None elif search_mode == SearchModes.CONTAINS_NAME: - api_endpoint = f"{self._host}/search/{node_type}" + api_endpoint = f"{self._host}/search/{node_type}/" elif search_mode == SearchModes.EXACT_NAME: - api_endpoint = f"{self._host}/search/exact/{node_type}" + api_endpoint = f"{self._host}/search/exact/{node_type}/" elif search_mode == SearchModes.UUID: - api_endpoint = f"{self._host}/{node_type}/{value_to_search}" + api_endpoint = f"{self._host}/{node_type}/{value_to_search}/" # putting the value_to_search in the URL instead of a query value_to_search = None elif search_mode == SearchModes.BIGSMILES: api_endpoint = f"{self._host}/search/bigsmiles/" - elif search_mode == SearchModes.NODE_TYPE_WITHIN_PARENT: - api_endpoint = f"{self._host}/{parent_node.node_type}/{parent_node.uuid}/{node_type}" + elif search_mode == SearchModes.CHILD_NODE_TYPE_WITHIN_PARENT: + api_endpoint = f"{self._host}/{parent_node.node_type}/{parent_node.uuid}/{node_type}/" + # not using `value_to_search` + value_to_search = None + + elif search_mode == SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT: + api_endpoint = f"{self._host}/search/exact/{parent_node.node_type}/{parent_node.uuid}/{node_type}/" assert api_endpoint != "" From 1808f3c384940e95c489b769e1d36e428cab2804 Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 17:59:02 -0700 Subject: [PATCH 08/15] added new search mode to tests to test_api.py * `CHILD_NODE_TYPE_WITHIN_PARENT` * `CHILD_WITH_EXACT_NAME_WITHIN_PARENT` --- tests/api/test_api.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 836818c09..f4b03d17c 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -11,6 +11,7 @@ from conftest import HAS_INTEGRATION_TESTS_ENABLED import cript +from cript import load_nodes_from_json from cript.api.exceptions import InvalidVocabulary from cript.api.paginator import Paginator from cript.nodes.exceptions import CRIPTNodeSchemaError @@ -388,18 +389,36 @@ def test_api_search_uuid(cript_api: cript.API) -> None: @pytest.mark.skipif(not HAS_INTEGRATION_TESTS_ENABLED, reason="requires a real cript_api_token") -def test_api_search_node_type_within_parent(cript_api: cript.API, simple_project_node) -> None: +def test_api_search_child_node_type_within_parent(cript_api: cript.API, cript_project_node) -> None: """ tests search NODE_TYPE_WITHIN_PARENT searches for all materials within a project node """ + materials_in_project_paginator = cript_api.search(node_type=cript.Material, search_mode=cript.SearchModes.CHILD_NODE_TYPE_WITHIN_PARENT, parent_node=cript_project_node) - all_materials_in_project_paginator = cript_api.search(node_type=cript.Material, search_mode=cript.SearchModes.NODE_TYPE_WITHIN_PARENT, parent_node=simple_project_node) + assert isinstance(materials_in_project_paginator, Paginator) + assert len(materials_in_project_paginator.current_page_results) >= 1 - print(simple_project_node.uuid) - assert isinstance(all_materials_in_project_paginator, Paginator) - assert len(all_materials_in_project_paginator.current_page_results) >= 1 +@pytest.mark.skipif(not HAS_INTEGRATION_TESTS_ENABLED, reason="requires a real cript_api_token") +def test_api_search_child_with_exact_name_within_parent(cript_api: cript.API, cript_project_node) -> None: + """ + tests search NODE_TYPE_WITHIN_PARENT + searches for all materials within a project node + """ + + exact_name_to_search = "N-Butyl-2-chlorobenzamide" + + materials_exact_name_in_project_paginator = cript_api.search( + node_type=cript.Material, + search_mode=cript.SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT, + value_to_search=exact_name_to_search, + parent_node=cript_project_node + ) + + assert isinstance(materials_exact_name_in_project_paginator, Paginator) + assert len(materials_exact_name_in_project_paginator.current_page_results) >= 1 + assert materials_exact_name_in_project_paginator.current_page_results[0]["name"] == exact_name_to_search def test_get_my_user_node_from_api(cript_api: cript.API) -> None: From cf45a73430bffe200a0fafbba1dbd8963b7594aa Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 17:59:34 -0700 Subject: [PATCH 09/15] formatted with black --- tests/api/test_api.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index f4b03d17c..6d42c9705 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -406,15 +406,9 @@ def test_api_search_child_with_exact_name_within_parent(cript_api: cript.API, cr tests search NODE_TYPE_WITHIN_PARENT searches for all materials within a project node """ - exact_name_to_search = "N-Butyl-2-chlorobenzamide" - materials_exact_name_in_project_paginator = cript_api.search( - node_type=cript.Material, - search_mode=cript.SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT, - value_to_search=exact_name_to_search, - parent_node=cript_project_node - ) + materials_exact_name_in_project_paginator = cript_api.search(node_type=cript.Material, search_mode=cript.SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT, value_to_search=exact_name_to_search, parent_node=cript_project_node) assert isinstance(materials_exact_name_in_project_paginator, Paginator) assert len(materials_exact_name_in_project_paginator.current_page_results) >= 1 From 11ce0879ac1db86cfd82f76889bdc6876836b39d Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 18:10:55 -0700 Subject: [PATCH 10/15] giving good errors in case the user forgets to pass in the required argument `value_to_search` or `parent_node` --- src/cript/api/api.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/cript/api/api.py b/src/cript/api/api.py index 4f2bf14e9..28266c231 100644 --- a/src/cript/api/api.py +++ b/src/cript/api/api.py @@ -966,9 +966,15 @@ def search(self, node_type: UUIDBaseNode, search_mode: SearchModes, value_to_sea elif search_mode == SearchModes.CONTAINS_NAME: api_endpoint = f"{self._host}/search/{node_type}/" + if value_to_search is None: + raise ValueError("`value_to_search` is needed for `SearchModes.CONTAINS_NAME`") + elif search_mode == SearchModes.EXACT_NAME: api_endpoint = f"{self._host}/search/exact/{node_type}/" + if value_to_search is None: + raise ValueError("`value_to_search` is needed for `SearchModes.EXACT_NAME`") + elif search_mode == SearchModes.UUID: api_endpoint = f"{self._host}/{node_type}/{value_to_search}/" # putting the value_to_search in the URL instead of a query @@ -977,14 +983,27 @@ def search(self, node_type: UUIDBaseNode, search_mode: SearchModes, value_to_sea elif search_mode == SearchModes.BIGSMILES: api_endpoint = f"{self._host}/search/bigsmiles/" + if value_to_search is None: + raise ValueError("`value_to_search` is needed for `SearchModes.BIGSMILES`") + + elif search_mode == SearchModes.CHILD_NODE_TYPE_WITHIN_PARENT: api_endpoint = f"{self._host}/{parent_node.node_type}/{parent_node.uuid}/{node_type}/" # not using `value_to_search` value_to_search = None + if parent_node is None: + raise ValueError("`parent_node` is needed for `SearchModes.CHILD_NODE_TYPE_WITHIN_PARENT`") + elif search_mode == SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT: api_endpoint = f"{self._host}/search/exact/{parent_node.node_type}/{parent_node.uuid}/{node_type}/" + if value_to_search is None: + raise ValueError("`value_to_search` is needed for `SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT`") + + if parent_node is None: + raise ValueError("`parent_node` is needed for `SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT`") + assert api_endpoint != "" # TODO error handling if none of the API endpoints got hit From ba46fa50dda9e1a233291978d194b576235baa80 Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 18:13:24 -0700 Subject: [PATCH 11/15] switching the order of gaurd clauses to be more efficient --- src/cript/api/api.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/cript/api/api.py b/src/cript/api/api.py index 28266c231..62412674b 100644 --- a/src/cript/api/api.py +++ b/src/cript/api/api.py @@ -964,46 +964,45 @@ def search(self, node_type: UUIDBaseNode, search_mode: SearchModes, value_to_sea value_to_search = None elif search_mode == SearchModes.CONTAINS_NAME: - api_endpoint = f"{self._host}/search/{node_type}/" - if value_to_search is None: raise ValueError("`value_to_search` is needed for `SearchModes.CONTAINS_NAME`") - elif search_mode == SearchModes.EXACT_NAME: - api_endpoint = f"{self._host}/search/exact/{node_type}/" + api_endpoint = f"{self._host}/search/{node_type}/" + elif search_mode == SearchModes.EXACT_NAME: if value_to_search is None: raise ValueError("`value_to_search` is needed for `SearchModes.EXACT_NAME`") + api_endpoint = f"{self._host}/search/exact/{node_type}/" + elif search_mode == SearchModes.UUID: api_endpoint = f"{self._host}/{node_type}/{value_to_search}/" # putting the value_to_search in the URL instead of a query value_to_search = None elif search_mode == SearchModes.BIGSMILES: - api_endpoint = f"{self._host}/search/bigsmiles/" - if value_to_search is None: raise ValueError("`value_to_search` is needed for `SearchModes.BIGSMILES`") + api_endpoint = f"{self._host}/search/bigsmiles/" elif search_mode == SearchModes.CHILD_NODE_TYPE_WITHIN_PARENT: + if parent_node is None: + raise ValueError("`parent_node` is needed for `SearchModes.CHILD_NODE_TYPE_WITHIN_PARENT`") + api_endpoint = f"{self._host}/{parent_node.node_type}/{parent_node.uuid}/{node_type}/" # not using `value_to_search` value_to_search = None - if parent_node is None: - raise ValueError("`parent_node` is needed for `SearchModes.CHILD_NODE_TYPE_WITHIN_PARENT`") - elif search_mode == SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT: - api_endpoint = f"{self._host}/search/exact/{parent_node.node_type}/{parent_node.uuid}/{node_type}/" - if value_to_search is None: raise ValueError("`value_to_search` is needed for `SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT`") if parent_node is None: raise ValueError("`parent_node` is needed for `SearchModes.CHILD_WITH_EXACT_NAME_WITHIN_PARENT`") + api_endpoint = f"{self._host}/search/exact/{parent_node.node_type}/{parent_node.uuid}/{node_type}/" + assert api_endpoint != "" # TODO error handling if none of the API endpoints got hit From eeded0bd2cc3fc147ff732930797002185a9c1d4 Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 18:17:44 -0700 Subject: [PATCH 12/15] fixed spelling errors --- .trunk/configs/.cspell.json | 3 ++- tests/fixtures/primary_nodes.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.trunk/configs/.cspell.json b/.trunk/configs/.cspell.json index 925a5edf9..906a008f3 100644 --- a/.trunk/configs/.cspell.json +++ b/.trunk/configs/.cspell.json @@ -117,6 +117,7 @@ "setuptools", "miniconda", "pymdown", - "BCDB" + "BCDB", + "chlorobenzamide" ] } diff --git a/tests/fixtures/primary_nodes.py b/tests/fixtures/primary_nodes.py index 5de6af34f..24e6191d4 100644 --- a/tests/fixtures/primary_nodes.py +++ b/tests/fixtures/primary_nodes.py @@ -13,7 +13,7 @@ @pytest.fixture(scope="function") def cript_project_node(cript_api: cript.API) -> cript.Project: """ - The CRIPT project node that exists on all server environements + The CRIPT project node that exists on all server environments Notes ----- From 1bb346dc442f48013c28bc91b99acef64c1390c4 Mon Sep 17 00:00:00 2001 From: nh916 Date: Wed, 23 Aug 2023 18:22:02 -0700 Subject: [PATCH 13/15] removed unused import --- tests/api/test_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 6d42c9705..252607a46 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -11,7 +11,6 @@ from conftest import HAS_INTEGRATION_TESTS_ENABLED import cript -from cript import load_nodes_from_json from cript.api.exceptions import InvalidVocabulary from cript.api.paginator import Paginator from cript.nodes.exceptions import CRIPTNodeSchemaError From 64dbd91287aa0a72fc1514cdb78d40775aeca248 Mon Sep 17 00:00:00 2001 From: nh916 Date: Thu, 24 Aug 2023 12:23:44 -0700 Subject: [PATCH 14/15] removed not implemented `SearchMode` --- src/cript/api/valid_search_modes.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cript/api/valid_search_modes.py b/src/cript/api/valid_search_modes.py index 593a192f3..cadd8c880 100644 --- a/src/cript/api/valid_search_modes.py +++ b/src/cript/api/valid_search_modes.py @@ -20,9 +20,6 @@ class SearchModes(Enum): CHILD_NODE_TYPE_WITHIN_PARENT: str Search for a node type within a parent. > Example: Find all the materials within this specific project. - CHILD_CONTAINS_NAME_WITHIN_PARENT: str - Search for a node containing a value for an attribute within a parent - > Example: Search for all materials that contain a certain name for the field specified within a project. CHILD_WITH_EXACT_NAME_WITHIN_PARENT: str Search for an exact node name within a parent > Example: Search for the materials with that exact name in the project or returns nothing. @@ -45,5 +42,4 @@ class SearchModes(Enum): UUID: str = "uuid" BIGSMILES: str = "bigsmiles" CHILD_NODE_TYPE_WITHIN_PARENT: str = "child_node_type_within_parent" - CHILD_CONTAINS_NAME_WITHIN_PARENT: str = "child_contains_name_within_parent" CHILD_WITH_EXACT_NAME_WITHIN_PARENT: str = "child_with_exact_name_within_parent" From 6442630132f45d60ab64e7632afc35bf355b6542 Mon Sep 17 00:00:00 2001 From: nh916 Date: Thu, 24 Aug 2023 14:16:12 -0700 Subject: [PATCH 15/15] added `cript.API.search()` documentation about raising exception --- src/cript/api/api.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/cript/api/api.py b/src/cript/api/api.py index 62412674b..c310f1993 100644 --- a/src/cript/api/api.py +++ b/src/cript/api/api.py @@ -943,6 +943,22 @@ def search(self, node_type: UUIDBaseNode, search_mode: SearchModes, value_to_sea The parent that you are searching through. > Not applicable for all search modes + Raises + ------ + ValueError + Raised when required arguments are missing for a specific search mode, preventing a silent failure. + + Certain arguments are specific to different search modes. To ensure smooth API calls and avoid + confusion, this function performs a *pre-call* check for the necessary arguments based on the chosen + search mode. If a required argument is missing, a `ValueError` is raised. + + Example: + ```python + cript_api.search(node_type=cript.Material, search_mode=cript.SearchModes.EXACT_NAME) + ``` + > This example will raise a `ValueError` since the `value_to_search` argument is missing, which is + required for the `EXACT_NAME` SearchMode. + Returns ------- Paginator