aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xledger.py4
-rw-r--r--utils/acc_regex.py105
-rw-r--r--utils/acc_regex_test.py17
3 files changed, 126 insertions, 0 deletions
diff --git a/ledger.py b/ledger.py
index d5e9b72..e98abc1 100755
--- a/ledger.py
+++ b/ledger.py
@@ -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()