diff --git a/bfabric/engine/engine_suds.py b/bfabric/engine/engine_suds.py index 258d0f1d..f01271a5 100644 --- a/bfabric/engine/engine_suds.py +++ b/bfabric/engine/engine_suds.py @@ -8,7 +8,8 @@ from bfabric.engine.response_format_suds import suds_asdict_recursive from bfabric.errors import BfabricRequestError, get_response_errors -from bfabric.results.result_container import _clean_result, ResultContainer +from bfabric.results.result_container import ResultContainer +from bfabric.results.response_format_dict import clean_result if TYPE_CHECKING: from suds.serviceproxy import ServiceProxy @@ -98,10 +99,10 @@ def _convert_results(self, response: Any, endpoint: str) -> ResultContainer: results = [] for result in response[endpoint]: result_parsed = suds_asdict_recursive(result, convert_types=True) - result_parsed = _clean_result( + result_parsed = clean_result( result_parsed, drop_underscores_suds=self._drop_underscores, - sort_responses=True, + sort_keys=True, ) results += [result_parsed] return ResultContainer(results=results, total_pages_api=n_available_pages, errors=errors) diff --git a/bfabric/engine/engine_zeep.py b/bfabric/engine/engine_zeep.py index 0e4cf752..b91c120c 100644 --- a/bfabric/engine/engine_zeep.py +++ b/bfabric/engine/engine_zeep.py @@ -6,7 +6,8 @@ from zeep.helpers import serialize_object from bfabric.errors import BfabricRequestError, get_response_errors -from bfabric.results.result_container import ResultContainer, _clean_result +from bfabric.results.result_container import ResultContainer +from bfabric.results.response_format_dict import clean_result if TYPE_CHECKING: from bfabric.bfabric_config import BfabricAuth @@ -122,10 +123,10 @@ def _convert_results(self, response: Any, endpoint: str) -> ResultContainer: results = [] for result in response[endpoint]: results_parsed = dict(serialize_object(result, target_cls=dict)) - results_parsed = _clean_result( + results_parsed = clean_result( results_parsed, drop_underscores_suds=False, # NOTE: Underscore problem specific to SUDS - sort_responses=True, + sort_keys=True, ) results += [results_parsed] return ResultContainer(results=results, total_pages_api=n_available_pages, errors=errors) diff --git a/bfabric/results/response_format_dict.py b/bfabric/results/response_format_dict.py index 47ce298a..fb7e0cc5 100644 --- a/bfabric/results/response_format_dict.py +++ b/bfabric/results/response_format_dict.py @@ -1,4 +1,5 @@ from __future__ import annotations + from copy import deepcopy @@ -115,3 +116,19 @@ def sort_dicts_by_key(response: list | dict, inplace: bool = True) -> list | dic response_filtered = deepcopy(response) if not inplace else response _recursive_sort_dicts_by_key(response_filtered) return response_filtered + + +def clean_result(result: dict, drop_underscores_suds: bool = True, sort_keys: bool = False) -> dict: + """ + :param result: the response dictionary to clean + :param drop_underscores_suds: if True, the keys of the dictionaries in the response will have leading + underscores removed in some cases (relevant for SUDS) + :param sort_keys: the keys of the dictionaries in the response will be sorted (recursively) + """ + result = deepcopy(result) + if drop_underscores_suds: + map_element_keys(result, {"_id": "id", "_classname": "classname", "_projectid": "projectid"}, inplace=True) + if sort_keys: + sort_dicts_by_key(result, inplace=True) + + return result diff --git a/bfabric/results/result_container.py b/bfabric/results/result_container.py index 0a5c086c..b8bd3a48 100644 --- a/bfabric/results/result_container.py +++ b/bfabric/results/result_container.py @@ -98,19 +98,3 @@ def to_polars(self, drop_empty: bool = False) -> polars.DataFrame: import polars return polars.DataFrame(self.to_list_dict(drop_empty=drop_empty)) - - -def _clean_result(result: dict, drop_underscores_suds: bool = True, sort_responses: bool = False) -> dict: - """ - :param drop_underscores_suds: if True, the keys of the dictionaries in the response will have leading - underscores removed in some cases (relevant for SUDS) - :param sort_responses: the keys of the dictionaries in the response will be sorted (recursively) - """ - if drop_underscores_suds: - formatter.map_element_keys( - result, {"_id": "id", "_classname": "classname", "_projectid": "projectid"}, inplace=True - ) - if sort_responses: - formatter.sort_dicts_by_key(result, inplace=True) - - return result diff --git a/bfabric/tests/unit/test_bfabric.py b/bfabric/tests/unit/test_bfabric.py index 355114be..23e01d3e 100644 --- a/bfabric/tests/unit/test_bfabric.py +++ b/bfabric/tests/unit/test_bfabric.py @@ -267,6 +267,38 @@ def test_delete_when_auth_and_check_true(self): method_assert_success.assert_called_once_with() mock_engine.delete.assert_called_once_with(endpoint=endpoint, id=10, auth=self.mock_auth) + @patch.object(Bfabric, "read") + def test_exists_when_true(self, method_read): + method_read.return_value.__len__.return_value = 1 + self.assertTrue(self.mock_bfabric.exists(endpoint="test_endpoint", key="key", value="value")) + method_read.assert_called_once_with( + endpoint="test_endpoint", obj={"key": "value"}, max_results=1, check=True, return_id_only=True + ) + + @patch.object(Bfabric, "read") + def test_exists_when_true_and_extra_args(self, method_read): + method_read.return_value.__len__.return_value = 1 + self.assertTrue( + self.mock_bfabric.exists( + endpoint="test_endpoint", key="key", value="value", query={"extra": "arg"}, check=False + ) + ) + method_read.assert_called_once_with( + endpoint="test_endpoint", + obj={"key": "value", "extra": "arg"}, + max_results=1, + check=False, + return_id_only=True, + ) + + @patch.object(Bfabric, "read") + def test_exists_when_false(self, method_read): + method_read.return_value.__len__.return_value = 0 + self.assertFalse(self.mock_bfabric.exists(endpoint="test_endpoint", key="key", value="value")) + method_read.assert_called_once_with( + endpoint="test_endpoint", obj={"key": "value"}, max_results=1, check=True, return_id_only=True + ) + @patch.object(Bfabric, "save") def test_upload_resource(self, method_save): resource_name = "hello_world.txt" diff --git a/bfabric/tests/unit/test_response_format_dict.py b/bfabric/tests/unit/test_response_format_dict.py index 35c312b3..9fe1765b 100644 --- a/bfabric/tests/unit/test_response_format_dict.py +++ b/bfabric/tests/unit/test_response_format_dict.py @@ -1,4 +1,5 @@ import unittest + import bfabric.results.response_format_dict as response_format_dict @@ -27,6 +28,15 @@ def test_sort_dicts_by_key(self): output_list_dict = response_format_dict.sort_dicts_by_key(input_list_dict, inplace=False) self.assertEqual(str(output_list_dict), str(target_list_dict)) + def test_clean_result(self): + result_input = [{"b": 1, "a": 2, "_id": 3}, {"b": 4, "_id": 5, "a": 6}] + cleaned = response_format_dict.clean_result(result_input, drop_underscores_suds=True, sort_keys=True) + self.assertEqual(repr([{"a": 2, "b": 1, "id": 3}, {"a": 6, "b": 4, "id": 5}]), repr(cleaned)) + self.assertEqual( + repr([{"b": 1, "a": 2, "_id": 3}, {"b": 4, "_id": 5, "a": 6}]), + repr(result_input), + ) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/bfabric/tests/unit/test_result_container.py b/bfabric/tests/unit/test_result_container.py index 8ced19ab..6591fa89 100644 --- a/bfabric/tests/unit/test_result_container.py +++ b/bfabric/tests/unit/test_result_container.py @@ -1,6 +1,8 @@ import logging import unittest +import polars.testing + from bfabric.results.result_container import ResultContainer @@ -15,8 +17,8 @@ def test_str(self): self.assertEqual("[4, 5]", str(self.res2)) def test_repr(self): - self.assertEqual("[1, 2, 3]", str(self.res1)) - self.assertEqual("[4, 5]", str(self.res2)) + self.assertEqual("[1, 2, 3]", repr(self.res1)) + self.assertEqual("[4, 5]", repr(self.res2)) def test_len(self): self.assertEqual(3, len(self.res1)) @@ -82,6 +84,11 @@ def test_to_list_dict_when_drop_empty(self): expected = [{"b": 1}, {"a": 2, "b": 3}] self.assertListEqual(expected, self.res_with_empty.to_list_dict(drop_empty=True)) + def test_to_polars(self): + res = ResultContainer([{"a": 1, "b": 2}, {"a": 3, "b": 4}]) + df = res.to_polars() + polars.testing.assert_frame_equal(polars.DataFrame({"a": [1, 3], "b": [2, 4]}), df) + if __name__ == "__main__": unittest.main(verbosity=2)