From 35804db617a10f67678e172067c4476d74874573 Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Mon, 31 Aug 2020 14:19:00 -0400 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. --- conservancy_beancount/cliutil.py | 12 ++++++++++++ conservancy_beancount/reports/accrual.py | 15 ++++++++++++--- conservancy_beancount/reports/balance_sheet.py | 13 ++----------- conservancy_beancount/reports/fund.py | 12 +++++++++++- conservancy_beancount/reports/ledger.py | 10 ++++++++++ conservancy_beancount/tools/audit_report.py | 7 +++---- setup.py | 2 +- 7 files changed, 51 insertions(+), 20 deletions(-) diff --git a/conservancy_beancount/cliutil.py b/conservancy_beancount/cliutil.py index ea7768e..781d9dd 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 7362341..39a306f 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 fed135f..d02fe42 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 1e09280..e38f4bf 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 7e15168..619948c 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 0463f38..10df2d8 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 1cd8199..40fe755 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+',