diff --git a/qf_lib/data_providers/bloomberg_beap_hapi/bloomberg_beap_hapi_universe_provider.py b/qf_lib/data_providers/bloomberg_beap_hapi/bloomberg_beap_hapi_universe_provider.py index 3306b33a..2e9dde88 100644 --- a/qf_lib/data_providers/bloomberg_beap_hapi/bloomberg_beap_hapi_universe_provider.py +++ b/qf_lib/data_providers/bloomberg_beap_hapi/bloomberg_beap_hapi_universe_provider.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +import re from typing import Union, Sequence, Optional, List, Tuple import pprint from urllib.parse import urljoin @@ -64,7 +64,7 @@ def get_universe_url(self, universe_id: str, tickers: Union[str, Sequence[str]], URL address of created hapi universe """ tickers, got_single_field = convert_to_list(tickers, str) - tickers_and_types = [self._get_indentifier_and_type(ticker) for ticker in tickers] + tickers_and_types = [self._get_identifier_and_type(ticker) for ticker in tickers] contains = [{'@type': 'Identifier', 'identifierType': identifier_type, 'identifierValue': identifier} for identifier_type, identifier in tickers_and_types if identifier] if len(contains) == 0: @@ -108,7 +108,7 @@ def get_universe_url(self, universe_id: str, tickers: Union[str, Sequence[str]], return universe_url - def _get_indentifier_and_type(self, ticker) -> Tuple[Optional[str], Optional[str]]: + def _get_identifier_and_type(self, identifier: str) -> Tuple[Optional[str], Optional[str]]: blp_hapi_compatibility_mapping = { "ticker": "TICKER", "cusip": "CUSIP", @@ -122,15 +122,16 @@ def _get_indentifier_and_type(self, ticker) -> Tuple[Optional[str], Optional[str "cats": "CATS" } - ticker = f"/ticker/{ticker}" if ticker.count("/") == 0 else ticker - if ticker.count("/") != 2: - self.logger.error(f"Detected incorrect identifier: {ticker}. It will be removed from the data request.\n" + identifier = f"/ticker/{identifier}" if not identifier.startswith("/") else identifier + match = re.match(r"^/(\w+)/(.+)", identifier) + if not match: + self.logger.error(f"Detected incorrect identifier: {identifier}. It will be removed from the data request.\n" f"In order to provide an identifier, which is not a ticker, please use " f"'/id_type/identifier' format, with id_type being one of the following: " f"{blp_hapi_compatibility_mapping.values()}") return None, None - id_type, id = ticker.lstrip("/").split("/") + id_type, id = match.groups() try: return blp_hapi_compatibility_mapping[id_type.lower()], id except KeyError: diff --git a/qf_lib/tests/unit_tests/data_providers/bloomberg_beap_hapi/test_universe_provider.py b/qf_lib/tests/unit_tests/data_providers/bloomberg_beap_hapi/test_universe_provider.py index 41f7f01b..85fb8430 100644 --- a/qf_lib/tests/unit_tests/data_providers/bloomberg_beap_hapi/test_universe_provider.py +++ b/qf_lib/tests/unit_tests/data_providers/bloomberg_beap_hapi/test_universe_provider.py @@ -31,33 +31,60 @@ def setUp(self): self.location = '{}universes/{}/'.format(self.address_url, self.fieldlist_id) self.host = 'https://api.bloomberg.com' self.account_url = urljoin(self.host, self.address_url) - self.tickers = ['TICKER'] self.post_response.headers = {'Location': self.location} - def test_get_fields_url__get_response(self): + def test_get_universe_url__get_response(self): self.session_mock.get.return_value.status_code = 200 provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url) field_overrides = [("CHAIN_DATE", datetime.now().strftime('%Y%m%d')), ('INCLUDE_EXPIRED_CONTRACTS', 'Y')] - url = provider.get_universe_url(self.fieldlist_id, self.tickers, field_overrides) + tickers = ["SPY US Equity"] + url = provider.get_universe_url(self.fieldlist_id, tickers, field_overrides) self.assertEqual(url, urljoin(self.host, self.location)) - def test_get_fields_url__post_response(self): + def test_get_universe_url__post_response(self): self.session_mock.get.return_value.status_code = 404 self.post_response.status_code = 201 provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url) field_overrides = [("CHAIN_DATE", datetime.now().strftime('%Y%m%d')), ('INCLUDE_EXPIRED_CONTRACTS', 'Y')] - url = provider.get_universe_url(self.fieldlist_id, self.tickers, field_overrides) + tickers = ["SPY US Equity"] + url = provider.get_universe_url(self.fieldlist_id, tickers, field_overrides) self.assertEqual(url, urljoin(self.host, self.location)) - def test_get_fields_url__unknown_get_response(self): + def test_get_universe_url__unknown_get_response(self): self.session_mock.get.return_value.status_code = 404 provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url) field_overrides = [("CHAIN_DATE", datetime.now().strftime('%Y%m%d')), ('INCLUDE_EXPIRED_CONTRACTS', 'Y')] - self.assertRaises(BloombergError, provider.get_universe_url, self.fieldlist_id, self.tickers, field_overrides) + tickers = ["SPY US Equity"] + self.assertRaises(BloombergError, provider.get_universe_url, self.fieldlist_id, tickers, field_overrides) - def test_get_fields_url__unknown_post_response(self): + def test_get_universe_url__unknown_post_response(self): self.session_mock.get.return_value.status_code = 404 self.post_response.status_code = 200 provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url) field_overrides = [("CHAIN_DATE", datetime.now().strftime('%Y%m%d')), ('INCLUDE_EXPIRED_CONTRACTS', 'Y')] - self.assertRaises(BloombergError, provider.get_universe_url, self.fieldlist_id, self.tickers, field_overrides) + tickers = ["SPY US Equity"] + self.assertRaises(BloombergError, provider.get_universe_url, self.fieldlist_id, tickers, field_overrides) + + def test_get_universe_url__no_correct_identifiers(self): + provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url) + tickers = ["/incorrect_type/SPY US Equity"] + self.assertRaises(ValueError, provider.get_universe_url, self.fieldlist_id, tickers) + + def test_get_universe_url__empty_identifiers_list(self): + provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url) + tickers = [] + self.assertRaises(ValueError, provider.get_universe_url, self.fieldlist_id, tickers) + + def test_get_universe_url__correct_identifier_with_special_characters(self): + self.session_mock.get.return_value.status_code = 200 + provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url) + tickers = ["/ticker/AA/ US Equity"] + url = provider.get_universe_url(self.fieldlist_id, tickers) + self.assertEqual(url, urljoin(self.host, self.location)) + + def test_get_universe_url__correct_ticker_with_special_characters(self): + self.session_mock.get.return_value.status_code = 200 + provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url) + tickers = ["AA/ US Equity"] + url = provider.get_universe_url(self.fieldlist_id, tickers) + self.assertEqual(url, urljoin(self.host, self.location))