Changeset - 76f2707aacf7
[Not reviewed]
0 5 1
Brett Smith - 6 years ago 2017-12-31 17:35:20
brettcsmith@brettcsmith.org
hooks.ledger_entry: New hook to output Ledger entries.

This is roughly the smallest diff necessary to move output to a hook.
There's a lot of code reorganization that should still happen to bring it
better in line with this new structure.
6 files changed with 100 insertions and 28 deletions:
0 comments (0 inline, 0 general)
import2ledger/__main__.py
Show inline comments
...
 
@@ -24,13 +24,3 @@ class FileImporter:
 
            if importer.can_import(in_file):
 
                try:
 
                    template = self.config.get_template(importer.TEMPLATE_KEY)
 
                except errors.UserInputConfigurationError as error:
 
                    if error.strerror.startswith('template not defined '):
 
                        have_template = False
 
                    else:
 
                        raise
 
                else:
 
                    have_template = not template.is_empty()
 
                if have_template:
 
                    importers.append((importer, template))
 
                importers.append(importer)
 
        if not importers:
...
 
@@ -45,17 +35,15 @@ class FileImporter:
 
        }
 
        with self.config.open_output_file() as out_file:
 
            for importer, template in importers:
 
                in_file.seek(0)
 
                for entry_data in importer(in_file):
 
                    for hook in self.hooks:
 
                        hook_retval = hook.run(entry_data)
 
                        if hook_retval is None:
 
                            pass
 
                        elif hook_retval is False:
 
                            break
 
                        else:
 
                            entry_data = hook_retval
 
        for importer in importers:
 
            in_file.seek(0)
 
            source_vars['template'] = importer.TEMPLATE_KEY
 
            for entry_data in importer(in_file):
 
                entry_data = collections.ChainMap(entry_data, source_vars)
 
                for hook in self.hooks:
 
                    hook_retval = hook.run(entry_data)
 
                    if hook_retval is None:
 
                        pass
 
                    elif hook_retval is False:
 
                        break
 
                    else:
 
                        render_vars = collections.ChainMap(entry_data, source_vars)
 
                        print(template.render(render_vars), file=out_file, end='')
 
                        entry_data = hook_retval
 

	
import2ledger/hooks/__init__.py
Show inline comments
...
 
@@ -21,2 +21,4 @@ HOOK_KINDS = enum.Enum('HOOK_KINDS', [
 
    'DATA_FILTER',
 
    # OUTPUT hooks run last, sending the data somewhere else.
 
    'OUTPUT',
 
])
import2ledger/hooks/ledger_entry.py
Show inline comments
 
new file 100644
 
from . import HOOK_KINDS
 

	
 
from .. import errors
 

	
 
class LedgerEntryHook:
 
    KIND = HOOK_KINDS.OUTPUT
 

	
 
    def __init__(self, config):
 
        self.config = config
 

	
 
    def run(self, entry_data):
 
        try:
 
            template = self.config.get_template(entry_data['template'])
 
        except errors.UserInputConfigurationError as error:
 
            if error.strerror.startswith('template not defined '):
 
                have_template = False
 
            else:
 
                raise
 
        else:
 
            have_template = not template.is_empty()
 
        if have_template:
 
            with self.config.open_output_file() as out_file:
 
                print(template.render(entry_data), file=out_file, end='')
tests/data/templates.ini
Show inline comments
...
 
@@ -42 +42,6 @@ template =
 
 ; :Item:
 

	
 
[Empty]
 
template =
 

	
 
[Nonexistent]
tests/test_hooks.py
Show inline comments
...
 
@@ -7,3 +7,3 @@ import pytest
 
from import2ledger import hooks
 
from import2ledger.hooks import add_entity, default_date, filter_by_date
 
from import2ledger.hooks import add_entity, default_date, filter_by_date, ledger_entry
 

	
...
 
@@ -12,4 +12,10 @@ def test_load_all():
 
    positions = {hook: index for index, hook in enumerate(all_hooks)}
 
    assert positions[default_date.DefaultDateHook] < positions[add_entity.AddEntityHook]
 
    assert positions[add_entity.AddEntityHook] < positions[filter_by_date.FilterByDateHook]
 
    expected_order = [
 
        default_date.DefaultDateHook,
 
        add_entity.AddEntityHook,
 
        filter_by_date.FilterByDateHook,
 
        ledger_entry.LedgerEntryHook,
 
    ]
 
    actual_order = list(sorted(expected_order, key=positions.__getitem__))
 
    assert actual_order == expected_order
 

	
tests/test_templates.py
Show inline comments
...
 
@@ -2,4 +2,6 @@ import collections
 
import configparser
 
import contextlib
 
import datetime
 
import decimal
 
import io
 
import pathlib
...
 
@@ -8,2 +10,3 @@ import pytest
 
from import2ledger import errors, template
 
from import2ledger.hooks import ledger_entry
 

	
...
 
@@ -201 +204,46 @@ def test_bad_amount_expression(amount_expr):
 
        template.Template(" Income  " + amount_expr)
 

	
 
class Config:
 
    def __init__(self):
 
        self.stdout = io.StringIO()
 

	
 
    @contextlib.contextmanager
 
    def open_output_file(self):
 
        yield self.stdout
 

	
 
    def get_template(self, key):
 
        try:
 
            return template_from(key)
 
        except KeyError:
 
            raise errors.UserInputConfigurationError(
 
                "template not defined in test config", key)
 

	
 

	
 
def run_hook(entry_data):
 
    hook_config = Config()
 
    hook = ledger_entry.LedgerEntryHook(hook_config)
 
    assert hook.run(entry_data) is None
 
    stdout = hook_config.stdout.getvalue()
 
    return normalize_whitespace(stdout).splitlines()
 

	
 
def hook_vars(template_key, payee, amount):
 
    return template_vars(payee, amount, other_vars={'template': template_key})
 

	
 
def test_hook_renders_template():
 
    entry_data = hook_vars('Simplest', 'BB', '0.99')
 
    lines = run_hook(entry_data)
 
    assert lines == [
 
        "",
 
        "2015/03/14 BB",
 
        "  Accrued:Accounts Receivable  0.99 USD",
 
        "  Income:Donations  -0.99 USD",
 
    ]
 

	
 
def test_hook_handles_empty_template():
 
    entry_data = hook_vars('Empty', 'CC', 1)
 
    assert not run_hook(entry_data)
 

	
 
def test_hook_handles_template_undefined():
 
    entry_data = hook_vars('Nonexistent', 'DD', 1)
 
    assert not run_hook(entry_data)
 

	
0 comments (0 inline, 0 general)