From 293212c60ed3744f6613f23fea058f8ba73a6f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Oliva?= Date: Thu, 4 May 2023 22:32:32 -0600 Subject: First attempt at storing ledger entries. Now we can store the entries in very simple ways and we can even print them! --- ledger.py | 9 +++- utils/read_file.py | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 utils/read_file.py diff --git a/ledger.py b/ledger.py index 61eb6a5..a224591 100755 --- a/ledger.py +++ b/ledger.py @@ -1,11 +1,18 @@ #!/usr/bin/env python3 from utils.args import get_arguments, test_args +from utils.read_file import read_ledger def main(): args = get_arguments() test_args(args) - print(args) + if args.files: + for file in args.files: + result = read_ledger(file) + + if args.verb == 'print': + for ent in result: + print(ent) if __name__ == '__main__': diff --git a/utils/read_file.py b/utils/read_file.py new file mode 100644 index 0000000..1906fc7 --- /dev/null +++ b/utils/read_file.py @@ -0,0 +1,119 @@ +from datetime import date, datetime +import re +import os + + +VALID_DATES_FORMAT = r'\d{4}[-./]\d{1,2}[-./]\d{1,2}' +VALID_DATES_SEP = '[-./]' + + +class entry: + def __init__(self, date: str, comment: str, transactions: list) -> None: + self.date = self.__date_from_str(date) + self.comment = comment + self.transactions = transactions + + + def __date_from_str(self, date_str: str): + """ + Searches for a valid date on a string and transforms it into ISO + format. (YYYY-MM-DD) + """ + my_date = re.findall(VALID_DATES_FORMAT, date_str)[0] + year, month, day = re.split(VALID_DATES_SEP, my_date) + + return datetime.fromisoformat(f'{int(year)}-{int(month):02}-{int(day):02}') + + + def __str__(self) -> str: + result = self.date.strftime('%Y/%m/%d') + result += " " + self.comment + "\n" + + for trans in self.transactions: + if len(trans) == 2: + # TODO: `price` must have a specific format! + account, price = trans + else: + account = trans[0] + price = "" + + result += f" {account:<35} {price:>12}\n" + + return result + + +def give_me_file_contents(path: str): + line_comments = ";#%|*" + try: + with open(path, 'r', encoding='utf8') as fp: + result = fp.readlines() + + for line in result: + # If line is just empty spaces or empty, ignore it. + if len(line.lstrip()) == 0: + continue + + # If first character of line is a line comment, ignore it. + first_char = line.lstrip()[0] + if first_char in line_comments: + continue + + yield line.strip() + + except: + raise Exception(f"Error while trying to read {path} file.") + + +def is_new_entry(line: str): + """ + Returns `True` if the line contains at least one valid date. This means + we're looking at a new transaction. + """ + return re.search(VALID_DATES_FORMAT, line) is not None + + +def read_ledger(path: str): + files_to_read = [path] + date = None + comment = None + transactions = None + + results = [] + + while files_to_read: + current_file = files_to_read.pop() + + for line in give_me_file_contents(current_file): + if line.startswith('!include'): + file_path = line.split()[-1] + base_dir = os.path.dirname(current_file) + files_to_read.append( + os.path.join(base_dir, file_path) + ) + + continue + + if is_new_entry(line) and date is not None: + results.append( + entry(date, comment, transactions) + ) + + if is_new_entry(line): + date, comment = line.split(maxsplit=1) + transactions = [] + else: + # The line is a new transaction + tabs_to_spaces = line.replace('\t', ' ') + transactions.append( + re.split(r'\s{2,}', tabs_to_spaces) + ) + + if date is not None: + results.append( + entry(date, comment, transactions) + ) + date = None + comment = None + transactions = None + + return results -- cgit v1.2.3