From 35804db617a10f67678e172067c4476d74874573 2020-08-31 18:19:00 From: Brett Smith Date: 2020-08-31 18:19:00 Subject: [PATCH] reports: All reports support rewrite rules. I realized that if ledger-report supported rewrite rules, then it would include all the information necessary to reproduce the numbers on the statement of functional expenses. With that, it was easy enough to add support to the rest of the reports for consistency's sake. --- 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+',