diff --git a/piggybank/__init__.py b/piggybank/__init__.py index 569e519..52179fe 100644 --- a/piggybank/__init__.py +++ b/piggybank/__init__.py @@ -1,3 +1,8 @@ +__all__ = ["__date__", "__version__", "__author__", "__email__", + "__copyright__", "__license__", "PIGGYBANK_FILE_EXTENSION", + "print_program_version"] + + __date__ = "4 June 2020" __version__ = "1.0.0" __author__ = "Alexander \"Arav\" Andreev" diff --git a/piggybank/cli/__init__.py b/piggybank/cli/__init__.py index f3a6286..d9a9c05 100644 --- a/piggybank/cli/__init__.py +++ b/piggybank/cli/__init__.py @@ -1,12 +1,13 @@ from re import search from typing import List, Callable -from sys import argv, exit +from sys import argv from piggybank import print_program_version from piggybank.configuration import Configuration from piggybank.currencies import CURRENCIES -__all__ = ["handle_default_arguments", "complement_array_of_coins"] +__all__ = ["handle_default_arguments", "complement_list_of_coins", + "print_supported_currencies", "decimal_to_float"] USAGE_COMMON: str = "Usage: piggybank-* " \ @@ -26,28 +27,31 @@ def parse_common_arguments(args: str): if not argd is None: argd = argd.groupdict() return { - "help": argd["help"] is None, - "version": argd["transactions"] is None, - "list-currencies": argd["list_currencies"] is None, + "help": not argd["help"] is None, + "version": not argd["version"] is None, + "list-currencies": not argd["list_currencies"] is None, "default-currency": argd["default_currency"] } + return None def handle_default_arguments(args: dict, help_func: Callable) -> None: cargs = parse_common_arguments(' '.join(argv)) - - if cargs["help"]: - help_func() - print(USAGE_COMMON) - exit() - elif cargs["version"]: - print_program_version() - exit() - elif cargs["list-currencies"]: - print_supported_currencies() - exit() - elif not cargs["default-currency"] is None: - Configuration()["default-currency"] = cargs["default-currency"] - exit() + if not cargs is None: + if cargs["help"]: + help_func() + print(USAGE_COMMON) + exit() + elif cargs["version"]: + print_program_version() + exit() + elif cargs["list-currencies"]: + print_supported_currencies() + exit() + elif not cargs["default-currency"] is None: + conf = Configuration() + conf["default-currency"] = cargs["default-currency"] + conf.save() + exit() def print_supported_currencies() -> None: @@ -59,8 +63,12 @@ def print_supported_currencies() -> None: print("Default currency is", Configuration()["default-currency"]) -def complement_array_of_coins(coins: List[int], currency: str, +def complement_list_of_coins(coins: List[int], currency: str, _reversed: bool = False) -> List[int]: - """Complements array of coins up to the count of currency's coins.""" + """Complements list of coins up to the count of currency's coins.""" offset_array = [0] * (CURRENCIES[currency]["count"] - len(coins)) return offset_array + coins if not _reversed else coins + offset_array + +def decimal_to_float(lst: List[int]) -> List[float]: + """Converts decimal style of storing money amounts to float.""" + return list(map(lambda x: x / 100, lst)) diff --git a/piggybank/cli/put.py b/piggybank/cli/put.py index ea0a11d..b3c92e2 100644 --- a/piggybank/cli/put.py +++ b/piggybank/cli/put.py @@ -1,11 +1,10 @@ """CLI: Put a set of coins into a piggy bank.""" -from re import match -from sys import argv, exit, stderr +from re import split, search +from sys import argv, stderr -from piggybank import print_program_version from piggybank.configuration import Configuration -from piggybank.cli import complement_array_of_coins, handle_default_arguments +from piggybank.cli import complement_list_of_coins, handle_default_arguments from piggybank.currencies import CURRENCIES, BaseCurrencyError from piggybank.piggybank import PiggyBank @@ -21,34 +20,39 @@ USAGE_PUT = "Usage: piggybank-put [-r|--reversed] COINS in FILE of CURRENCY\n" \ def parse_put_arguments(args): - r = r"^(?P-r|--reversed)? (?P[\d ,]+)" \ - r" in (?P\S+)(?= of (?P\w+))?" - argd = match(r, args) + r = r"(?P-r|--reversed)? ?(?P[\d ,]+) in (?P\S+)(?= of (?P\S+))?" + argd = search(r, args) if not argd is None: argd = argd.groupdict() return { - "coins": list(map(str, argd["coins"].split(", "))), + "coins": list(map(int, split(r"\D",argd["coins"]))), "file": argd["file"], "currency": Configuration()["default-currency"] \ if argd["currency"] is None else argd["currency"], - "reversed": argd["reversed"] is None } + "reversed": not argd["reversed"] is None } + return None def main() -> None: - handle_default_arguments(' '.join(argv), lambda: print(USAGE_PUT)) - - args = parse_put_arguments(' '.join(argv)) + handle_default_arguments(' '.join(argv[1:]), lambda: print(USAGE_PUT)) + args = parse_put_arguments(' '.join(argv[1:])) + + if args is None: + print(USAGE_PUT) + exit() try: try: piggybank = PiggyBank.from_file(args["file"]) except FileNotFoundError: - piggybank = PiggyBank(args["currency"]) - coins = complement_array_of_coins(args["coins"], piggybank.currency, - args["reversed"]) + currency = Configuration()["default-currency"] \ + if args["currency"] is None else args["currency"] + piggybank = PiggyBank(currency) + coins = complement_list_of_coins(args["coins"], piggybank.currency, + args["reversed"]) piggybank.transact(coins) piggybank.save(args["file"]) - except BaseCurrencyError: + except BaseCurrencyError as err: print(f"{type(err).__name__}:", err, file=stderr) except ValueError as err: print(f"{type(err).__name__}:", err, file=stderr) diff --git a/piggybank/cli/show.py b/piggybank/cli/show.py index dd2aae8..64ebf33 100644 --- a/piggybank/cli/show.py +++ b/piggybank/cli/show.py @@ -3,12 +3,10 @@ from re import match from sys import argv, exit, stderr -from piggybank import print_program_version, PIGGYBANK_FILE_EXTENSION -from piggybank.configuration import Configuration from piggybank.cli import handle_default_arguments -from piggybank.currencies import CURRENCIES, \ - BaseCurrencyError +from piggybank.currencies import CURRENCIES, BaseCurrencyError from piggybank.piggybank import PiggyBank +from piggybank.transaction import sum_transactions, multiply_transactions __all__ = ["main"] @@ -28,7 +26,8 @@ def parse_show_arguments(args): argd = argd.groupdict() return { "file": argd["file"], - "transactions": argd["transactions"] is None } + "transactions": not argd["transactions"] is None } + return None def print_summary(piggybank: PiggyBank, @@ -43,24 +42,26 @@ def print_summary(piggybank: PiggyBank, range(CURRENCIES[piggybank.currency]['count'])) print(f"{left}{'━'*27}{lmiddle}{line}{right}") - cc, cs, ct = piggybank.count, piggybank.sum, piggybank.total + cc = sum_transactions(piggybank.transactions) + cs = multiply_transactions(piggybank.transactions, piggybank.currency) + ct = sum(cc) - cline = "┃".join([f'{l:^{centering}}' + nline = "┃".join([f'{l:^{centering}}' for l in CURRENCIES[piggybank.currency]["names"]]) - cline_len = len(cline) + nline_len = len(nline) print_separator(left="┏", lmiddle="┳", rmiddle="━", right="┓") print(f"┃{'currency':^27}┃" - f"{CURRENCIES[piggybank.currency]['name']:^{cline_len}}┃") + f"{CURRENCIES[piggybank.currency]['name']:^{nline_len}}┃") print_separator(rmiddle="┳") - print(f"┃{'face values':^27}┃{cline}┃") + print(f"┃{'face values':^27}┃{nline}┃") print_separator() print(f"┃{'amount':^27}┃{'┃'.join([f'{c:^{centering}}' for c in cc])}┃") print_separator() print(f"┃{'sum':^27}┃" f"{'┃'.join(['{:^{}.2f}'.format(c / 100, centering) for c in cs])}┃") print_separator(rmiddle="┻") - print(f"┃{'total':^27}┃{'{:^{}.2f}'.format(ct / 100, cline_len)}┃") + print(f"┃{'total':^27}┃{'{:^{}.2f}'.format(ct / 100, nline_len)}┃") print_separator(left="┗", lmiddle="┻", rmiddle="━", right="┛") @@ -72,11 +73,11 @@ def print_transactions(piggybank, centering=DEFAULT_COIN_CENTERING): range(CURRENCIES[piggybank.currency]['count'])) print(f"{left}━━━━━━━━━━━━━━━━━━━━━{middle}━━━━━{middle}{line}{right}") - cline = "┃".join([f'{l:^{centering}}' + nline = "┃".join([f'{l:^{centering}}' for l in CURRENCIES[piggybank.currency]["names"]]) print_separator() - print(f"┃{'Timestamp':^21}┃ I/O ┃{cline}┃") + print(f"┃{'Timestamp':^21}┃ I/O ┃{nline}┃") print_separator("┣", "╋", "┫") for tr in piggybank.transactions: coin_line = "┃".join([f'{c:^{centering}}' for c in tr.coins]) @@ -86,9 +87,12 @@ def print_transactions(piggybank, centering=DEFAULT_COIN_CENTERING): def main() -> None: - handle_default_arguments(' '.join(argv), lambda: print(USAGE_SHOW)) + handle_default_arguments(' '.join(argv[1:]), lambda: print(USAGE_SHOW)) + args = parse_show_arguments(' '.join(argv[1:])) - args = parse_show_arguments(' '.join(argv)) + if args is None: + print(USAGE_SHOW) + exit() try: piggybank = PiggyBank.from_file(args["file"]) diff --git a/piggybank/cli/take.py b/piggybank/cli/take.py index 1db9211..65905e3 100644 --- a/piggybank/cli/take.py +++ b/piggybank/cli/take.py @@ -1,11 +1,11 @@ """CLI: Take a set of coins from a coin box.""" -from re import match, search +from re import match, split from sys import argv, exit, stderr from piggybank import print_program_version from piggybank.configuration import Configuration -from piggybank.cli import complement_array_of_coins, handle_default_arguments +from piggybank.cli import complement_list_of_coins, handle_default_arguments from piggybank.currencies import CURRENCIES, BaseCurrencyError from piggybank.piggybank import PiggyBank from piggybank.transaction import TYPE_OUTCOME @@ -21,28 +21,28 @@ USAGE_TAKE: str = "Usage: piggybank-take [-r|--reversed] COINS from FILE\n\n" \ def parse_take_arguments(args): - r = r"^(?P-r|--reversed)?(?P[\d ,]+) from (?P\S+)" + r = r"^(?P-r|--reversed)? ?(?P[\d ,]+) from (?P\S+)" argd = match(r, args) if not argd is None: argd = argd.groupdict() return { - "coins": list(map(str, argd["coins"].split(", "))), + "coins": list(map(int, split(r"\D", argd["coins"]))), "file": argd["file"], - "reversed": argd["reversed"] is None } + "reversed": not argd["reversed"] is None } + return None def main(): - handle_default_arguments(' '.join(argv), lambda: print(USAGE_TAKE)) + handle_default_arguments(' '.join(argv[1:]), lambda: print(USAGE_TAKE)) + args = parse_take_arguments(' '.join(argv[1:])) - args = parse_take_arguments(' '.join(argv)) - - if args["coins"] is None or args["file"] is None: + if args is None: print(USAGE_TAKE) exit() try: piggybank = PiggyBank.from_file(args["file"]) - coins = complement_array_of_coins(args["coins"], piggybank.currency, + coins = complement_list_of_coins(args["coins"], piggybank.currency, args["reversed"]) piggybank.transact(coins, TYPE_OUTCOME) piggybank.save(args["file"]) diff --git a/piggybank/configuration.py b/piggybank/configuration.py index 92fa7f6..f2046c2 100644 --- a/piggybank/configuration.py +++ b/piggybank/configuration.py @@ -8,12 +8,6 @@ from typing import Union __all__ = ["Configuration", "get_configuration_path"] -DEFAULT_CONFIGURATION = { - "default-currency": "SRUB" -} -DEFAULT_CONFIGURATION_FILE = join(get_configuration_path(), "piggybank.conf") - - def get_configuration_path(): if system() == "Linux": return getenv("XDG_CONFIG_HOME") or f"{getenv('HOME')}/.config" @@ -21,12 +15,17 @@ def get_configuration_path(): return getenv("APPDATA") +DEFAULT_CONFIGURATION = { + "default-currency": "SRUB" +} +DEFAULT_CONFIGURATION_FILE = join(get_configuration_path(), "piggybank.conf") + + class Configuration: def __init__(self, configuration_file: str = DEFAULT_CONFIGURATION_FILE, default_configuration: dict = DEFAULT_CONFIGURATION) -> None: self._configuration_file = configuration_file self._configuration = dict() - if exists(self._configuration_file): self.load() elif not default_configuration is None: @@ -35,7 +34,7 @@ class Configuration: def load(self) -> None: for line in open(self._configuration_file, 'r'): - key, value = line.split(" = ") + key, value = line[:-1].split(" = ") self._configuration[key] = value def save(self) -> None: diff --git a/piggybank/currencies.py b/piggybank/currencies.py index 32c4447..b7b9df5 100644 --- a/piggybank/currencies.py +++ b/piggybank/currencies.py @@ -22,7 +22,7 @@ this number by 100 to get a regular floating-point number. from typing import Dict, List, TypedDict __all__ = ["CURRENCIES", "BaseCurrencyError", "CurrencyIsNotSupportedError", - "CurrenciesCoinCountMismatchError", "CurrencyMismatchError"] + "CurrenciesCoinCountMismatchError", "CurrencyMismatchError"] class BaseCurrencyError(Exception): diff --git a/piggybank/piggybank.py b/piggybank/piggybank.py index 517a678..3f1ee88 100644 --- a/piggybank/piggybank.py +++ b/piggybank/piggybank.py @@ -5,8 +5,7 @@ from os.path import exists from typing import List from piggybank import PIGGYBANK_FILE_EXTENSION -from piggybank.currencies import CURRENCIES, \ - CurrencyIsNotSupportedError, CurrenciesCoinCountMismatchError, \ +from piggybank.currencies import CURRENCIES, CurrencyIsNotSupportedError, \ CurrencyMismatchError from piggybank.transaction import Transaction, sum_transactions, TYPE_INCOME @@ -16,6 +15,7 @@ __all__ = ["PiggyBank"] class PiggyBank: """This class stores array of transactions and perform some actions on it.""" def __init__(self, currency: str = None) -> None: + self._currency = None if not currency is None: self.currency = currency self._transactions = [] @@ -42,16 +42,11 @@ class PiggyBank: return self._currency @currency.setter - def currency(self, currency: str = None) -> 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.""" + def currency(self, currency: str) -> None: + """Sets a currency of a PiggyBank with check for support.""" currency = currency.upper() - if not currency in CURRENCIES: + if not currency in CURRENCIES.keys(): raise CurrencyIsNotSupportedError - if not self._currency is None and \ - CURRENCIES[currency]["count"] \ - != CURRENCIES[self._currency]["count"]: - raise CurrenciesCoinCountMismatchError self._currency = currency @property diff --git a/piggybank/transaction.py b/piggybank/transaction.py index b0aa180..cfc3b28 100644 --- a/piggybank/transaction.py +++ b/piggybank/transaction.py @@ -10,6 +10,7 @@ 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" @@ -21,9 +22,9 @@ def sum_transactions(transactions: List[Transaction]) -> List[int]: coins = [0] * len(transactions[0].coins) for transaction in transactions: if transaction.direction == TYPE_INCOME: - coins = list(map(add, transaction.coins, coins)) + coins = list(map(add, coins, transaction.coins)) else: - coins = list(map(sub, transaction.coins, coins)) + coins = list(map(sub, coins, transaction.coins)) return coins def multiply_transactions(transactions: Union[List[Transaction], Transaction], @@ -57,7 +58,7 @@ class Transaction: @coins.setter def coins(self, coins: List[int]) -> None: - if coins is list: + if type(coins) is list: self._coins = coins else: raise TypeError("Coins must be of type 'list'.")