Source code for app.components.providers.forex.forex

# SPDX-License-Identifier: GPLv3-or-later
# Copyright © 2025 pygaindalf Rui Pinheiro

import functools
import datetime
from decimal import Decimal
from iso4217 import Currency

from abc import ABCMeta, abstractmethod
from .. import component_entrypoint, ProviderBase, BaseProviderConfig


# MARK: Provider Base Configuration
[docs] class BaseForexProviderConfig(BaseProviderConfig, metaclass=ABCMeta): pass
# MARK: Provider Base class
[docs] class ForexProviderBase[C : BaseForexProviderConfig](ProviderBase[C], metaclass=ABCMeta): @functools.lru_cache(maxsize=128) @abstractmethod def _get_daily_exchange_rate(self, from_currency: Currency, to_currency: Currency, date: datetime.date) -> Decimal: raise NotImplementedError("This method should be implemented by subclasses.") @classmethod def _validate_currency(cls, currency: Currency | str) -> Currency: if isinstance(currency, Currency): return currency if isinstance(currency, str): return Currency(currency.upper()) raise TypeError(f"Expected Currency or str, got {type(currency).__name__}")
[docs] @component_entrypoint def get_daily_rate(self, from_currency: Currency | str, to_currency: Currency | str, date: datetime.date) -> Decimal: from_currency = self._validate_currency(from_currency) to_currency = self._validate_currency(to_currency ) return self._get_daily_exchange_rate(from_currency, to_currency, date)
[docs] @component_entrypoint def convert_currency(self, amount: Decimal, from_currency: Currency | str, to_currency: Currency | str, date: datetime.date) -> Decimal: from_currency = self._validate_currency(from_currency) to_currency = self._validate_currency(to_currency ) rate = self._get_daily_exchange_rate(from_currency, to_currency, date) return amount * rate