import datetime import os from abc import ABC, abstractmethod from decimal import Decimal from money.currency import Currency, CurrencyHelper from money.money import Money from xecd_rates_client import XecdClient from xexe.exchange_rates import ExchangeRate class RateFetcher(ABC): is_production_grade = False @abstractmethod def fetch_rate( self, from_currency: Currency, to_currency: Currency, rate_date: datetime.date ) -> ExchangeRate: pass class MockRateFetcher(RateFetcher): is_production_grade = False def __init__(self) -> None: super().__init__() def fetch_rate( self, from_currency: Currency, to_currency: Currency, rate_date: datetime.date ) -> ExchangeRate: return ExchangeRate( from_currency=from_currency, to_currency=to_currency, rate=Money(42, to_currency), rate_date=rate_date, ) class XERateFetcher(RateFetcher): is_production_grade = True def __init__(self) -> None: super().__init__() self.xe_client = XecdClient( account_id=os.environ["XE_ACCOUNT_ID"], api_key=os.environ["XE_API_KEY"], ) def fetch_rate( self, from_currency: Currency, to_currency: Currency, rate_date: datetime.date ) -> ExchangeRate: response = self.xe_client.historic_rate( rate_date.strftime("%Y-%m-%d"), "12:00", from_currency.value, # .value will access the ISO 4217 str code of the currency to_currency.value, 1, # always 1, because the rate we want is how much of to_curr does # 1 unit of from_curr get us. ) from_currency = Currency(response["from"]) to_currency = Currency(response["to"][0]["quotecurrency"]) rate_date = datetime.datetime.fromisoformat( response["timestamp"].replace( "Z", "+00:00" ) # Funny replace is necessary because of API response format ).date() rate = f"""{response["to"][0]["mid"]:.{CurrencyHelper.decimal_precision_for_currency(to_currency)}f}""" return ExchangeRate( from_currency=from_currency, to_currency=to_currency, rate_date=rate_date, rate=rate, )