1
0

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:
Alexander Andreev 2020-06-07 20:41:17 +04:00
parent 0efb129ad3
commit f2d2e75128
8 changed files with 76 additions and 89 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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
@ -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:

View File

@ -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,18 +60,24 @@ 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():
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

View File

@ -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,
["", "", "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])
# }

View File

@ -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

View File

@ -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.