Changeset - e07a47ec8f8b
[Not reviewed]
0 2 0
Brett Smith - 4 years ago 2020-05-30 02:05:26
brettcsmith@brettcsmith.org
accrual: Use cliutil for better logging and error reporting.
2 files changed with 15 insertions and 17 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/reports/accrual.py
Show inline comments
...
 
@@ -88,21 +88,24 @@ from ..beancount_types import (
 

	
 
import rt
 

	
 
from beancount.parser import printer as bc_printer
 

	
 
from . import core
 
from .. import cliutil
 
from .. import config as configmod
 
from .. import data
 
from .. import filters
 
from .. import rtutil
 

	
 
PROGNAME = 'accrual-report'
 

	
 
PostGroups = Mapping[Optional[MetaValue], core.RelatedPostings]
 
RTObject = Mapping[str, str]
 

	
 
_logger = logging.getLogger('conservancy_beancount.reports.accrual')
 
logger = logging.getLogger('conservancy_beancount.reports.accrual')
 

	
 
class Account(NamedTuple):
 
    name: str
 
    balance_paid: Callable[[core.Balance], bool]
 

	
 

	
...
 
@@ -131,13 +134,13 @@ class AccrualAccount(enum.Enum):
 
        }
 

	
 

	
 
class BaseReport:
 
    def __init__(self, out_file: TextIO) -> None:
 
        self.out_file = out_file
 
        self.logger = _logger.getChild(type(self).__name__)
 
        self.logger = logger.getChild(type(self).__name__)
 

	
 
    def _since_last_nonzero(self, posts: core.RelatedPostings) -> core.RelatedPostings:
 
        retval = core.RelatedPostings()
 
        for post in posts:
 
            if retval.balance().is_zero():
 
                retval.clear()
...
 
@@ -346,13 +349,14 @@ def filter_search(postings: Iterable[data.Posting],
 
    postings = (post for post in postings if post.account.is_under(*accounts))
 
    for meta_key, pattern in search_terms:
 
        postings = filters.filter_meta_match(postings, meta_key, re.compile(pattern))
 
    return postings
 

	
 
def parse_arguments(arglist: Optional[Sequence[str]]=None) -> argparse.Namespace:
 
    parser = argparse.ArgumentParser()
 
    parser = argparse.ArgumentParser(prog=PROGNAME)
 
    cliutil.add_version_argument(parser)
 
    parser.add_argument(
 
        '--report-type', '-t',
 
        metavar='NAME',
 
        type=ReportType.by_name,
 
        help="""The type of report to generate, either `balance` or `outgoing`.
 
If not specified, the default is `outgoing` for search criteria that return a
...
 
@@ -365,12 +369,13 @@ single outstanding payable, and `balance` any other time.
 
        default=-1,
 
        help="""How far back to search the books for related transactions.
 
You can either specify a fiscal year, or a negative offset from the current
 
fiscal year, to start loading entries from. The default is -1 (start from the
 
previous fiscal year).
 
""")
 
    cliutil.add_loglevel_argument(parser)
 
    parser.add_argument(
 
        'search',
 
        nargs=argparse.ZERO_OR_MORE,
 
        help="""Report on accruals that match this criteria. The format is
 
NAME=TERM. TERM is a link or word that must exist in a posting's NAME
 
metadata to match. A single ticket number is a shortcut for
...
 
@@ -378,31 +383,23 @@ metadata to match. A single ticket number is a shortcut for
 
`TIK/ATT` format, is a shortcut for `invoice=LINK`.
 
""")
 
    args = parser.parse_args(arglist)
 
    args.search_terms = [SearchTerm.parse(s) for s in args.search]
 
    return args
 

	
 
def setup_logger(logger: logging.Logger, loglevel: int, stream: TextIO=sys.stderr) -> None:
 
    formatter = logging.Formatter('%(name)s: %(levelname)s: %(message)s')
 
    handler = logging.StreamHandler(stream)
 
    handler.setFormatter(formatter)
 
    logger.addHandler(handler)
 
    logger.setLevel(loglevel)
 

	
 
def main(arglist: Optional[Sequence[str]]=None,
 
         stdout: TextIO=sys.stdout,
 
         stderr: TextIO=sys.stderr,
 
         config: Optional[configmod.Config]=None,
 
         logger: Optional[logging.Logger]=None,
 
) -> int:
 
    if logger is None:
 
        global _logger
 
        _logger = logger = logging.getLogger('accrual-report')
 
        setup_logger(_logger, logging.INFO, stderr)
 
    returncode = 0
 
    if cliutil.is_main_script():
 
        global logger
 
        logger = logging.getLogger(PROGNAME)
 
        sys.excepthook = cliutil.ExceptHook(logger)
 
    args = parse_arguments(arglist)
 
    cliutil.setup_logger(logger, args.loglevel, stderr)
 
    if config is None:
 
        config = configmod.Config()
 
        config.load_file()
 
    books_loader = config.books_loader()
 
    if books_loader is not None:
 
        entries, load_errors, _ = books_loader.load_fy_range(args.since)
...
 
@@ -414,12 +411,13 @@ def main(arglist: Optional[Sequence[str]]=None,
 
        }
 
        load_errors = [Error(source, "no books to load in configuration", None)]
 
    postings = filter_search(data.Posting.from_entries(entries), args.search_terms)
 
    groups = core.RelatedPostings.group_by_meta(postings, 'invoice')
 
    groups = AccrualAccount.filter_paid_accruals(groups) or groups
 
    meta_errors = consistency_check(groups)
 
    returncode = 0
 
    for error in load_errors:
 
        bc_printer.print_error(error, file=stderr)
 
        returncode |= ReturnFlag.LOAD_ERRORS
 
    for error in meta_errors:
 
        bc_printer.print_error(error, file=stderr)
 
        returncode |= ReturnFlag.CONSISTENCY_ERRORS
setup.py
Show inline comments
...
 
@@ -2,13 +2,13 @@
 

	
 
from setuptools import setup
 

	
 
setup(
 
    name='conservancy_beancount',
 
    description="Plugin, library, and reports for reading Conservancy's books",
 
    version='1.0.7',
 
    version='1.0.8',
 
    author='Software Freedom Conservancy',
 
    author_email='info@sfconservancy.org',
 
    license='GNU AGPLv3+',
 

	
 
    install_requires=[
 
        'babel>=2.6',  # Debian:python3-babel
0 comments (0 inline, 0 general)