diff --git a/conservancy_beancount/cliutil.py b/conservancy_beancount/cliutil.py index ea7768e31afde12aa7416de8be7dbede93a2ce86..781d9dd935fc76fd068060a4f99851aa835ed4fd 100644 --- a/conservancy_beancount/cliutil.py +++ b/conservancy_beancount/cliutil.py @@ -119,6 +119,7 @@ class ExitCode(enum.IntEnum): NoConfig = NoConfiguration NoDataFiltered = os.EX_DATAERR NoDataLoaded = os.EX_NOINPUT + RewriteRulesError = os.EX_DATAERR # Our own exit codes, working down from that range BeancountErrors = 63 @@ -253,6 +254,17 @@ def add_loglevel_argument(parser: argparse.ArgumentParser, f" Default {default.name.lower()}.", ) +def add_rewrite_rules_argument(parser: argparse.ArgumentParser) -> argparse.Action: + return parser.add_argument( + '--rewrite-rules', '--rewrites', '-r', + action='append', + default=[], + metavar='PATH', + type=Path, + help="""Use rewrite rules from the given YAML file. You can specify +this option multiple times to load multiple sets of rewrite rules in order. +""") + def add_version_argument(parser: argparse.ArgumentParser) -> argparse.Action: progname = parser.prog or sys.argv[0] return parser.add_argument( diff --git a/conservancy_beancount/reports/accrual.py b/conservancy_beancount/reports/accrual.py index 7362341fc099a884056eb401a5fabbe944a7b118..39a306f78a89ed0dc61abb04a93414933bca5924 100644 --- a/conservancy_beancount/reports/accrual.py +++ b/conservancy_beancount/reports/accrual.py @@ -115,6 +115,7 @@ import rt from beancount.parser import printer as bc_printer from . import core +from . import rewrite from .. import books from .. import cliutil from .. import config as configmod @@ -647,6 +648,7 @@ def filter_search(postings: Iterable[data.Posting], def parse_arguments(arglist: Optional[Sequence[str]]=None) -> argparse.Namespace: parser = argparse.ArgumentParser(prog=PROGNAME) cliutil.add_version_argument(parser) + cliutil.add_rewrite_rules_argument(parser) parser.add_argument( '--report-type', '-t', metavar='NAME', @@ -721,9 +723,16 @@ def main(arglist: Optional[Sequence[str]]=None, for error in load_errors: bc_printer.print_error(error, file=stderr) - postings = list(filter_search( - data.Posting.from_entries(entries), args.search_terms, - )) + postings_src = data.Posting.from_entries(entries) + for rewrite_path in args.rewrite_rules: + try: + ruleset = rewrite.RewriteRuleset.from_yaml(rewrite_path) + except ValueError as error: + logger.critical("failed loading rewrite rules from %s: %s", + rewrite_path, error.args[0]) + return cliutil.ExitCode.RewriteRulesError + postings_src = ruleset.rewrite(postings_src) + postings = list(filter_search(postings_src, args.search_terms)) if not postings: logger.warning("no matching entries found to report") returncode = returncode or cliutil.ExitCode.NoDataFiltered diff --git a/conservancy_beancount/reports/balance_sheet.py b/conservancy_beancount/reports/balance_sheet.py index fed135f8d348921b5257d8040b6678948dcbf6fd..d02fe4227f39df6a14bc5b97e9f620926038c9a4 100644 --- a/conservancy_beancount/reports/balance_sheet.py +++ b/conservancy_beancount/reports/balance_sheet.py @@ -20,7 +20,6 @@ import datetime import enum import logging import operator -import os import sys from decimal import Decimal @@ -633,15 +632,7 @@ The default is one year ago. help="""Date to stop reporting entries, exclusive, in YYYY-MM-DD format. The default is a year after the start date. """) - parser.add_argument( - '--rewrite-rules', '--rewrites', '-r', - action='append', - default=[], - metavar='PATH', - type=Path, - help="""Use rewrite rules from the given YAML file. You can specify -this option multiple times to load multiple sets of rewrite rules in order. -""") + cliutil.add_rewrite_rules_argument(parser) parser.add_argument( '--fund-metadata-key', '-m', metavar='KEY', @@ -705,7 +696,7 @@ def main(arglist: Optional[Sequence[str]]=None, except ValueError as error: logger.critical("failed loading rewrite rules from %s: %s", rewrite_path, error.args[0]) - return os.EX_DATAERR + return cliutil.ExitCode.RewriteRulesError postings = ruleset.rewrite(postings) balances = Balances( diff --git a/conservancy_beancount/reports/fund.py b/conservancy_beancount/reports/fund.py index 1e09280db38a785357020998e5e660379244dcc5..e38f4bfe57d46cb66a9d7175ce8ab1fce99ae15a 100644 --- a/conservancy_beancount/reports/fund.py +++ b/conservancy_beancount/reports/fund.py @@ -74,6 +74,7 @@ import odf.table # type:ignore[import] from beancount.parser import printer as bc_printer from . import core +from . import rewrite from .. import books from .. import cliutil from .. import config as configmod @@ -308,6 +309,7 @@ The default is one year ago. help="""Date to stop reporting entries, exclusive, in YYYY-MM-DD format. The default is a year after the start date. """) + cliutil.add_rewrite_rules_argument(parser) parser.add_argument( '--report-type', '-t', metavar='TYPE', @@ -378,11 +380,19 @@ def main(arglist: Optional[Sequence[str]]=None, for error in load_errors: bc_printer.print_error(error, file=stderr) - postings = ( + postings = iter( post for post in data.Posting.from_entries(entries) if post.meta.date < args.stop_date ) + for rewrite_path in args.rewrite_rules: + try: + ruleset = rewrite.RewriteRuleset.from_yaml(rewrite_path) + except ValueError as error: + logger.critical("failed loading rewrite rules from %s: %s", + rewrite_path, error.args[0]) + return cliutil.ExitCode.RewriteRulesError + postings = ruleset.rewrite(postings) for search_term in args.search_terms: postings = search_term.filter_postings(postings) fund_postings = { diff --git a/conservancy_beancount/reports/ledger.py b/conservancy_beancount/reports/ledger.py index 7e15168975090c4f1e4fa0c2932ad9c3dfead33a..619948c012d866bde565d2979e063aa4d9464afb 100644 --- a/conservancy_beancount/reports/ledger.py +++ b/conservancy_beancount/reports/ledger.py @@ -78,6 +78,7 @@ from beancount.core import data as bc_data from beancount.parser import printer as bc_printer from . import core +from . import rewrite from .. import books from .. import cliutil from .. import config as configmod @@ -701,6 +702,7 @@ multiple times. You can specify a part of the account hierarchy, or an account classification from metadata. If not specified, the default set adapts to your search criteria. """) + cliutil.add_rewrite_rules_argument(parser) parser.add_argument( '--show-totals', '-S', metavar='ACCOUNT', @@ -797,6 +799,14 @@ def main(arglist: Optional[Sequence[str]]=None, data.Account.load_from_books(entries, options) postings = data.Posting.from_entries(entries) + for rewrite_path in args.rewrite_rules: + try: + ruleset = rewrite.RewriteRuleset.from_yaml(rewrite_path) + except ValueError as error: + logger.critical("failed loading rewrite rules from %s: %s", + rewrite_path, error.args[0]) + return cliutil.ExitCode.RewriteRulesError + postings = ruleset.rewrite(postings) for search_term in args.search_terms: postings = search_term.filter_postings(postings) diff --git a/conservancy_beancount/tools/audit_report.py b/conservancy_beancount/tools/audit_report.py index 0463f388f54b046c24937787aa3e5907d8a0983c..10df2d837f7910460284ee098feffa02e6116044 100644 --- a/conservancy_beancount/tools/audit_report.py +++ b/conservancy_beancount/tools/audit_report.py @@ -196,6 +196,8 @@ def main(arglist: Optional[Sequence[str]]=None, raise ValueError(f"unknown year {year!r}") out_path = args.output_directory / out_name output_reports.append(out_path) + for path in args.rewrite_rules: + yield f'--rewrite-rules={path}' yield f'--output-file={out_path}' yield from arglist reports: List[Tuple[ReportFunc, ArgList]] = [ @@ -208,10 +210,7 @@ def main(arglist: Optional[Sequence[str]]=None, (ledger.main, list(common_args('Disbursements', next_year, '--disbursements'))), (ledger.main, list(common_args('Receipts', next_year, '--receipts'))), (accrual.main, list(common_args('AgingReport.ods'))), - (balance_sheet.main, list(common_args( - 'Summary', args.audit_year, - *(f'--rewrite-rules={path}' for path in args.rewrite_rules), - ))), + (balance_sheet.main, list(common_args('Summary', args.audit_year))), (fund.main, list(common_args('FundReport', args.audit_year))), (fund.main, list(common_args('FundReport', next_year))), ] diff --git a/setup.py b/setup.py index 1cd8199c2c4b68ddb6bab459ec9339129a9b85a1..40fe75587c0d996dea4565fdb466fe36a460c820 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from setuptools import setup setup( name='conservancy_beancount', description="Plugin, library, and reports for reading Conservancy's books", - version='1.9.0', + version='1.9.1', author='Software Freedom Conservancy', author_email='info@sfconservancy.org', license='GNU AGPLv3+',