diff --git a/.config/pre-commit-config.yaml b/.config/pre-commit-config.yaml index 8aaefe0..9342c05 100644 --- a/.config/pre-commit-config.yaml +++ b/.config/pre-commit-config.yaml @@ -8,7 +8,7 @@ repos: - id: end-of-file-fixer - id: check-ast - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.14 + rev: v0.3.0 hooks: # Run the linter. - id: ruff @@ -17,7 +17,7 @@ repos: - id: ruff-format args: [--config, .config/ruff.toml] - repo: https://github.com/jazzband/pip-tools - rev: 7.3.0 + rev: 7.4.0 hooks: - id: pip-compile name: pip-compile requirements.txt diff --git a/.github/workflows/run-python-tests.yaml b/.github/workflows/run-python-tests.yaml index 7e3ec06..88690c9 100644 --- a/.github/workflows/run-python-tests.yaml +++ b/.github/workflows/run-python-tests.yaml @@ -28,6 +28,9 @@ jobs: python -m pip install --upgrade pip pip install --upgrade hatch - name: Test with hatch/pytest + env: + WFP_KEY: ${{ secrets.WFP_KEY }} + WFP_SECRET: ${{ secrets.WFP_SECRET }} run: | hatch run test:test - name: Check styling diff --git a/pyproject.toml b/pyproject.toml index 50fd957..1771bc2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,12 +50,16 @@ Homepage = "https://github.com/OCHA-DAP/hdx-python-country" [project.optional-dependencies] test = ["pytest", "pytest-cov"] dev = ["pre-commit"] +wfp = ["data-bridges-client@git+https://github.com/WFP-VAM/DataBridgesAPI@dev#egg=data-bridges-client"] ######### # Hatch # ######### +[tool.hatch.metadata] +allow-direct-references = true + # Build [tool.hatch.build.targets.wheel] @@ -76,7 +80,7 @@ version_scheme = "python-simplified-semver" # Tests [tool.hatch.envs.test] -features = ["test"] +features = ["test", "wfp"] [tool.hatch.envs.test.scripts] test = """ diff --git a/requirements.txt b/requirements.txt index 924eefc..d9cd8d9 100755 --- a/requirements.txt +++ b/requirements.txt @@ -2,17 +2,22 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile --all-extras --output-file=requirements.txt --resolver=backtracking pyproject.toml +# pip-compile --all-extras --output-file=requirements.txt pyproject.toml # annotated-types==0.6.0 # via pydantic +anyio==4.3.0 + # via httpx attrs==23.2.0 # via # frictionless # jsonlines # jsonschema certifi==2024.2.2 - # via requests + # via + # httpcore + # httpx + # requests cfgv==3.4.0 # via pre-commit chardet==5.2.0 @@ -24,7 +29,11 @@ click==8.1.7 colorama==0.4.6 # via typer coverage[toml]==7.4.3 - # via pytest-cov + # via + # coverage + # pytest-cov +data-bridges-client @ git+https://github.com/WFP-VAM/DataBridgesAPI@dev + # via hdx-python-country (pyproject.toml) distlib==0.3.8 # via virtualenv et-xmlfile==1.1.0 @@ -33,14 +42,23 @@ filelock==3.13.1 # via virtualenv frictionless==5.16.1 # via hdx-python-utilities +h11==0.14.0 + # via httpcore hdx-python-utilities==3.6.5 # via hdx-python-country (pyproject.toml) +httpcore==1.0.4 + # via httpx +httpx==0.27.0 + # via data-bridges-client humanize==4.9.0 # via frictionless identify==2.5.35 # via pre-commit idna==3.6 - # via requests + # via + # anyio + # httpx + # requests ijson==3.2.3 # via hdx-python-utilities iniconfig==2.0.0 @@ -87,8 +105,10 @@ ply==3.11 # libhxl pre-commit==3.6.2 # via hdx-python-country (pyproject.toml) -pydantic==2.6.2 - # via frictionless +pydantic==2.6.3 + # via + # data-bridges-client + # frictionless pydantic-core==2.16.3 # via pydantic pygments==2.17.2 @@ -105,6 +125,7 @@ pytest-cov==4.1.0 # via hdx-python-country (pyproject.toml) python-dateutil==2.8.2 # via + # data-bridges-client # frictionless # hdx-python-utilities # libhxl @@ -128,7 +149,7 @@ requests-file==2.0.0 # via hdx-python-utilities rfc3986==2.0.0 # via frictionless -rich==13.7.0 +rich==13.7.1 # via typer ruamel-yaml==0.18.6 # via hdx-python-utilities @@ -142,6 +163,10 @@ six==1.16.0 # via # isodate # python-dateutil +sniffio==1.3.1 + # via + # anyio + # httpx stringcase==1.2.0 # via frictionless structlog==24.1.0 @@ -153,9 +178,12 @@ tabulate==0.9.0 text-unidecode==1.3 # via python-slugify typer[all]==0.9.0 - # via frictionless + # via + # frictionless + # typer typing-extensions==4.10.0 # via + # data-bridges-client # frictionless # pydantic # pydantic-core @@ -164,8 +192,9 @@ unidecode==1.3.8 # via # libhxl # pyphonetics -urllib3==2.2.1 +urllib3==2.0.7 # via + # data-bridges-client # libhxl # requests validators==0.22.0 diff --git a/src/hdx/location/__init__.py b/src/hdx/location/__init__.py index d986e5b..a197743 100755 --- a/src/hdx/location/__init__.py +++ b/src/hdx/location/__init__.py @@ -1 +1,17 @@ +from datetime import datetime + from ._version import version as __version__ # noqa: F401 +from hdx.utilities.dateparse import get_timestamp_from_datetime + + +def get_int_timestamp(date: datetime) -> int: + """ + Get integer timestamp from datetime object + + Args: + date (datetime): datetime object + + Returns: + int: Integer timestamp + """ + return int(round(get_timestamp_from_datetime(date))) diff --git a/src/hdx/location/adminlevel.py b/src/hdx/location/adminlevel.py index b076488..5888164 100755 --- a/src/hdx/location/adminlevel.py +++ b/src/hdx/location/adminlevel.py @@ -58,7 +58,7 @@ def __init__( "admin_name_replacements", {} ) self.admin_fuzzy_dont = admin_config.get("admin_fuzzy_dont", list()) - self.pcodes = list() + self.pcodes = [] self.pcode_lengths = {} self.name_to_pcode = {} self.pcode_to_name = {} @@ -634,7 +634,7 @@ def output_matches(self) -> List[str]: Returns: List[str]: List of matches """ - output = list() + output = [] for match in sorted(self.matches): line = f"{match[0]} - {match[1]}: Matching ({match[4]}) {match[2]} to {match[3]} on map" logger.info(line) @@ -647,7 +647,7 @@ def output_ignored(self) -> List[str]: Returns: List[str]: List of ignored """ - output = list() + output = [] for ignored in sorted(self.ignored): if len(ignored) == 2: line = f"{ignored[0]} - Ignored {ignored[1]}!" @@ -663,7 +663,7 @@ def output_errors(self) -> List[str]: Returns: List[str]: List of errors """ - output = list() + output = [] for error in sorted(self.errors): if len(error) == 2: line = f"{error[0]} - Could not find {error[1]} in map names!" diff --git a/src/hdx/location/country.py b/src/hdx/location/country.py index 4956616..08f67b2 100755 --- a/src/hdx/location/country.py +++ b/src/hdx/location/country.py @@ -1,4 +1,5 @@ """Country location""" + import copy import logging import re @@ -69,8 +70,8 @@ class Country: _countriesdata = None _ochaurl_default = "https://docs.google.com/spreadsheets/d/1NjSI2LaS3SqbgYc0HdD8oIb7lofGtiHgoKKATCpwVdY/export?format=csv&gid=1088874596" _ochaurl = _ochaurl_default - _country_name_overrides = dict() - _country_name_mappings = dict() + _country_name_overrides = {} + _country_name_mappings = {} @classmethod def _add_countriesdata(cls, iso3: str, hxlcountry: hxl.Row) -> Dict: @@ -131,24 +132,24 @@ def add_country_to_set(colname, idval, iso3): if regionname: add_country_to_set("regioncodes2countries", regionid, iso3) cls._countriesdata["regioncodes2names"][regionid] = regionname - cls._countriesdata["regionnames2codes"][ - regionname.upper() - ] = regionid + cls._countriesdata["regionnames2codes"][regionname.upper()] = ( + regionid + ) if sub_regionname: add_country_to_set("regioncodes2countries", sub_regionid, iso3) - cls._countriesdata["regioncodes2names"][ + cls._countriesdata["regioncodes2names"][sub_regionid] = ( + sub_regionname + ) + cls._countriesdata["regionnames2codes"][sub_regionname.upper()] = ( sub_regionid - ] = sub_regionname - cls._countriesdata["regionnames2codes"][ - sub_regionname.upper() - ] = sub_regionid + ) if intermediate_regionname: add_country_to_set( "regioncodes2countries", intermediate_regionid, iso3 ) - cls._countriesdata["regioncodes2names"][ - intermediate_regionid - ] = intermediate_regionname + cls._countriesdata["regioncodes2names"][intermediate_regionid] = ( + intermediate_regionname + ) cls._countriesdata["regionnames2codes"][ intermediate_regionname.upper() ] = intermediate_regionid @@ -167,21 +168,21 @@ def set_countriesdata(cls, countries: str) -> None: Returns: None """ - cls._countriesdata = dict() - cls._countriesdata["countries"] = dict() - cls._countriesdata["iso2iso3"] = dict() - cls._countriesdata["m49iso3"] = dict() - cls._countriesdata["countrynames2iso3"] = dict() - cls._countriesdata["regioncodes2countries"] = dict() - cls._countriesdata["regioncodes2names"] = dict() - cls._countriesdata["regionnames2codes"] = dict() - cls._countriesdata["aliases"] = dict() - cls._countriesdata["currencies"] = dict() + cls._countriesdata = {} + cls._countriesdata["countries"] = {} + cls._countriesdata["iso2iso3"] = {} + cls._countriesdata["m49iso3"] = {} + cls._countriesdata["countrynames2iso3"] = {} + cls._countriesdata["regioncodes2countries"] = {} + cls._countriesdata["regioncodes2names"] = {} + cls._countriesdata["regionnames2codes"] = {} + cls._countriesdata["aliases"] = {} + cls._countriesdata["currencies"] = {} for key, value in cls._country_name_mappings.items(): - cls._countriesdata["countrynames2iso3"][ - key.upper() - ] = value.upper() + cls._countriesdata["countrynames2iso3"][key.upper()] = ( + value.upper() + ) for country in countries: iso3 = country.get("#country+code+v_iso3") @@ -891,4 +892,4 @@ def get_countries_in_region( if exception is not None: raise exception - return list() + return [] diff --git a/src/hdx/location/currency.py b/src/hdx/location/currency.py index 58b4635..4c49439 100644 --- a/src/hdx/location/currency.py +++ b/src/hdx/location/currency.py @@ -1,10 +1,12 @@ """Currency conversion""" + import logging +from copy import copy from datetime import datetime, timezone from typing import Dict, Optional, Union +from . import get_int_timestamp from hdx.utilities.dateparse import ( - get_timestamp_from_datetime, now_utc, parse_date, ) @@ -44,19 +46,6 @@ class Currency: _fixed_now = None _threshold = 1.3 - @classmethod - def _get_int_timestamp(cls, date: datetime) -> int: - """ - Get integer timestamp from datetime object - - Args: - date (datetime): datetime object - - Returns: - int: Integer timestamp - """ - return int(round(get_timestamp_from_datetime(date))) - @classmethod def setup( cls, @@ -69,6 +58,8 @@ def setup( no_historic: bool = False, fixed_now: Optional[datetime] = None, log_level: int = logging.DEBUG, + current_rates_cache: Dict = {"USD": 1}, + historic_rates_cache: Dict = {}, ) -> None: """ Setup the sources. If you wish to use a static fallback file by setting @@ -85,13 +76,15 @@ def setup( no_historic (bool): Do not set up historic rates. Defaults to False. fixed_now (Optional[datetime]): Use a fixed datetime for now. Defaults to None (use datetime.now()). log_level (int): Level at which to log messages. Defaults to logging.DEBUG. + current_rates_cache (Dict): Pre-populate current rates cache with given values. Defaults to {"USD": 1}. + historic_rates_cache (Dict): Pre-populate historic rates cache with given values. Defaults to {}. Returns: None """ - cls._cached_current_rates = {"USD": 1} - cls._cached_historic_rates = dict() + cls._cached_current_rates = copy(current_rates_cache) + cls._cached_historic_rates = copy(historic_rates_cache) cls._rates_api = primary_rates_url cls._secondary_rates = None cls._secondary_historic = None @@ -131,10 +124,10 @@ def setup( filename="historic_rates.csv", logstr="secondary historic exchange rates", ) - cls._secondary_historic = dict() + cls._secondary_historic = {} for row in iterator: currency = row["Currency"] - date = cls._get_int_timestamp(parse_date(row["Date"])) + date = get_int_timestamp(parse_date(row["Date"])) rate = float(row["Rate"]) dict_of_dicts_add( cls._secondary_historic, currency, date, rate @@ -281,7 +274,7 @@ def _get_primary_rate( else: now = now_utc() get_close = False - timestamp = cls._get_int_timestamp(now) + timestamp = get_int_timestamp(now) else: get_close = True data = cls._get_primary_rates_data(currency, timestamp) @@ -479,7 +472,7 @@ def get_historic_rate( ) else: date = date.astimezone(timezone.utc) - timestamp = cls._get_int_timestamp(date) + timestamp = get_int_timestamp(date) if currency_data is not None: fx_rate = currency_data.get(timestamp) if fx_rate is not None: diff --git a/src/hdx/location/names.py b/src/hdx/location/names.py index f9770a5..40d1dcb 100644 --- a/src/hdx/location/names.py +++ b/src/hdx/location/names.py @@ -3,7 +3,7 @@ from unidecode import unidecode -non_ascii = "([^\x00-\x7F])+" +non_ascii = "([^\x00-\x7f])+" def clean_name(name: str) -> str: diff --git a/src/hdx/location/phonetics.py b/src/hdx/location/phonetics.py index 57b707c..a148295 100644 --- a/src/hdx/location/phonetics.py +++ b/src/hdx/location/phonetics.py @@ -11,7 +11,7 @@ def match( possible_names: ListTuple, name: str, alternative_name: Optional[str] = None, - transform_possible_names: ListTuple[Callable] = list(), + transform_possible_names: ListTuple[Callable] = [], threshold: int = 2, ) -> Optional[int]: """ diff --git a/src/hdx/location/wfp_exchangerates.py b/src/hdx/location/wfp_exchangerates.py new file mode 100644 index 0000000..1a8442b --- /dev/null +++ b/src/hdx/location/wfp_exchangerates.py @@ -0,0 +1,101 @@ +from datetime import timezone +from typing import Dict, List + +from data_bridges_client.exceptions import ( + ApiException, +) + +from . import get_int_timestamp +from hdx.utilities.typehint import ListTuple + +try: + from data_bridges_client import ApiClient, Configuration, CurrencyApi + from data_bridges_client.token import WfpApiToken +except ImportError: + pass + + +class WFPExchangeRates: + """Obtain WFP official exchange rates. Requires WFP credentials amd + installation of the WFP extra eg. `pip install hdx-python-country[wfp]` + + Args: + key: WFP API key + secret: WFP API secret + """ + + def __init__(self, key: str, secret: str): + # Configure OAuth2 access token for authorization: default + self.token = WfpApiToken(api_key=key, api_secret=secret) + configuration = Configuration() + configuration.access_token = self.token.refresh() + api_client = ApiClient(configuration) + self.api_instance = CurrencyApi(api_client) + + def get_currencies(self) -> List[str]: + """Get list of currencies in WFP API + + Returns: + List[str]: List of currencies in WFP API + """ + currencies = [] + for currencydto in self.api_instance.currency_list_get().items: + currencies.append(currencydto.name) + return currencies + + def get_currency_historic_rates(self, currency: str) -> Dict[int, float]: + """Get historic rates for currency from WFP API + + Args: + currency (str): Currency + + Returns: + Dict[int, float]: Mapping from timestamp to rate + """ + historic_rates = {} + page = 1 + while True: + try: + usdquotations = ( + self.api_instance.currency_usd_indirect_quotation_get( + currency_name=currency, page=page + ).items + ) + except ApiException as ex: + if ex.status not in (104, 401, 403): + raise + self.token.refresh() + usdquotations = ( + self.api_instance.currency_usd_indirect_quotation_get( + currency_name=currency, page=page + ).items + ) + if not usdquotations: + break + for usdquotation in usdquotations: + if not usdquotation.is_official: + continue + date = usdquotation.var_date.replace(tzinfo=timezone.utc) + timestamp = get_int_timestamp(date) + historic_rates[timestamp] = usdquotation.value + page = page + 1 + return historic_rates + + def get_historic_rates( + self, currencies: ListTuple[str] + ) -> Dict[str, Dict]: + """Get historic rates for a list of currencies from WFP API + + Args: + currencies (List[str]): List of currencies + + Returns: + Dict[str, Dict]: Mapping from currency to mapping from timestamp to rate + """ + historic_rates = {} + for currency in currencies: + currency_historic_rates = self.get_currency_historic_rates( + currency + ) + historic_rates[currency.upper()] = currency_historic_rates + return historic_rates diff --git a/tests/hdx/location/test_adminlevel.py b/tests/hdx/location/test_adminlevel.py index 81f9890..171e7cf 100755 --- a/tests/hdx/location/test_adminlevel.py +++ b/tests/hdx/location/test_adminlevel.py @@ -1,4 +1,5 @@ """location Tests""" + from os.path import join import pytest diff --git a/tests/hdx/location/test_country.py b/tests/hdx/location/test_country.py index 2da4d8f..35b1389 100755 --- a/tests/hdx/location/test_country.py +++ b/tests/hdx/location/test_country.py @@ -1,4 +1,5 @@ """location Tests""" + import hxl import pytest from hxl import InputOptions diff --git a/tests/hdx/location/test_currency.py b/tests/hdx/location/test_currency.py index a1f9be5..ffd27e5 100755 --- a/tests/hdx/location/test_currency.py +++ b/tests/hdx/location/test_currency.py @@ -1,8 +1,10 @@ """Currency Tests""" + from os.path import join import pytest +from hdx.location import get_int_timestamp from hdx.location.currency import Currency, CurrencyError from hdx.utilities.dateparse import parse_date from hdx.utilities.downloader import Download @@ -333,7 +335,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2017-02-15")) + timestamp = get_int_timestamp(parse_date("2017-02-15")) assert Currency._get_adjclose(indicators, "NGN", timestamp) == 314.5 indicators = { "adjclose": [{"adjclose": [33.13999938964844]}], @@ -347,7 +349,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2015-12-15")) + timestamp = get_int_timestamp(parse_date("2015-12-15")) assert Currency._get_adjclose(indicators, "COP", timestamp) is None indicators = { "adjclose": [{"adjclose": [605.5509643554688]}], @@ -361,7 +363,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2022-04-14")) + timestamp = get_int_timestamp(parse_date("2022-04-14")) assert ( Currency._get_adjclose(indicators, "XAF", timestamp) == 605.5509643554688 @@ -378,7 +380,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2022-04-15")) + timestamp = get_int_timestamp(parse_date("2022-04-15")) assert ( Currency._get_adjclose(indicators, "XAF", timestamp) == 601.632568359375 @@ -395,7 +397,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2017-02-15")) + timestamp = get_int_timestamp(parse_date("2017-02-15")) assert Currency._get_adjclose(indicators, "XXX", timestamp) == 3.145 Currency.setup(secondary_historic_url=secondary_historic_url) @@ -411,7 +413,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2017-02-15")) + timestamp = get_int_timestamp(parse_date("2017-02-15")) assert Currency._get_adjclose(indicators, "NGN", timestamp) == 314.5 indicators = { "adjclose": [{"adjclose": [33.13999938964844]}], @@ -425,7 +427,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2015-12-15")) + timestamp = get_int_timestamp(parse_date("2015-12-15")) assert ( Currency._get_adjclose(indicators, "COP", timestamp) == 3269.199951171875 @@ -442,7 +444,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2022-04-14")) + timestamp = get_int_timestamp(parse_date("2022-04-14")) assert ( Currency._get_adjclose(indicators, "XAF", timestamp) == 605.5509643554688 @@ -459,7 +461,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2022-04-15")) + timestamp = get_int_timestamp(parse_date("2022-04-15")) assert ( Currency._get_adjclose(indicators, "XAF", timestamp) == 601.632568359375 @@ -476,7 +478,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2017-02-15")) + timestamp = get_int_timestamp(parse_date("2017-02-15")) assert Currency._get_adjclose(indicators, "XXX", timestamp) == 3.145 indicators = { "adjclose": [{"adjclose": [33.13999938964844]}], @@ -490,7 +492,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2015-12-15")) + timestamp = get_int_timestamp(parse_date("2015-12-15")) assert Currency._get_adjclose(indicators, "COP", timestamp) == 3320.0 indicators = { "adjclose": [{"adjclose": [33.13999938964844]}], @@ -504,7 +506,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2015-12-15")) + timestamp = get_int_timestamp(parse_date("2015-12-15")) assert ( Currency._get_adjclose(indicators, "COP", timestamp) == 3313.999938964844 @@ -523,7 +525,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2015-12-15")) + timestamp = get_int_timestamp(parse_date("2015-12-15")) assert ( Currency._get_adjclose(indicators, "COP", timestamp) == 33.13999938964844 @@ -542,7 +544,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2015-12-15")) + timestamp = get_int_timestamp(parse_date("2015-12-15")) assert ( Currency._get_adjclose(indicators, "COP", timestamp) == 3124.504838709677 @@ -561,7 +563,7 @@ def test_get_adjclose(self, retrievers, secondary_historic_url): } ], } - timestamp = Currency._get_int_timestamp(parse_date("2015-12-15")) + timestamp = get_int_timestamp(parse_date("2015-12-15")) assert Currency._get_adjclose(indicators, "COP", timestamp) == 3270 Currency._no_historic = True diff --git a/tests/hdx/location/test_wfp_exchangerates.py b/tests/hdx/location/test_wfp_exchangerates.py new file mode 100644 index 0000000..a5e41f0 --- /dev/null +++ b/tests/hdx/location/test_wfp_exchangerates.py @@ -0,0 +1,39 @@ +from os import environ + +import pytest + +from hdx.location import get_int_timestamp +from hdx.location.currency import Currency +from hdx.location.wfp_exchangerates import WFPExchangeRates +from hdx.utilities.dateparse import parse_date + + +class TestWFPExchangeRates: + @pytest.fixture(scope="class") + def wfp_fx(self): + key = environ.get("WFP_KEY") + secret = environ.get("WFP_SECRET") + return WFPExchangeRates(key, secret) + + @pytest.fixture(scope="class") + def currency(self): + return "afn" + + @pytest.fixture(scope="class") + def date(self): + return parse_date("2020-02-20") + + def test_get_currencies(self, wfp_fx): + currencies = wfp_fx.get_currencies() + assert len(currencies) == 126 + + def test_get_historic_rates(self, wfp_fx, currency, date): + assert Currency.get_historic_rate(currency, date) == 76.80000305175781 + timestamp = get_int_timestamp(date) + historic_rates = wfp_fx.get_currency_historic_rates(currency) + assert historic_rates[timestamp] == 77.01 + + def test_get_all_historic_rates(self, wfp_fx, currency, date): + all_historic_rates = wfp_fx.get_historic_rates([currency]) + Currency.setup(historic_rates_cache=all_historic_rates) + assert Currency.get_historic_rate(currency, date) == 77.01