aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xledger.py7
-rw-r--r--utils/register.py162
2 files changed, 168 insertions, 1 deletions
diff --git a/ledger.py b/ledger.py
index a224591..d9eb14f 100755
--- a/ledger.py
+++ b/ledger.py
@@ -1,19 +1,24 @@
#!/usr/bin/env python3
from utils.args import get_arguments, test_args
from utils.read_file import read_ledger
+from utils.register import print_register
def main():
args = get_arguments()
test_args(args)
if args.files:
+ result = []
for file in args.files:
- result = read_ledger(file)
+ result += read_ledger(file)
if args.verb == 'print':
for ent in result:
print(ent)
+ elif args.verb in ['register', 'reg', 'r']:
+ print_register(result)
+
if __name__ == '__main__':
main()
diff --git a/utils/register.py b/utils/register.py
new file mode 100644
index 0000000..96bcd3c
--- /dev/null
+++ b/utils/register.py
@@ -0,0 +1,162 @@
+from utils.read_file import entry
+from typing import List, Iterator
+
+import re
+import shutil
+
+
+class currencies:
+ """
+ Store various currencies and add or subtract them.
+ """
+ def __init__(self) -> None:
+ # The keys of the dictionary will be the currency symbol. The value of
+ # the dictionary will be a float number, representing the amount.
+ self.money = dict()
+
+
+ def __retrieve_number(self, price: str) -> float:
+ """
+ Given a string, extract the number from it. The number is defined by a
+ consecutive string of digits, with maybe a dot in between or end of it.
+ """
+ result = re.findall(r'\d+(?:\.\d*)?', price)[0]
+ result = float(result)
+
+ # If it has the minus symbol (-), it must be a negative value.
+ if '-' in price:
+ return -result
+ else:
+ return result
+
+ def __retrieve_currency(self, price: str) -> str:
+ """
+ Given a string, remove the number and maybe a negative symbol from it.
+ The resulting string is most likely the currency symbol.
+ """
+ number = re.findall(r'\d+(?:\.\d*)?', price)[0]
+ result = price.replace(number, '')
+ result = result.replace('-', '')
+
+ return result.strip()
+
+
+ def add_money(self, price: str) -> None:
+ """
+ Add money to our "wallet." If it is a currency we already have, sum the
+ values. If the currency is new, create the entry on our dictionary and
+ assign the amount to it.
+
+ On the other hand, if the amount is equal to 0, delete the currency
+ entry.
+ """
+ number = self.__retrieve_number(price)
+ currency = self.__retrieve_currency(price)
+
+ if currency in self.money:
+ self.money[currency] += number
+ else:
+ self.money[currency] = number
+
+ if self.money[currency] == 0:
+ del self.money[currency]
+
+
+ def __iter__(self) -> Iterator[str]:
+ """
+ When iterating through our currencies, yield the already formatted
+ currencies. If there are not currencies, just return a 0.
+ """
+ if not self.money:
+ yield '0'
+ for currency, number in self.money.items():
+ if len(currency) == 1:
+ yield f'{currency}{number:.02f}'
+ else:
+ yield f'{number:.02f} {currency}'
+
+
+ # What to do if we do "-currencies"?
+ def __neg__(self):
+ negated = currencies()
+ for currency, number in self.money.items():
+ negated.add_money(f'{currency} {-number}')
+
+ return negated
+
+
+def complete_prices(my_entry: entry):
+ """
+ Transform all string of prices to the class `currencies`. If there is an
+ entry that has no price, balance the transaction so the sum is equal to 0.
+ """
+ idx_not_complete = -1
+ money_to_bal = currencies()
+
+ for idx in range(len(my_entry.transactions)):
+ if (len(my_entry.transactions[idx]) == 1):
+ idx_not_complete = idx
+ else:
+ _, price = my_entry.transactions[idx]
+ money_to_bal.add_money(price)
+
+ new_price_format = currencies()
+ new_price_format.add_money(price)
+ my_entry.transactions[idx][1] = new_price_format
+
+ if idx_not_complete != -1:
+ my_entry.transactions[idx_not_complete].append(
+ -money_to_bal
+ )
+
+
+def print_register(entries: List[entry]):
+ width_term, _ = shutil.get_terminal_size(fallback=(80, 24))
+ # We will divide the terminal width in the following way:
+ # - Date and Comment: 37/100 parts of the terminal width.
+ # - Account name: 31/100 parts of the terminal width.
+ # - First price column: 16/100 parts of the terminal width.
+ # - Second price column: 16/100 parts of the terminal width.
+ # NOTE: We reserve 3 spaces of the terminal width to help separate the
+ # columns.
+ d_c_len = int((width_term - 3) * 37 / 100); d_c_len = max(d_c_len, 15)
+ acc_len = int((width_term - 3) * 31 / 100); acc_len = max(acc_len, 6)
+ p_1_len = int((width_term - 3) * 16 / 100)
+ p_2_len = int((width_term - 3) * 16 / 100)
+
+ result = ''
+ current_money = currencies()
+
+ for ent in entries:
+ complete_prices(ent)
+
+ date_comment = ent.date.strftime('%y-%b-%d ')
+ date_comment += ent.comment
+ if len(date_comment) > d_c_len:
+ date_comment = date_comment[:d_c_len - 2] + '..'
+
+ result += f'{date_comment:<{d_c_len}} '
+
+ for trans in ent.transactions:
+ account = trans[0]
+ if len(account) > acc_len:
+ account = '..' + account[-(acc_len - 2):]
+ result += f'{account:<{acc_len}} '
+
+ for price in trans[1]:
+ result += f'{price:>{p_1_len}} '
+
+ current_money.add_money(price)
+ for curr_price in current_money:
+ result += f'{curr_price:>{p_2_len}}\n'
+ result += ' ' * (d_c_len + 1 + acc_len + 1 + p_1_len + 1)
+
+ result = result.rstrip() + '\n'
+ result += ' ' * (d_c_len + 1 + acc_len + 1)
+
+ result = result.rstrip() + '\n'
+ result += ' ' * (d_c_len + 1)
+
+ result = result.rstrip() + '\n'
+
+ print(result.rstrip())