diff options
-rwxr-xr-x | ledger.py | 4 | ||||
-rw-r--r-- | utils/acc_regex.py | 105 | ||||
-rw-r--r-- | utils/acc_regex_test.py | 17 |
3 files changed, 126 insertions, 0 deletions
@@ -4,6 +4,7 @@ from utils.read_file import read_ledger from utils.register import print_register from utils.sort import sort_entries from utils.balance import print_balance +from utils.acc_regex import filter_accounts def main(): args = get_arguments() @@ -14,6 +15,9 @@ def main(): for file in args.files: result += read_ledger(file) + if len(args.verb) > 1: + result = filter_accounts(args.verb[1:], result) + if args.sort: sort_entries(result, args.sort) diff --git a/utils/acc_regex.py b/utils/acc_regex.py new file mode 100644 index 0000000..1c85def --- /dev/null +++ b/utils/acc_regex.py @@ -0,0 +1,105 @@ +from typing import List +from utils.read_file import entry +from utils.register import complete_prices +import re + +RE_NOT = r'not ((?:[^()\s]+)|(?:\(.+\)))' +RE_AND = r'((?:[^()\s]+)|(?:\(.+\))) and ((?:[^()\s]+)|(?:\(.+\)))' +RE_OR = r'((?:[^()\s]+)|(?:\(.+\))) or ((?:[^()\s]+)|(?:\(.+\)))' + + +def clean_pattern(pattern: str): + pattern = pattern.strip() + + if pattern[0] == '(' and pattern[-1] == ')': + pattern = pattern[1:-1] + + return pattern + + +def create_ors(string: str): + splitted = string.split() + result = '' + + for left, right in zip(splitted[:-1], splitted[1:]): + if left not in ['not', 'and', 'or'] and right not in ['and', 'or']: + result += f'{left} or ' + else: + result += f'{left} ' + + result += splitted[-1] + + return result + + +def find_logic_patt(pattern: str): + # First search for the pattern "not {text}" + patt = re.findall(RE_NOT, pattern) + if patt: + return ('not', patt[0]) + + # If there wasn't a "not" pattern, try searching for "{text 1} or {text 2}" + patt = re.findall(RE_OR, pattern) + if patt: + return ('or', patt[0]) + + # Lastly, try searching for "{text 1} and {text 2}" pattern. + patt = re.findall(RE_AND, pattern) + if patt: + return ('and', patt[0]) + + # If there wasn't any pattern, return None. + return (None, None) + + +def re_ledger(pattern: str, account_name: str): + pattern = clean_pattern(pattern) + + if ' ' not in pattern: + if pattern == '{False}': + return False + elif pattern == '{True}': + return True + elif re.search(pattern, account_name) is None: + return False + else: + return True + + pattern = create_ors(pattern) + key, patt = find_logic_patt(pattern) + + while key is not None: + if key == 'not': + meets_criteria = not re_ledger(patt, account_name) + pattern = pattern.replace(f'not {patt}', f'{{{meets_criteria}}}') + + elif key == 'or': + meets_criteria = re_ledger(patt[0], account_name) or re_ledger(patt[1], account_name) + pattern = pattern.replace(f'{patt[0]} or {patt[1]}', f'{{{meets_criteria}}}') + + elif key == 'and': + meets_criteria = re_ledger(patt[0], account_name) and re_ledger(patt[1], account_name) + pattern = pattern.replace(f'{patt[0]} and {patt[1]}', f'{{{meets_criteria}}}') + + + key, patt = find_logic_patt(pattern) + + return re_ledger(pattern, account_name) + + +def filter_accounts(arguments: List[str], my_entries: List[entry]): + pattern = ' '.join(arguments) + result = [] + + for ent in my_entries: + complete_prices(ent) + + for trans in ent.transactions: + if re_ledger(pattern, trans[0]): + result.append(entry( + date=ent.date.strftime('%Y-%m-%d'), + comment=ent.comment, + transactions=[[trans[0], str(trans[1])]] + )) + + return result diff --git a/utils/acc_regex_test.py b/utils/acc_regex_test.py new file mode 100644 index 0000000..309d83a --- /dev/null +++ b/utils/acc_regex_test.py @@ -0,0 +1,17 @@ +import acc_regex +import unittest + + +class test_create_ors(unittest.TestCase): + def test_create_ors(self): + self.assertEqual(acc_regex.create_ors('Rent Transportation'), 'Rent or Transportation') + self.assertEqual(acc_regex.create_ors('Income and Job'), 'Income and Job') + self.assertEqual(acc_regex.create_ors('Radio and (Judge Police)'), 'Radio and (Judge or Police)') + + self.assertEqual(acc_regex.create_ors('(Telephone not Beach) not House'), '(Telephone or not Beach) or not House') + + self.assertEqual(acc_regex.create_ors('Run'), 'Run') + + +if __name__ == '__main__': + unittest.main() |