diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/read_file.py | 119 |
1 files changed, 119 insertions, 0 deletions
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 |