From d382c332e3ab780c6146f7df5ddf539dfeca31c9 Mon Sep 17 00:00:00 2001 From: Pablo Martin Date: Wed, 12 Jun 2024 00:44:30 +0200 Subject: [PATCH] more methods and tests --- tests/tests_unit/test_exchange_rates.py | 97 ++++++++++++++++++++++++- xexe/exchange_rates.py | 60 ++++++++++++--- 2 files changed, 145 insertions(+), 12 deletions(-) diff --git a/tests/tests_unit/test_exchange_rates.py b/tests/tests_unit/test_exchange_rates.py index c10566a..b5080af 100644 --- a/tests/tests_unit/test_exchange_rates.py +++ b/tests/tests_unit/test_exchange_rates.py @@ -4,7 +4,12 @@ from decimal import Decimal from money.currency import Currency from money.money import Money -from xexe.exchange_rates import ExchangeRate, ExchangeRates, add_equal_rates +from xexe.exchange_rates import ( + ExchangeRate, + ExchangeRates, + add_equal_rates, + add_inverse_rates, +) def test_exchange_rate_creation_works(): @@ -124,6 +129,26 @@ def test_present_dates_works_fine(): } +def test_exchange_rates_is_rate_present_method_works_fine(): + a_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.EUR, + rate=Decimal("1.2"), + rate_date=datetime.date(2020, 3, 10), + ) + another_rate = ExchangeRate( + from_currency=Currency.GBP, + to_currency=Currency.JPY, + rate=Decimal("10"), + rate_date=datetime.date(2019, 10, 3), + ) + + rates = ExchangeRates({a_rate}) + + assert rates.is_rate_present(a_rate) + assert not rates.is_rate_present(another_rate) + + def test_add_equal_rates_returns_expected_values(): a_rate = ExchangeRate( from_currency=Currency.USD, @@ -170,3 +195,73 @@ def test_add_equal_rates_runs_on_empty_rates(): empty_rates = add_equal_rates(empty_rates) assert True + + +def test_add_equal_rates_overwrites(): + a_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.USD, + rate=Decimal("1.2"), + rate_date=datetime.date(2020, 3, 10), + ) + + rates = ExchangeRates([a_rate]) + + assert len(rates) == 1 + + rates = add_equal_rates(rates, overwrite=True) + + assert len(rates) == 1 + assert list(rates)[0].amount == 1 + + +def test_add_equal_rates_does_not_overwrite(): + a_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.USD, + rate=Decimal("1.2"), + rate_date=datetime.date(2020, 3, 10), + ) + + rates = ExchangeRates([a_rate]) + + assert len(rates) == 1 + + rates = add_equal_rates(rates, overwrite=False) + + assert len(rates) == 1 + assert list(rates)[0].amount == Decimal("1.2") + + +def test_add_inverse_rates_returns_expected(): + a_rate = ExchangeRate( + from_currency=Currency.EUR, + to_currency=Currency.USD, + rate=Decimal("1.25"), + rate_date=datetime.date(2020, 3, 10), + ) + inverse_rate = ExchangeRate( + from_currency=Currency.USD, + to_currency=Currency.EUR, + rate=Decimal("0.8"), + rate_date=datetime.date(2020, 3, 10), + ) + + rates = ExchangeRates([a_rate]) + + assert len(rates) == 1 + + rates = add_inverse_rates(rates) + + assert len(rates) == 2 + + assert rates.is_rate_present(inverse_rate) + assert rates[inverse_rate.descriptor].amount == Decimal("0.8") + + +def test_add_inverse_rates_runs_on_empty_rates(): + empty_rates = ExchangeRates() + + empty_rates = add_inverse_rates(empty_rates) + + assert True diff --git a/xexe/exchange_rates.py b/xexe/exchange_rates.py index 6276016..209acf7 100644 --- a/xexe/exchange_rates.py +++ b/xexe/exchange_rates.py @@ -3,7 +3,7 @@ from decimal import Decimal from numbers import Number from typing import Iterable, Set, Union -from money.currency import Currency +from money.currency import Currency, CurrencyHelper from money.money import Money @@ -13,7 +13,7 @@ class ExchangeRate: self, from_currency: Currency, to_currency: Currency, - rate: Union[Money, Number], + rate: Union[Money, Number, str], rate_date: datetime.date, ) -> None: self.from_currency = from_currency @@ -66,26 +66,64 @@ class ExchangeRates: self._rate_index[new_rate.descriptor] = new_rate def __iter__(self): - return iter(self._rate_index.values()) + return iter(list(self._rate_index.values())) def __len__(self): return len(self._rate_index) + def __contains__(self, rate) -> bool: + if not isinstance(rate, ExchangeRate): + raise TypeError("ExchangeRates can only hold Rates.") -def add_equal_rates(rates: ExchangeRates) -> ExchangeRates: + if rate.descriptor in self._rate_index: + return True + + return False + + def is_rate_present(self, rate: ExchangeRate) -> bool: + if rate.descriptor in self._rate_index: + return True + + return False + + def __getitem__(self, rate_descriptor) -> ExchangeRate: + return self._rate_index[rate_descriptor] + + +def add_equal_rates(rates: ExchangeRates, overwrite: bool = False) -> ExchangeRates: present_currencies = rates.present_currencies present_dates = rates.present_dates for date in present_dates: for currency in present_currencies: - rates.add_rate( - ExchangeRate( - from_currency=currency, - to_currency=currency, - rate=Money(1, currency), - rate_date=date, - ) + new_rate = ExchangeRate( + from_currency=currency, + to_currency=currency, + rate=Money(1, currency), + rate_date=date, ) + if new_rate in rates and not overwrite: + continue + rates.add_rate(new_rate) + + return rates + + +def add_inverse_rates(rates: ExchangeRates) -> ExchangeRates: + + # Hey, I haven't thought properly what happens here if the inverse rate is + # *already* present in the rates set. It's probably going to be fucky. I + # would advise only running this against sets where you don't have inverse + # rates already present. + + for rate in rates: + inverse_rate = ExchangeRate( + from_currency=rate.to_currency, + to_currency=rate.from_currency, + rate_date=rate.rate_date, + rate=f"{1 / rate.amount:.{CurrencyHelper.decimal_precision_for_currency(rate.from_currency)}f}", + ) + rates.add_rate(inverse_rate) return rates