"""Transaction class implementation.""" from __future__ import annotations from operator import add, sub, mul from time import strftime, strptime, gmtime from typing import Optional, List, Union from piggybank.currencies import CURRENCIES __all__ = ["Transaction", "sum_transactions", "multiply_transactions", "TYPE_INCOME", "TYPE_OUTCOME", "TIME_FORMAT"] TYPE_INCOME = "i" TYPE_OUTCOME = "o" TIME_FORMAT = "%Y-%m-%dT%H:%M:%S" def sum_transactions(transactions: List[Transaction]) -> List[int]: """Sums all the coins and returns resulting list. All transactions must have same length of coins' list.""" coins = [0] * len(transactions[0].coins) for transaction in transactions: if transaction.direction == TYPE_INCOME: coins = list(map(add, coins, transaction.coins)) else: coins = list(map(sub, coins, transaction.coins)) return coins def multiply_transactions(transactions: Union[List[Transaction], Transaction], currency: str) -> List[int]: """Multiplies coins' counts by currency.""" if type(transactions) is Transaction: return list(map(mul, transactions.coins, \ CURRENCIES[currency]["multipliers"])) else: coins = sum_transactions(transactions) return list(map(mul, coins, CURRENCIES[currency]["multipliers"])) class Transaction: """An object that holds a single transaction. Arguments: - coins -- a list of numbers represent count for each face value; - direction -- is this income or outcome. Takes TYPE_INCOME or TYPE_OUTCOME; - timestamp -- date and time formated accordingly to TIME_FORMAT.""" def __init__(self, coins: List[int], direction: str = TYPE_INCOME, timestamp: Optional[str] = None) -> None: self.coins = coins self.direction = direction self.timestamp = timestamp @property def coins(self) -> List[int]: return self._coins @coins.setter def coins(self, coins: List[int]) -> None: if type(coins) is list: self._coins = coins else: raise TypeError("Coins must be of type 'list'.") @property def direction(self) -> str: return self._direction @direction.setter def direction(self, direction: str = TYPE_INCOME) -> None: if direction in [TYPE_INCOME, TYPE_OUTCOME]: self._direction = direction else: raise ValueError("Direction may only be of TYPE_INCOME(\"i\") " \ "or TYPE_OUTCOME(\"o\").") @property def timestamp(self) -> str: return self._timestamp @timestamp.setter def timestamp(self, timestamp: str = None) -> None: if timestamp is None: self._timestamp = strftime(TIME_FORMAT, gmtime()) else: try: strptime(timestamp, TIME_FORMAT) except ValueError: raise ValueError(f"Timestamp {timestamp} has wrong format. " \ f"The right one is \"{TIME_FORMAT}\".") self._timestamp = timestamp @staticmethod def from_string(transaction: str) -> Transaction: """Makes a Transaction object from its string output.""" timestamp, direction, coins = transaction.split() coins = list(map(int, coins.split(","))) return Transaction(coins, direction, timestamp) def __str__(self) -> str: return f"{self.timestamp} {self.direction} " \ f"{','.join(map(str, self.coins))}"