diff --git a/README.md b/README.md index 7e47066..c9bb7bd 100644 --- a/README.md +++ b/README.md @@ -7,26 +7,12 @@ you decide to take them all out. Yes, only coins, no banknots. Well, you actually can easily add support for banknots, but table will become indeed wide. -# Backstory - -Many years ago I wrote a little script to store info on how much coins I have -in my piggy bank. It used SQLite 3 database for it and was hardcoded to rubles. -Why had I use whole SQL database? That was the first time I tried SQLite in -Python. - -Once I came up with an idea that it would be good to learn on how to make -packages for Python. And with no better ideas I took my piggy bank script to be -rewritten to support any currency. Database was thrown away and its place was -took by a simple text format. - # Usage -This is a CLI program that is broken down to three separate programs: - piggybank [OPTIONS] (COINS in FILE [of CURRENCY] | COINS from FILE | show FILE [with t(ransactions)]) -Options: +Common options: - `-h,--help` — print this help; @@ -120,6 +106,6 @@ Currency is defined following way: That is best described with an example. Here it is: - currency.SRUB = SRUB;Russian ruble (shortened);Russian Federation. Excluding coins of 1 and 5 kopek;6;10к.,50к.,1₽,2₽,5₽,10₽;10,50,100,200,500,1000 + currency.EXM = EXM;Example;Example currency;2;1E,2E;100,200 Yes, long and clunky way... May be I'll come up with something better. diff --git a/piggybank/__init__.py b/piggybank/__init__.py index d6d8a04..d2abe28 100644 --- a/piggybank/__init__.py +++ b/piggybank/__init__.py @@ -1,4 +1,4 @@ -__all__ = ["__date__", "__version__", "__author__", "__author_email__", +__all__ = ["__date__", "__version__", "__author__", "__email__", "__copyright__", "__license__", "PIGGYBANK_FILE_EXTENSION", "VERSION"] @@ -6,8 +6,8 @@ __all__ = ["__date__", "__version__", "__author__", "__author_email__", __date__ = "7 June 2020" __version__ = "1.0.0" __author__ = "Alexander \"Arav\" Andreev" -__author_email__ = "me@arav.top" -__copyright__ = f"Copyright (c) 2020 {__author__} <{__author_email__}>" +__email__ = "me@arav.top" +__copyright__ = f"Copyright (c) 2020 {__author__} <{__email__}>" __license__ = \ """This program is free software. It comes without any warranty, to the extent permitted by applicable law. You can redistribute it diff --git a/piggybank/cli/__init__.py b/piggybank/cli/__init__.py index 92813c2..e69de29 100644 --- a/piggybank/cli/__init__.py +++ b/piggybank/cli/__init__.py @@ -1,12 +0,0 @@ -from typing import List - -from piggybank.currencies import CURRENCIES - -__all__ = ["complement_list_of_coins"] - - -def complement_list_of_coins(coins: List[int], currency: str, - _reversed: bool = False) -> List[int]: - """Complements list of coins up to the count of currency's coins.""" - offset_array = [0] * (len(CURRENCIES[currency]) - len(coins)) - return offset_array + coins if not _reversed else coins + offset_array diff --git a/piggybank/cli/main.py b/piggybank/cli/main.py index c9b5144..96f83e0 100644 --- a/piggybank/cli/main.py +++ b/piggybank/cli/main.py @@ -2,9 +2,9 @@ from re import search, split from sys import argv, stderr +from typing import List from piggybank import VERSION -from piggybank.cli import complement_list_of_coins from piggybank.configuration import Configuration from piggybank.currencies import Currency, BaseCurrencyError, CURRENCIES from piggybank.piggybank import PiggyBank @@ -34,8 +34,8 @@ Show: piggybank show FILE [with t(ransactions)] Arguments: \tCOINS -- a set of comma or whitespace separated coin counts; -\tFILE -- a filename of a piggybank; -\tof CURRENCY -- set a partcular currency for a piggybank; +\tFILE -- a filename of a piggy bank; +\tof CURRENCY -- set a partcular currency for a piggy bank; \twith t(ransactions) -- print a table of transactions as well. """ @@ -99,11 +99,11 @@ def print_summary(pb: PiggyBank) -> None: s = sum_transactions(pb.transactions) print(f"┃{'Counts':^27}┃{'┃'.join(f'{c:^12}' for c in s)}┃") print_separator("┣", "╋", "╋", "┫") - s = cur.multiply(s) - print(f"┃{'Sums':^27}┃{'┃'.join(f'{c/100:^12.2f}' for c in s)}┃") + s = cur * s + print(f"┃{'Sums in currency':^27}┃{'┃'.join(f'{c/100:^12.2f}' for c in s)}┃") print_separator("┣", "╋", "┻", "┫") s = sum(s) - print(f"┃{'Total':^27}┃{s:^{12*cur.count+cur.count-1}}┃") + print(f"┃{'Total in currency':^27}┃{s/100:^{12*cur.count+cur.count-1}.2f}┃") print_separator("┗", "┻", "━", "┛") @@ -115,12 +115,18 @@ def print_supported_currencies() -> None: f" ┃ {CURRENCIES[cur].description}") print("Default currency is", Configuration()["default-currency"]) - -def load_user_defined_currencies() -> None: +def load_currencies() -> None: + """Load currencies defined in a configuration file.""" if not "currency" in Configuration().items: return for iso, cur in Configuration()["currency"].items(): - CURRENCIES[iso] = Currency.from_string(cur) + CURRENCIES[iso.upper()] = Currency.from_string(cur) + +def complement_list_of_coins(coins: List[int], currency: str, + _reversed: bool = False) -> List[int]: + """Complements list of coins up to the count of currency's coins.""" + offset_array = [0] * (len(CURRENCIES[currency]) - len(coins)) + return offset_array + coins if not _reversed else coins + offset_array def main(): @@ -133,6 +139,7 @@ def main(): print(VERSION) exit() elif cargs["list-currencies"]: + load_currencies() print_supported_currencies() exit() elif not cargs["default-currency"] is None: @@ -147,7 +154,7 @@ def main(): exit() try: - load_user_defined_currencies() + load_currencies() try: pb = PiggyBank.from_file(args["file"]) except FileNotFoundError: diff --git a/piggybank/configuration.py b/piggybank/configuration.py index 6ea65ff..0738ec2 100644 --- a/piggybank/configuration.py +++ b/piggybank/configuration.py @@ -27,7 +27,22 @@ def get_configuration_path() -> str: DEFAULT_CONFIGURATION = { - "default-currency": "SRUB" } + "default-currency": "SRUB", + "currency": { + "RUB": "RUB;Russian ruble;Russian Federation;8;" \ + "1к,5к,10к,50к,1₽,2₽,5₽,10₽;1,5,10,50,100,200,500,1000", + "SRUB": "SRUB;Russian ruble (short);No 1 and 5 kopek;6;" \ + "10к,50к,1₽,2₽,5₽,10₽;10,50,100,200,500,1000", + "BYN": "BYN;Belarusian ruble;Belarus;8;" \ + "1к,2к,5к,10к,20к,50к,1р,2р;1,2,5,10,20,50,100,200", + "UAH": "UAH;Ukrainian hryvnia;Ukraine;10;" \ + "1к,2к,5к,10к,25к,50к,₴1,₴2,₴5,₴10;1,2,5,10,25,50,100,200,500,1000", + "USD": "USD;US Dollar;United States of America;6;" \ + "1¢,5¢,10¢,25¢,50¢,$1;1,5,10,25,50,100", + "EUR": "EUR;Euro;European Union;8;" \ + "1c,2c,5c,10c,20c,50c,€1,€2;1,2,5,10,20,50,100,200", + "GBP": "GBP;Pound sterling;United Kingdom;9;" \ + "1p,2p,5p,10p,20p,25p,50p,£1,£2;1,2,5,10,20,25,50,100,200" } } DEFAULT_CONFIGURATION_FILE = join(get_configuration_path(), "piggybank.conf") @@ -45,19 +60,25 @@ class Configuration: def load(self) -> None: for line in open(self._configuration_file, 'r'): - if line[0] == "#": + if line[0] == "#" or line[0] == "\n": continue key, value = line[:-1].split(" = ") if key.find(".") != -1: k0, k1 = key.split(".") - self._configuration[k0] = {k1 : value} + if not k0 in self._configuration: + self._configuration[k0] = dict() + self._configuration[k0][k1] = value else: self._configuration[key] = value def save(self) -> None: with open(self._configuration_file, 'w') as cf: for key, value in self._configuration.items(): - cf.write(f"{key} = {value}\n") + if type(value) is dict: + for subkey, subvalue in value.items(): + cf.write(f"{key}.{subkey} = {subvalue}\n") + else: + cf.write(f"{key} = {value}\n") @property def items(self) -> dict: diff --git a/piggybank/currencies.py b/piggybank/currencies.py index 5734214..9f0cc99 100644 --- a/piggybank/currencies.py +++ b/piggybank/currencies.py @@ -16,6 +16,9 @@ __all__ = [ "Currency", "CURRENCIES", "BaseCurrencyError", "CurrencyMismatchError"] +CURRENCIES: Dict[str, Currency] = dict() + + class BaseCurrencyError(Exception): """Base class for all currency exeptions.""" def __init__(self, message=None, *args, **kwargs) -> None: @@ -122,27 +125,27 @@ class Currency: f"{','.join(list(map(str, self.face_values)))}" -CURRENCIES: Dict[str, Currency] = { - "RUB": Currency("RUB", "Russian ruble", "Russian Federation", 8, - ["1к.", "5к.", "10к.", "50к.", "1₽", "2₽", "5₽", "10₽"], - [1, 5, 10, 50, 1_00, 2_00, 5_00, 10_00]), - "SRUB": Currency("SRUB", "Russian ruble (shortened)", - "Russian Federation. Excluding coins of 1 and 5 kopek.", - 6, ["10к.", "50к.", "1₽", "2₽", "5₽", "10₽"], - [10, 50, 1_00, 2_00, 5_00, 10_00]), - "BYN": Currency("BYN", "Belarusian ruble", "Belarus", 8, - ["1к.", "2к.", "5к.", "10к.", "20к.", "50к.", "1р.", "2р."], - [1, 2, 5, 10, 20, 50, 1_00, 2_00]), - "UAH": Currency("UAH", "Ukrainian hryvnia", "Ukraine", 10, - ["1к.", "2к.", "5к.", "10к.", "25к.", "50к.", "₴1", "₴2", "₴5", "₴10"], - [1, 2, 5, 10, 25, 50, 1_00, 2_00, 5_00, 10_00]), - "USD": Currency("USD", "US Dollar", "United States of America", 6, - ["1¢", "5¢", "10¢", "25¢", "50¢", "$1"], - [1, 5, 10, 25, 50, 1_00]), - "EUR": Currency("EUR", "Euro", "European Union", 8, - ["1c", "2c", "5c", "10c", "20c", "50c", "€1", "€2"], - [1, 2, 5, 10, 20, 50, 1_00, 2_00]), - "GBP": Currency("GBP", "Pound sterling", "United Kingdom", 9, - ["1p", "2p", "5p", "10p", "20p", "25p", "50p", "£1", "£2"], - [1, 2, 5, 10, 20, 25, 50, 1_00, 2_00]) -} +# CURRENCIES: Dict[str, Currency] = { +# "RUB": Currency("RUB", "Russian ruble", "Russian Federation", 8, +# ["1к.", "5к.", "10к.", "50к.", "1₽", "2₽", "5₽", "10₽"], +# [1, 5, 10, 50, 1_00, 2_00, 5_00, 10_00]), +# "SRUB": Currency("SRUB", "Russian ruble (shortened)", +# "Russian Federation. Excluding coins of 1 and 5 kopek.", +# 6, ["10к.", "50к.", "1₽", "2₽", "5₽", "10₽"], +# [10, 50, 1_00, 2_00, 5_00, 10_00]), +# "BYN": Currency("BYN", "Belarusian ruble", "Belarus", 8, +# ["1к.", "2к.", "5к.", "10к.", "20к.", "50к.", "1р.", "2р."], +# [1, 2, 5, 10, 20, 50, 1_00, 2_00]), +# "UAH": Currency("UAH", "Ukrainian hryvnia", "Ukraine", 10, +# ["1к.", "2к.", "5к.", "10к.", "25к.", "50к.", "₴1", "₴2", "₴5", "₴10"], +# [1, 2, 5, 10, 25, 50, 1_00, 2_00, 5_00, 10_00]), +# "USD": Currency("USD", "US Dollar", "United States of America", 6, +# ["1¢", "5¢", "10¢", "25¢", "50¢", "$1"], +# [1, 5, 10, 25, 50, 1_00]), +# "EUR": Currency("EUR", "Euro", "European Union", 8, +# ["1c", "2c", "5c", "10c", "20c", "50c", "€1", "€2"], +# [1, 2, 5, 10, 20, 50, 1_00, 2_00]), +# "GBP": Currency("GBP", "Pound sterling", "United Kingdom", 9, +# ["1p", "2p", "5p", "10p", "20p", "25p", "50p", "£1", "£2"], +# [1, 2, 5, 10, 20, 25, 50, 1_00, 2_00]) +# } diff --git a/piggybank/piggybank.py b/piggybank/piggybank.py index b501314..fe6e324 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, \ - CurrencyMismatchError +from piggybank.currencies import CURRENCIES, CurrencyIsNotSupportedError from piggybank.transaction import Transaction, sum_transactions, TYPE_INCOME __all__ = ["PiggyBank"] @@ -93,10 +92,3 @@ class PiggyBank: def __eq__(self, piggybank: PiggyBank) -> str: """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 diff --git a/piggybank/transaction.py b/piggybank/transaction.py index cfc3b28..5f4b7a2 100644 --- a/piggybank/transaction.py +++ b/piggybank/transaction.py @@ -7,7 +7,7 @@ from typing import Optional, List, Union from piggybank.currencies import CURRENCIES -__all__ = ["Transaction", "sum_transactions", "multiply_transactions", +__all__ = ["Transaction", "sum_transactions", "TYPE_INCOME", "TYPE_OUTCOME", "TIME_FORMAT"] @@ -27,16 +27,6 @@ def sum_transactions(transactions: List[Transaction]) -> List[int]: 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.