acc_regex.py (2998B) - raw
1 from typing import List
2 from utils.read_file import entry
3 from utils.register import complete_prices
4 import re
5
6 RE_NOT = r'not ((?:[^()\s]+)|(?:\(.+\)))'
7 RE_AND = r'((?:[^()\s]+)|(?:\(.+\))) and ((?:[^()\s]+)|(?:\(.+\)))'
8 RE_OR = r'((?:[^()\s]+)|(?:\(.+\))) or ((?:[^()\s]+)|(?:\(.+\)))'
9
10
11 def clean_pattern(pattern: str):
12 pattern = pattern.strip()
13
14 if pattern[0] == '(' and pattern[-1] == ')':
15 pattern = pattern[1:-1]
16
17 return pattern
18
19
20 def create_ors(string: str):
21 splitted = string.split()
22 result = ''
23
24 for left, right in zip(splitted[:-1], splitted[1:]):
25 if left not in ['not', 'and', 'or'] and right not in ['and', 'or']:
26 result += f'{left} or '
27 else:
28 result += f'{left} '
29
30 result += splitted[-1]
31
32 return result
33
34
35 def find_logic_patt(pattern: str):
36 # First search for the pattern "not {text}"
37 patt = re.findall(RE_NOT, pattern)
38 if patt:
39 return ('not', patt[0])
40
41 # If there wasn't a "not" pattern, try searching for "{text 1} or {text 2}"
42 patt = re.findall(RE_OR, pattern)
43 if patt:
44 return ('or', patt[0])
45
46 # Lastly, try searching for "{text 1} and {text 2}" pattern.
47 patt = re.findall(RE_AND, pattern)
48 if patt:
49 return ('and', patt[0])
50
51 # If there wasn't any pattern, return None.
52 return (None, None)
53
54
55 def re_ledger(pattern: str, account_name: str):
56 pattern = clean_pattern(pattern)
57
58 if ' ' not in pattern:
59 if pattern == '{False}':
60 return False
61 elif pattern == '{True}':
62 return True
63 elif re.search(pattern, account_name) is None:
64 return False
65 else:
66 return True
67
68 pattern = create_ors(pattern)
69 key, patt = find_logic_patt(pattern)
70
71 while key is not None:
72 if key == 'not':
73 meets_criteria = not re_ledger(patt, account_name)
74 pattern = pattern.replace(f'not {patt}', f'{{{meets_criteria}}}')
75
76 elif key == 'or':
77 meets_criteria = re_ledger(patt[0], account_name) or re_ledger(patt[1], account_name)
78 pattern = pattern.replace(f'{patt[0]} or {patt[1]}', f'{{{meets_criteria}}}')
79
80 elif key == 'and':
81 meets_criteria = re_ledger(patt[0], account_name) and re_ledger(patt[1], account_name)
82 pattern = pattern.replace(f'{patt[0]} and {patt[1]}', f'{{{meets_criteria}}}')
83
84
85 key, patt = find_logic_patt(pattern)
86
87 return re_ledger(pattern, account_name)
88
89
90 def filter_accounts(arguments: List[str], my_entries: List[entry]):
91 pattern = ' '.join(arguments)
92 result = []
93
94 for ent in my_entries:
95 complete_prices(ent)
96
97 for trans in ent.transactions:
98 if re_ledger(pattern, trans[0]):
99 result.append(entry(
100 date=ent.date.strftime('%Y-%m-%d'),
101 comment=ent.comment,
102 transactions=[[trans[0], str(trans[1])]]
103 ))
104
105 return result