Source code for finalynx.fetch.fetch_line

"""This file defines elements used to match `Line` instances defined in the main portfolio
tree with investments fetched online (e.g. from your Finary account).
"""
from dataclasses import dataclass
from typing import Any
from typing import Dict
from typing import Optional

from finalynx.config import DEFAULT_CURRENCY
from finalynx.portfolio.line import Line


[docs]@dataclass class FetchAttribs: """Abstract class that defines common attributes used to match Keys (defined in the portfolio) with FetchLines (created by fetch agents, e.g. `FinaryFetch`).""" name: Optional[str] = None id: Optional[str] = None account: Optional[str] = None custom: Optional[Dict[str, Any]] = None
[docs] def __post_init__(self) -> None: """Runs after an instance is created to validate inputs. At least one field must be set.""" if not (self.name or self.id or self.account or self.custom): raise ValueError(f"{type(self).__name__} instance must have at least one field set.")
[docs] def to_dict(self) -> Dict[str, Any]: return self.__dict__
[docs] @staticmethod def from_dict(dict: Dict[str, Any]) -> "FetchAttribs": raise NotImplementedError("Abstract method must be overridden by subclasses.")
[docs]@dataclass class FetchLine(FetchAttribs): """Represents each investment found in your online accounts (e.g. Finary). The instance is populated with information found online about this line. FetchLines will then be matched to Keys defined in the portfolio to populate 'real' `Line` instances with FetchLine information.""" amount: float = 0 currency: Optional[str] = None
[docs] def matches_line(self, line: Line) -> bool: # TODO improve? # See if the line's name or key fields match matched_name = bool((line.key and line.key in [self.name, self.id, self.account]) or line.name == self.name) # If the envelope is set, it must also match if line.envelope: return matched_name and (line.envelope.name == self.account or line.envelope.key == self.account) # If there's no envelope set, only the name/key counts return matched_name
[docs] def update_line(self, line: Line) -> None: """Update a `Line` instance, usually matched against a FetchKe, in the portfolio, with data fetched online.""" if not line.amount: line.amount = self.amount if not line.currency: line.currency = self.currency if self.currency else DEFAULT_CURRENCY
[docs] def generate_line(self) -> Line: """Generate a basic Line instance from this abstract fetched line. Used when filling folders based on account filters.""" name = self.name if self.name else "Unknown" return Line(name, key=self.id, amount=self.amount, currency=self.currency)
[docs] def to_dict(self) -> Dict[str, Any]: return self.__dict__
[docs] @staticmethod def from_dict(dict: Dict[str, Any]) -> "FetchLine": return FetchLine( name=dict["name"], id=dict["id"], account=dict["account"], custom=dict["custom"], amount=dict["amount"], currency=dict["currency"], )