Massive refactoring continues. All the mess I made last night is fixed next night. Program works!
This commit is contained in:
parent
df78739a36
commit
1da27a0271
@ -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"
|
||||
|
@ -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))
|
||||
|
@ -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<reversed>-r|--reversed)? (?P<coins>[\d ,]+)" \
|
||||
r" in (?P<file>\S+)(?= of (?P<currency>\w+))?"
|
||||
argd = match(r, args)
|
||||
r = r"(?P<reversed>-r|--reversed)? ?(?P<coins>[\d ,]+) in (?P<file>\S+)(?= of (?P<currency>\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)
|
||||
|
@ -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"])
|
||||
|
@ -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<reversed>-r|--reversed)?(?P<coins>[\d ,]+) from (?P<file>\S+)"
|
||||
r = r"^(?P<reversed>-r|--reversed)? ?(?P<coins>[\d ,]+) from (?P<file>\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"])
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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'.")
|
||||
|
Loading…
Reference in New Issue
Block a user