Changeset - b41a7a99af17
[Not reviewed]
0 4 0
Brett Smith - 6 years ago 2017-12-19 14:13:44
brettcsmith@brettcsmith.org
hooks: Filter entries by setting entry_data['_hook_cancel'] to True.

The current method only works for plain dicts and other simple mappings.
Mapping that may still contain items after .clear(), like ChainMap, can't
rely on the old method.
4 files changed with 6 insertions and 4 deletions:
0 comments (0 inline, 0 general)
CODE.rst
Show inline comments
...
 
@@ -42,25 +42,25 @@ Class method ``can_handle(source_file)``
 
Class attribute ``TEMPLATE_KEY``
 
  A string with the full key to load the corresponding template from the user's configuration (e.g., ``'template patreon income'``).
 

	
 
Hooks
 
~~~~~
 

	
 
Hooks make arbitrary transformations to entry data dicts.  Every entry data dict generated by an importer is run through every hook before being output.
 

	
 
``__init__(config)``
 
  Initializes the hook with the user's configuration.
 

	
 
``run(entry_data)``
 
  This method makes the hook's transformations to the entry data dict, if any.  If this method clears the entry data dict, that entry will not be output.
 
  This method makes the hook's transformations to the entry data dict, if any.  If this method sets ``entry_data['_hook_cancel']`` to a truthy value, that entry will not be output.
 

	
 
Templates
 
~~~~~~~~~
 

	
 
Templates receive entry data dicts and format them into final output entries.
 

	
 
``__init__(template_str)``
 
  Initializes the template from a single string, as read from the user's configuration.
 

	
 
``render(entry_data)``
 
  Returns a string with the output entry, using the given entry data.
 

	
import2ledger/__main__.py
Show inline comments
...
 
@@ -32,29 +32,31 @@ class FileImporter:
 
        if not importers:
 
            raise errors.UserInputFileError("no importers available", in_file.name)
 
        with contextlib.ExitStack() as exit_stack:
 
            output_path = self.config.get_output_path()
 
            if output_path is None:
 
                out_file = self.stdout
 
            else:
 
                out_file = exit_stack.enter_context(output_path.open('a'))
 
            for importer, template in importers:
 
                default_date = self.config.get_default_date()
 
                in_file.seek(0)
 
                for entry_data in importer(in_file):
 
                    entry_data['_hook_cancel'] = False
 
                    for hook in self.hooks:
 
                        hook.run(entry_data)
 
                        if not entry_data:
 
                        if entry_data['_hook_cancel']:
 
                            break
 
                    else:
 
                        del entry_data['_hook_cancel']
 
                        print(template.render(**entry_data), file=out_file, end='')
 

	
 
    def import_path(self, in_path):
 
        if in_path is None:
 
            raise errors.UserInputFileError("only seekable files are supported", '<stdin>')
 
        with in_path.open(errors='replace') as in_file:
 
            if not in_file.seekable():
 
                raise errors.UserInputFileError("only seekable files are supported", in_path)
 
            return self.import_file(in_file)
 

	
 
    def import_paths(self, path_seq):
 
        for in_path in path_seq:
import2ledger/hooks/filter_by_date.py
Show inline comments
 
class FilterByDateHook:
 
    def __init__(self, config):
 
        self.config = config
 

	
 
    def run(self, entry_data):
 
        try:
 
            date = entry_data['date']
 
        except KeyError:
 
            pass
 
        else:
 
            if not self.config.date_in_want_range(date):
 
                entry_data.clear()
 
                entry_data['_hook_cancel'] = True
tests/test_hooks.py
Show inline comments
...
 
@@ -49,25 +49,25 @@ class DateRangeConfig:
 
    (datetime.date(2016, 1, 1), datetime.date(2016, 1, 1), None, True),
 
    (datetime.date(2016, 12, 31), None, datetime.date(2016, 12, 31), True),
 
    (datetime.date(1999, 1, 2), None, None, True),
 
    (datetime.date(2016, 1, 25), datetime.date(2016, 2, 1), datetime.date(2016, 12, 31), False),
 
    (datetime.date(2016, 12, 26), datetime.date(2016, 1, 1), datetime.date(2016, 11, 30), False),
 
    (datetime.date(2016, 1, 31), datetime.date(2016, 2, 1), None, False),
 
    (datetime.date(2016, 12, 1), None, datetime.date(2016, 11, 30), False),
 
])
 
def test_filter_by_date(entry_date, start_date, end_date, allowed):
 
    entry_data = {'date': entry_date}
 
    hook = filter_by_date.FilterByDateHook(DateRangeConfig(start_date, end_date))
 
    hook.run(entry_data)
 
    assert bool(entry_data) == allowed
 
    assert entry_data.get('_hook_cancel', False) == (not allowed)
 

	
 
class DefaultDateConfig:
 
    ONE_DAY = datetime.timedelta(days=1)
 

	
 
    def __init__(self, start_date=None):
 
        if start_date is None:
 
            start_date = datetime.date(2016, 3, 5)
 
        self.date = start_date - self.ONE_DAY
 

	
 
    def get_default_date(self, section_name=None):
 
        self.date += self.ONE_DAY
 
        return self.date
0 comments (0 inline, 0 general)