From: Dustin Walde Date: Sun, 12 Nov 2023 21:09:17 +0000 (-0800) Subject: Clean recurring code X-Git-Url: https://git.walde.dev/?a=commitdiff_plain;h=95cf54ad0df05e93ac1552c99793eea49860eecd;p=beanbeanbean Clean recurring code - Better date format token config + validation - Reuse shared code --- diff --git a/src/beanbeanbean/recurring.py b/src/beanbeanbean/recurring.py index 023f999..2206112 100644 --- a/src/beanbeanbean/recurring.py +++ b/src/beanbeanbean/recurring.py @@ -3,6 +3,7 @@ from decimal import ( Decimal, localcontext, ) +import re from beancount.core import amount, position from beancount.core.convert import get_weight @@ -19,12 +20,13 @@ import recurrent from beanbeanbean.balance import balance -from .utils import flag +from .utils import flag, make_error from typing import List, Optional, Union -RECURRING_KEYS = ['recur', 'recurring', 'repeat', 'repeating'] AMORTIZE_KEYS = ['amortize'] +DEFAULT_DATEFMT_TOKEN = "/" +RECURRING_KEYS = ['recur', 'recurring', 'repeat', 'repeating'] __plugins__ = ('recurring',) @@ -90,10 +92,7 @@ def handle_recurring_transaction(txn: Transaction, format_token: str) -> Union[L break if phrase is None: - return LoadError( - source=new_metadata(txn.meta["filename"], txn.meta["lineno"]), - message="Recurring metadata key found, but missing phrase", - entry=txn) + return make_error(txn, "Recurring metadata key found, but missing phrase") rr = generate_rrule_from_string(phrase, txn.date) dates = [dt for dt in rr] @@ -194,25 +193,36 @@ def handle_recurring_transaction(txn: Transaction, format_token: str) -> Union[L return entries -def recurring(entries: Entries, options_map, config_string=""): - del options_map, config_string # unused +def _validate_datefmt_token(config_token: str, errors: list) -> str: + if not re.match(r"[a-zA-Z0-9\-_/.]", config_token): + errors.append(LoadError( + entry=None, + message="Invalid dateformat replace string: {}".format(config_token), + source=new_metadata(filename="", lineno=0))) + return DEFAULT_DATEFMT_TOKEN + return config_token + + +def recurring(entries: Entries, options_map, config_string=DEFAULT_DATEFMT_TOKEN): + del options_map # unused out_entries = [] errors = [] + + dateformat_token = _validate_datefmt_token(config_string, errors) + for entry in entries: if not is_recurring_transaction(entry): out_entries.append(entry) else: try: - res = handle_recurring_transaction(entry, "/") + res = handle_recurring_transaction(entry, dateformat_token) if type(res) is list: out_entries.extend(res) else: errors.append(res) except Exception as e: - errors.append(LoadError( - source=new_metadata(entry.meta["filename"], entry.meta["lineno"]), - message="Failed to handle recurring transaction: {}".format(e), - entry=entry)) + errors.append(make_error(entry, + "Failed to handle recurring transaction: {}".format(e))) out_entries.append(flag(entry)) return out_entries, errors