diff --git a/pfbudget/categories.py b/pfbudget/categories.py index e045c32..398d8d2 100644 --- a/pfbudget/categories.py +++ b/pfbudget/categories.py @@ -56,11 +56,10 @@ def categorize_data(db: DBManager): # 3rd) Classify all else based on regex if transactions := db.get_uncategorized_transactions(): for transaction in transactions: - if not transaction.category: - for name, category in categories.items(): - if matches(transaction, category): - transaction.category = name - break + for name, category in categories.items(): + if matches(transaction, category): + transaction.category = name + break db.update_categories( [transaction for transaction in transactions if transaction.category] ) @@ -150,7 +149,10 @@ def nulls(db: DBManager) -> None: def matches(transaction: Transaction, category: Options): if not category.regex: return False - return any( - re.compile(pattern).search(transaction.description.lower()) - for pattern in category.regex - ) + try: + return any( + re.compile(pattern).search(transaction.description.lower()) + for pattern in category.regex + ) + except re.error as e: + print(f"{e}{transaction} {category}") diff --git a/pfbudget/graph.py b/pfbudget/graph.py index 59a2820..42034f0 100644 --- a/pfbudget/graph.py +++ b/pfbudget/graph.py @@ -41,7 +41,7 @@ def monthly( ] ) - plt.figure(figsize=(30, 10)) + plt.figure(tight_layout=True) plt.plot( list(rrule(MONTHLY, dtstart=start.replace(day=1), until=end.replace(day=1))), [groups["income"] for _, groups in monthly_transactions], @@ -60,7 +60,6 @@ def monthly( ], ) plt.legend(loc="upper left") - plt.tight_layout() if args["save"]: plt.savefig("graph.png") else: diff --git a/pfbudget/parsers.py b/pfbudget/parsers.py index 08aa8ce..6fab891 100644 --- a/pfbudget/parsers.py +++ b/pfbudget/parsers.py @@ -31,7 +31,20 @@ Options = namedtuple( "MasterCard", "AmericanExpress", ], - defaults=["", "", "", 1, None, Index(), Index(), False, None, None, None, None], + defaults=[ + "", + "", + "", + 1, + None, + Index(), + Index(), + False, + None, + None, + None, + None, + ], ) @@ -88,6 +101,7 @@ class Parser: for line in list(open(self.filename, encoding=self.options.encoding))[ self.options.start - 1 : self.options.end ] + if len(line) > 2 ] return transactions @@ -100,9 +114,10 @@ class Parser: elif line[options.credit.value]: index = options.credit elif options.debit.date != options.credit.date: - if line[options.debit.date]: + negate = 1 if (options.debit.negate or options.credit.negate) else -1 + if (negate * utils.parse_decimal(line[options.debit.value])) < 0: index = options.debit - elif line[options.credit.date]: + else: index = options.credit elif options.debit.text != options.credit.text: if line[options.debit.text]: diff --git a/pfbudget/utils.py b/pfbudget/utils.py index 7061439..ea5986c 100644 --- a/pfbudget/utils.py +++ b/pfbudget/utils.py @@ -1,5 +1,5 @@ from datetime import date, datetime, timedelta -from decimal import Decimal +from decimal import Decimal, InvalidOperation from pathlib import Path @@ -21,15 +21,18 @@ def parse_decimal(s: str) -> Decimal: return Decimal(s) except ValueError: pass - s = s.strip().replace(u"\xa0", "").replace(" ", "") - s = s.strip().replace("€", "").replace("+", "") - if s.rfind(",") > s.rfind("."): - s = s.replace(".", "") - i = s.rfind(",") - li = list(s) - li[i] = "." - s = "".join(li) - return Decimal(s.replace(",", "")) + try: + d = s.strip().replace("\xa0", "").replace(" ", "") + d = d.replace("€", "").replace("+", "").replace("EUR", "").strip() + if d.rfind(",") > d.rfind("."): + d = d.replace(".", "") + i = d.rfind(",") + li = list(d) + li[i] = "." + d = "".join(li) + return Decimal(d.replace(",", "")) + except InvalidOperation: + raise InvalidOperation(f"{s} -> {d}") def find_credit_institution(fn, banks, creditcards): @@ -47,10 +50,10 @@ def find_credit_institution(fn, banks, creditcards): if not bank: raise WrongFilenameError - if bank not in banks: - raise BankNotAvailableError - if cc and cc not in creditcards: - raise CreditCardNotAvailableError + if bank.lower() not in [bank.lower() for bank in banks]: + raise BankNotAvailableError(f"{fn}: {banks}") + if cc and cc.lower() not in [cc.lower() for cc in creditcards]: + raise CreditCardNotAvailableError(f"{fn}: {banks}") return bank, cc