I moved built-in currencies to a configuration file. Made some clean ups, fixed what I've done last night. I guess, it's READY!
This commit is contained in:
parent
0efb129ad3
commit
f2d2e75128
18
README.md
18
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
|
Yes, only coins, no banknots. Well, you actually can easily add support for
|
||||||
banknots, but table will become indeed wide.
|
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
|
# Usage
|
||||||
|
|
||||||
This is a CLI program that is broken down to three separate programs:
|
|
||||||
|
|
||||||
piggybank [OPTIONS] (COINS in FILE [of CURRENCY] | COINS from FILE
|
piggybank [OPTIONS] (COINS in FILE [of CURRENCY] | COINS from FILE
|
||||||
| show FILE [with t(ransactions)])
|
| show FILE [with t(ransactions)])
|
||||||
|
|
||||||
Options:
|
Common options:
|
||||||
|
|
||||||
- `-h,--help` — print this help;
|
- `-h,--help` — print this help;
|
||||||
|
|
||||||
@ -120,6 +106,6 @@ Currency is defined following way:
|
|||||||
|
|
||||||
That is best described with an example. Here it is:
|
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.
|
Yes, long and clunky way... May be I'll come up with something better.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
__all__ = ["__date__", "__version__", "__author__", "__author_email__",
|
__all__ = ["__date__", "__version__", "__author__", "__email__",
|
||||||
"__copyright__", "__license__", "PIGGYBANK_FILE_EXTENSION",
|
"__copyright__", "__license__", "PIGGYBANK_FILE_EXTENSION",
|
||||||
"VERSION"]
|
"VERSION"]
|
||||||
|
|
||||||
@ -6,8 +6,8 @@ __all__ = ["__date__", "__version__", "__author__", "__author_email__",
|
|||||||
__date__ = "7 June 2020"
|
__date__ = "7 June 2020"
|
||||||
__version__ = "1.0.0"
|
__version__ = "1.0.0"
|
||||||
__author__ = "Alexander \"Arav\" Andreev"
|
__author__ = "Alexander \"Arav\" Andreev"
|
||||||
__author_email__ = "me@arav.top"
|
__email__ = "me@arav.top"
|
||||||
__copyright__ = f"Copyright (c) 2020 {__author__} <{__author_email__}>"
|
__copyright__ = f"Copyright (c) 2020 {__author__} <{__email__}>"
|
||||||
__license__ = \
|
__license__ = \
|
||||||
"""This program is free software. It comes without any warranty, to
|
"""This program is free software. It comes without any warranty, to
|
||||||
the extent permitted by applicable law. You can redistribute it
|
the extent permitted by applicable law. You can redistribute it
|
||||||
|
@ -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
|
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
from re import search, split
|
from re import search, split
|
||||||
from sys import argv, stderr
|
from sys import argv, stderr
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from piggybank import VERSION
|
from piggybank import VERSION
|
||||||
from piggybank.cli import complement_list_of_coins
|
|
||||||
from piggybank.configuration import Configuration
|
from piggybank.configuration import Configuration
|
||||||
from piggybank.currencies import Currency, BaseCurrencyError, CURRENCIES
|
from piggybank.currencies import Currency, BaseCurrencyError, CURRENCIES
|
||||||
from piggybank.piggybank import PiggyBank
|
from piggybank.piggybank import PiggyBank
|
||||||
@ -34,8 +34,8 @@ Show: piggybank show FILE [with t(ransactions)]
|
|||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
\tCOINS -- a set of comma or whitespace separated coin counts;
|
\tCOINS -- a set of comma or whitespace separated coin counts;
|
||||||
\tFILE -- a filename of a piggybank;
|
\tFILE -- a filename of a piggy bank;
|
||||||
\tof CURRENCY -- set a partcular currency for a piggybank;
|
\tof CURRENCY -- set a partcular currency for a piggy bank;
|
||||||
\twith t(ransactions) -- print a table of transactions as well.
|
\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)
|
s = sum_transactions(pb.transactions)
|
||||||
print(f"┃{'Counts':^27}┃{'┃'.join(f'{c:^12}' for c in s)}┃")
|
print(f"┃{'Counts':^27}┃{'┃'.join(f'{c:^12}' for c in s)}┃")
|
||||||
print_separator("┣", "╋", "╋", "┫")
|
print_separator("┣", "╋", "╋", "┫")
|
||||||
s = cur.multiply(s)
|
s = cur * s
|
||||||
print(f"┃{'Sums':^27}┃{'┃'.join(f'{c/100:^12.2f}' for c in s)}┃")
|
print(f"┃{'Sums in currency':^27}┃{'┃'.join(f'{c/100:^12.2f}' for c in s)}┃")
|
||||||
print_separator("┣", "╋", "┻", "┫")
|
print_separator("┣", "╋", "┻", "┫")
|
||||||
s = sum(s)
|
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("┗", "┻", "━", "┛")
|
print_separator("┗", "┻", "━", "┛")
|
||||||
|
|
||||||
|
|
||||||
@ -115,12 +115,18 @@ def print_supported_currencies() -> None:
|
|||||||
f" ┃ {CURRENCIES[cur].description}")
|
f" ┃ {CURRENCIES[cur].description}")
|
||||||
print("Default currency is", Configuration()["default-currency"])
|
print("Default currency is", Configuration()["default-currency"])
|
||||||
|
|
||||||
|
def load_currencies() -> None:
|
||||||
def load_user_defined_currencies() -> None:
|
"""Load currencies defined in a configuration file."""
|
||||||
if not "currency" in Configuration().items:
|
if not "currency" in Configuration().items:
|
||||||
return
|
return
|
||||||
for iso, cur in Configuration()["currency"].items():
|
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():
|
def main():
|
||||||
@ -133,6 +139,7 @@ def main():
|
|||||||
print(VERSION)
|
print(VERSION)
|
||||||
exit()
|
exit()
|
||||||
elif cargs["list-currencies"]:
|
elif cargs["list-currencies"]:
|
||||||
|
load_currencies()
|
||||||
print_supported_currencies()
|
print_supported_currencies()
|
||||||
exit()
|
exit()
|
||||||
elif not cargs["default-currency"] is None:
|
elif not cargs["default-currency"] is None:
|
||||||
@ -147,7 +154,7 @@ def main():
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
load_user_defined_currencies()
|
load_currencies()
|
||||||
try:
|
try:
|
||||||
pb = PiggyBank.from_file(args["file"])
|
pb = PiggyBank.from_file(args["file"])
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
@ -27,7 +27,22 @@ def get_configuration_path() -> str:
|
|||||||
|
|
||||||
|
|
||||||
DEFAULT_CONFIGURATION = {
|
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")
|
DEFAULT_CONFIGURATION_FILE = join(get_configuration_path(), "piggybank.conf")
|
||||||
|
|
||||||
@ -45,19 +60,25 @@ class Configuration:
|
|||||||
|
|
||||||
def load(self) -> None:
|
def load(self) -> None:
|
||||||
for line in open(self._configuration_file, 'r'):
|
for line in open(self._configuration_file, 'r'):
|
||||||
if line[0] == "#":
|
if line[0] == "#" or line[0] == "\n":
|
||||||
continue
|
continue
|
||||||
key, value = line[:-1].split(" = ")
|
key, value = line[:-1].split(" = ")
|
||||||
if key.find(".") != -1:
|
if key.find(".") != -1:
|
||||||
k0, k1 = key.split(".")
|
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:
|
else:
|
||||||
self._configuration[key] = value
|
self._configuration[key] = value
|
||||||
|
|
||||||
def save(self) -> None:
|
def save(self) -> None:
|
||||||
with open(self._configuration_file, 'w') as cf:
|
with open(self._configuration_file, 'w') as cf:
|
||||||
for key, value in self._configuration.items():
|
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
|
@property
|
||||||
def items(self) -> dict:
|
def items(self) -> dict:
|
||||||
|
@ -16,6 +16,9 @@ __all__ = [ "Currency", "CURRENCIES", "BaseCurrencyError",
|
|||||||
"CurrencyMismatchError"]
|
"CurrencyMismatchError"]
|
||||||
|
|
||||||
|
|
||||||
|
CURRENCIES: Dict[str, Currency] = dict()
|
||||||
|
|
||||||
|
|
||||||
class BaseCurrencyError(Exception):
|
class BaseCurrencyError(Exception):
|
||||||
"""Base class for all currency exeptions."""
|
"""Base class for all currency exeptions."""
|
||||||
def __init__(self, message=None, *args, **kwargs) -> None:
|
def __init__(self, message=None, *args, **kwargs) -> None:
|
||||||
@ -122,27 +125,27 @@ class Currency:
|
|||||||
f"{','.join(list(map(str, self.face_values)))}"
|
f"{','.join(list(map(str, self.face_values)))}"
|
||||||
|
|
||||||
|
|
||||||
CURRENCIES: Dict[str, Currency] = {
|
# CURRENCIES: Dict[str, Currency] = {
|
||||||
"RUB": Currency("RUB", "Russian ruble", "Russian Federation", 8,
|
# "RUB": Currency("RUB", "Russian ruble", "Russian Federation", 8,
|
||||||
["1к.", "5к.", "10к.", "50к.", "1₽", "2₽", "5₽", "10₽"],
|
# ["1к.", "5к.", "10к.", "50к.", "1₽", "2₽", "5₽", "10₽"],
|
||||||
[1, 5, 10, 50, 1_00, 2_00, 5_00, 10_00]),
|
# [1, 5, 10, 50, 1_00, 2_00, 5_00, 10_00]),
|
||||||
"SRUB": Currency("SRUB", "Russian ruble (shortened)",
|
# "SRUB": Currency("SRUB", "Russian ruble (shortened)",
|
||||||
"Russian Federation. Excluding coins of 1 and 5 kopek.",
|
# "Russian Federation. Excluding coins of 1 and 5 kopek.",
|
||||||
6, ["10к.", "50к.", "1₽", "2₽", "5₽", "10₽"],
|
# 6, ["10к.", "50к.", "1₽", "2₽", "5₽", "10₽"],
|
||||||
[10, 50, 1_00, 2_00, 5_00, 10_00]),
|
# [10, 50, 1_00, 2_00, 5_00, 10_00]),
|
||||||
"BYN": Currency("BYN", "Belarusian ruble", "Belarus", 8,
|
# "BYN": Currency("BYN", "Belarusian ruble", "Belarus", 8,
|
||||||
["1к.", "2к.", "5к.", "10к.", "20к.", "50к.", "1р.", "2р."],
|
# ["1к.", "2к.", "5к.", "10к.", "20к.", "50к.", "1р.", "2р."],
|
||||||
[1, 2, 5, 10, 20, 50, 1_00, 2_00]),
|
# [1, 2, 5, 10, 20, 50, 1_00, 2_00]),
|
||||||
"UAH": Currency("UAH", "Ukrainian hryvnia", "Ukraine", 10,
|
# "UAH": Currency("UAH", "Ukrainian hryvnia", "Ukraine", 10,
|
||||||
["1к.", "2к.", "5к.", "10к.", "25к.", "50к.", "₴1", "₴2", "₴5", "₴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]),
|
# [1, 2, 5, 10, 25, 50, 1_00, 2_00, 5_00, 10_00]),
|
||||||
"USD": Currency("USD", "US Dollar", "United States of America", 6,
|
# "USD": Currency("USD", "US Dollar", "United States of America", 6,
|
||||||
["1¢", "5¢", "10¢", "25¢", "50¢", "$1"],
|
# ["1¢", "5¢", "10¢", "25¢", "50¢", "$1"],
|
||||||
[1, 5, 10, 25, 50, 1_00]),
|
# [1, 5, 10, 25, 50, 1_00]),
|
||||||
"EUR": Currency("EUR", "Euro", "European Union", 8,
|
# "EUR": Currency("EUR", "Euro", "European Union", 8,
|
||||||
["1c", "2c", "5c", "10c", "20c", "50c", "€1", "€2"],
|
# ["1c", "2c", "5c", "10c", "20c", "50c", "€1", "€2"],
|
||||||
[1, 2, 5, 10, 20, 50, 1_00, 2_00]),
|
# [1, 2, 5, 10, 20, 50, 1_00, 2_00]),
|
||||||
"GBP": Currency("GBP", "Pound sterling", "United Kingdom", 9,
|
# "GBP": Currency("GBP", "Pound sterling", "United Kingdom", 9,
|
||||||
["1p", "2p", "5p", "10p", "20p", "25p", "50p", "£1", "£2"],
|
# ["1p", "2p", "5p", "10p", "20p", "25p", "50p", "£1", "£2"],
|
||||||
[1, 2, 5, 10, 20, 25, 50, 1_00, 2_00])
|
# [1, 2, 5, 10, 20, 25, 50, 1_00, 2_00])
|
||||||
}
|
# }
|
||||||
|
@ -5,8 +5,7 @@ from os.path import exists
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from piggybank import PIGGYBANK_FILE_EXTENSION
|
from piggybank import PIGGYBANK_FILE_EXTENSION
|
||||||
from piggybank.currencies import CURRENCIES, CurrencyIsNotSupportedError, \
|
from piggybank.currencies import CURRENCIES, CurrencyIsNotSupportedError
|
||||||
CurrencyMismatchError
|
|
||||||
from piggybank.transaction import Transaction, sum_transactions, TYPE_INCOME
|
from piggybank.transaction import Transaction, sum_transactions, TYPE_INCOME
|
||||||
|
|
||||||
__all__ = ["PiggyBank"]
|
__all__ = ["PiggyBank"]
|
||||||
@ -93,10 +92,3 @@ class PiggyBank:
|
|||||||
def __eq__(self, piggybank: PiggyBank) -> str:
|
def __eq__(self, piggybank: PiggyBank) -> str:
|
||||||
"""Compares only currency."""
|
"""Compares only currency."""
|
||||||
return self._currency == piggybank.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
|
|
||||||
|
@ -7,7 +7,7 @@ from typing import Optional, List, Union
|
|||||||
|
|
||||||
from piggybank.currencies import CURRENCIES
|
from piggybank.currencies import CURRENCIES
|
||||||
|
|
||||||
__all__ = ["Transaction", "sum_transactions", "multiply_transactions",
|
__all__ = ["Transaction", "sum_transactions",
|
||||||
"TYPE_INCOME", "TYPE_OUTCOME", "TIME_FORMAT"]
|
"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))
|
coins = list(map(sub, coins, transaction.coins))
|
||||||
return 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:
|
class Transaction:
|
||||||
"""An object that holds a single transaction.
|
"""An object that holds a single transaction.
|
||||||
|
Loading…
Reference in New Issue
Block a user