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