"""Implementation of the piggy bank itself.""" from __future__ import annotations from os.path import exists from typing import List from piggybank import PIGGYBANK_FILE_EXTENSION from piggybank.currencies import CURRENCIES, DEFAULT_CURRENCY, \ CurrencyIsNotSupportedError, CurrenciesCoinCountMismatchError, \ CurrencyMismatchError from piggybank.transaction import Transaction, TYPE_INCOME __all__ = ["PiggyBank"] class PiggyBank: """This class stores transactions and do file I/O on piggy bank .pb files.""" def __init__(self, currency: str = DEFAULT_CURRENCY) -> None: if currency.upper() in CURRENCIES: self._currency = currency.upper() else: raise CurrencyIsNotSupportedError self._transactions = [] def transact(self, coins: List[int], direction: str = TYPE_INCOME) -> None: """Make a transaction.""" if len(coins) != CURRENCIES[self.currency]["count"]: raise ValueError("Length of passed coins list doesn't match the " f"currency's coins count ({len(coins)} " f"!= {CURRENCIES[self.currency]['count']})") self._transactions.append(Transaction(coins, direction)) for coin_count in self.count: if coin_count < 0: del self._transactions[-1] raise ValueError( "You can't take out more than you have.") @property def count(self) -> List[int]: """Returns a list of counts for each face value.""" count = [0] * CURRENCIES[self.currency]["count"] for tr in self.transactions: count = [x + y if tr.direction == TYPE_INCOME else x - y for x, y in zip(count, tr.coins)] return count @property def sum(self) -> List[int]: """Returns a list of sums for each face value multiplied by its currency's multipilers.""" return [x * y for x, y in zip(self.count, CURRENCIES[self.currency]["multipliers"])] @property def total(self) -> int: """Returns a total amount of money stored in a PiggyBank.""" return sum(self.sum) @staticmethod def from_file(filename: str) -> PiggyBank: """Returns a PiggyBank object loaded from a file with name `filename`.""" piggybank = PiggyBank() piggybank.load(filename) return piggybank def save(self, filename: str) -> None: """Writes a PiggyBank object to a file with name `filename`.""" if not filename.endswith(PIGGYBANK_FILE_EXTENSION): filename += PIGGYBANK_FILE_EXTENSION with open(filename, 'w') as _file: _file.write(f"{self.currency}\n") for transaction in self.transactions: _file.write(f"{str(transaction)}\n") def load(self, filename: str) -> None: """Loads a PiggyBank from a file with name `filename`.""" if not filename.endswith(PIGGYBANK_FILE_EXTENSION): filename += PIGGYBANK_FILE_EXTENSION if not exists(filename): raise FileNotFoundError(filename) with open(filename, 'r') as _file: currency = _file.readline()[:-1] if currency not in CURRENCIES: raise CurrencyIsNotSupportedError else: self.currency = currency for transaction in _file: self._transactions.append(Transaction.from_string(transaction)) @property def currency(self) -> str: """Returns a currency of a PiggyBank.""" return self._currency @currency.setter def currency(self, currency: str) -> None: """Sets a currency of a PiggyBank with check for support. And if count of coins doesn't match the old currency it won't set a new one.""" currency = currency.upper() if currency not in CURRENCIES: raise CurrencyIsNotSupportedError if CURRENCIES[currency]["count"] != CURRENCIES[self.currency]["count"]: raise CurrenciesCoinCountMismatchError self._currency = currency @property def transactions(self) -> List[Transaction]: """Returns a list of transactions.""" return self._transactions def __eq__(self, piggybank: PiggyBank) -> str: """It compares only currency.""" return self.currency == piggybank._currency def __add__(self, piggybank: PiggyBank) -> PiggyBank: if self != piggybank: raise CurrencyMismatchError new = PiggyBank(self.currency) new._transactions = self.transactions + piggybank._transactions return new def __repr__(self) -> str: return f"PiggyBank(currency={self.currency!r})"