1
0
fin-depo/fin_depo/data.py
Jon Michael Aanes 7a5ebae6ff
All checks were successful
Test Python / Test (push) Successful in 27s
Code quality
2024-07-19 01:25:14 +02:00

95 lines
2.5 KiB
Python

import abc
import dataclasses
import datetime
from collections.abc import Iterable, Mapping
from decimal import Decimal
from typing import TypeVar
import enforce_typing
from fin_defs import Asset
@enforce_typing.enforce_types
@dataclasses.dataclass
class Depo(abc.ABC):
"""A depository tracking some amount of assets.
Depo can either be DepoSingle, which is the base layer of the depository
structure, or nested in DepoGroup, which allows for a complex hierarcy of
depositories.
The depository structure exposed by each backend depends upon the logical
structure of the relevant service and the API of this service.
"""
name: str
updated_time: datetime.datetime
@abc.abstractmethod
def assets(self) -> Iterable[Asset]:
"""Returns the different assets managed by this depo."""
@abc.abstractmethod
def get_amount_of_asset(self, asset: Asset) -> Decimal:
"""Returns the amount of owned assets for all nested depos.
Must return 0 if depo does not contain the given asset.
"""
@enforce_typing.enforce_types
@dataclasses.dataclass
class DepoSingle(Depo):
"""Base level of depository."""
_assets: Mapping[Asset, Decimal]
def assets(self) -> Iterable[Asset]:
return self._assets
def get_amount_of_asset(self, asset: Asset) -> Decimal:
return self._assets.get(asset, Decimal(0))
@enforce_typing.enforce_types
@dataclasses.dataclass
class DepoGroup(Depo):
"""Nested depository."""
nested: list[Depo]
def assets(self) -> Iterable[Asset]:
assets: set[Asset] = set()
for nested_depo in self.nested:
assets.update(nested_depo.assets())
return assets
def get_amount_of_asset(self, asset: Asset) -> Decimal:
summed: Decimal = Decimal(0)
for nested_depo in self.nested:
summed += nested_depo.get_amount_of_asset(asset)
del nested_depo
return summed
T = TypeVar('T')
class DepoFetcher(abc.ABC):
"""Base `Depo` fetcher interface.
Used to get depository information for specific websites/backends.
"""
@abc.abstractmethod
def get_depo(self) -> Depo:
"""Fetches the `Depo`s available for the fetcher, possibly as
a `DepoGroup`.
"""
def assert_param(self, param_name: str, param_type: type[T], param_value: T) -> T:
if not isinstance(param_value, param_type):
msg = f'{self} expected {param_name} parameter of type {param_type}, but got: {param_value}'
raise TypeError(msg)
return param_value