2020-03-27 20:14:22 +04:00
|
|
|
"""PiggyBank implementation."""
|
2019-12-25 23:08:20 +04:00
|
|
|
|
|
|
|
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:
|
2020-03-27 20:14:22 +04:00
|
|
|
"""This class stores array of transactions and perform some actions on it."""
|
2019-12-25 23:08:20 +04:00
|
|
|
def __init__(self, currency: str = DEFAULT_CURRENCY) -> None:
|
|
|
|
if currency.upper() in CURRENCIES:
|
2019-12-27 12:54:58 +04:00
|
|
|
self._currency = currency.upper()
|
2019-12-25 23:08:20 +04:00
|
|
|
else:
|
|
|
|
raise CurrencyIsNotSupportedError
|
|
|
|
|
2019-12-27 12:54:58 +04:00
|
|
|
self._transactions = []
|
2019-12-25 23:08:20 +04:00
|
|
|
|
|
|
|
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 "
|
2020-03-27 20:14:22 +04:00
|
|
|
f"currency's coins count. ({len(coins)} "
|
2019-12-25 23:08:20 +04:00
|
|
|
f"!= {CURRENCIES[self.currency]['count']})")
|
|
|
|
|
2019-12-27 12:54:58 +04:00
|
|
|
self._transactions.append(Transaction(coins, direction))
|
2019-12-25 23:08:20 +04:00
|
|
|
|
|
|
|
for coin_count in self.count:
|
|
|
|
if coin_count < 0:
|
2019-12-27 12:54:58 +04:00
|
|
|
del self._transactions[-1]
|
2019-12-25 23:08:20 +04:00
|
|
|
raise ValueError(
|
|
|
|
"You can't take out more than you have.")
|
|
|
|
|
|
|
|
@property
|
|
|
|
def count(self) -> List[int]:
|
2020-03-27 20:14:22 +04:00
|
|
|
"""Returns a list of counts for each face value in total."""
|
2019-12-25 23:08:20 +04:00
|
|
|
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:
|
2019-12-27 12:54:58 +04:00
|
|
|
self._transactions.append(Transaction.from_string(transaction))
|
2019-12-25 23:08:20 +04:00
|
|
|
|
|
|
|
@property
|
|
|
|
def currency(self) -> str:
|
|
|
|
"""Returns a currency of a PiggyBank."""
|
2019-12-27 12:54:58 +04:00
|
|
|
return self._currency
|
2019-12-25 23:08:20 +04:00
|
|
|
|
|
|
|
@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
|
2019-12-27 12:54:58 +04:00
|
|
|
self._currency = currency
|
2019-12-25 23:08:20 +04:00
|
|
|
|
|
|
|
@property
|
|
|
|
def transactions(self) -> List[Transaction]:
|
|
|
|
"""Returns a list of transactions."""
|
2019-12-27 12:54:58 +04:00
|
|
|
return self._transactions
|
2019-12-25 23:08:20 +04:00
|
|
|
|
|
|
|
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
|