Changeset - ab9c65d20dc5
[Not reviewed]
0 5 1
Brett Smith - 7 years ago 2017-10-22 20:10:17
brettcsmith@brettcsmith.org
import2ledger: Support only importing entries in a date range.
6 files changed with 127 insertions and 14 deletions:
0 comments (0 inline, 0 general)
import2ledger/__main__.py
Show inline comments
...
 
@@ -36,3 +36,6 @@ class FileImporter:
 
                        hook.run(entry_data)
 
                    print(template.render(**entry_data), file=out_file, end='')
 
                        if not entry_data:
 
                            break
 
                    else:
 
                        print(template.render(**entry_data), file=out_file, end='')
 

	
import2ledger/config.py
Show inline comments
...
 
@@ -21,2 +21,3 @@ class Configuration:
 
        'date_format': '%%Y/%%m/%%d',
 
        'date_range': '-',
 
        'loglevel': 'WARNING',
...
 
@@ -69,7 +70,2 @@ class Configuration:
 
        )
 
        parser.add_argument(
 
            '--loglevel', '-L', metavar='LEVEL',
 
            choices=['debug', 'info', 'warning', 'error', 'critical'],
 
            help="Log messages at this level and above. Default WARNING.",
 
        )
 
        out_args.add_argument(
...
 
@@ -84,2 +80,13 @@ class Configuration:
 
        )
 
        out_args.add_argument(
 
            '--date-range', metavar='DATE-DATE',
 
            help="Only import entries in this date range, inclusive. "
 
            "Write dates in your configured date format. "
 
            "You can omit either side of the range.",
 
        )
 
        out_args.add_argument(
 
            '--loglevel', '-L', metavar='LEVEL',
 
            choices=['debug', 'info', 'warning', 'error', 'critical'],
 
            help="Log messages at this level and above. Default WARNING.",
 
        )
 
        out_args.add_argument(
...
 
@@ -127,5 +134,27 @@ class Configuration:
 

	
 
    def _parse_date_range(self, section_name):
 
        section = self.conffile[section_name]
 
        range_s = section['date_range']
 
        date_fmt = section['date_format']
 
        if not range_s:
 
            range_s = '-'
 
        if range_s.startswith('-'):
 
            start_s = ''
 
            end_s = range_s[1:]
 
        elif range_s.endswith('-'):
 
            start_s = range_s[:-1]
 
            end_s = ''
 
        else:
 
            range_parts = range_s.split('-')
 
            mid_index = len(range_parts) // 2
 
            start_s = '-'.join(range_parts[:mid_index])
 
            end_s = '-'.join(range_parts[mid_index:])
 
        start_d = self._strpdate(start_s, date_fmt) if start_s else datetime.date.min
 
        end_d = self._strpdate(end_s, date_fmt) if end_s else datetime.date.max
 
        return range(start_d.toordinal(), end_d.toordinal() + 1)
 

	
 
    def finalize(self):
 
        default_secname = self.conffile.default_section
 
        if self.args.use_config is None:
 
            self.args.use_config = self.conffile.default_section
 
            self.args.use_config = default_secname
 
        elif not self.conffile.has_section(self.args.use_config):
...
 
@@ -134,3 +163,3 @@ class Configuration:
 

	
 
        defaults = self.conffile[self.conffile.default_section]
 
        defaults = self.conffile[default_secname]
 
        for key in self.CONFIG_DEFAULTS:
...
 
@@ -142,3 +171,3 @@ class Configuration:
 
            else:
 
                defaults[key] = value
 
                defaults[key] = value.replace('%', '%%')
 

	
...
 
@@ -154,3 +183,6 @@ class Configuration:
 
                      for secname in self.conffile}
 
        self.dates[self.conffile.default_section] = default_date
 
        self.dates[default_secname] = default_date
 
        self.date_ranges = {secname: self._parse_date_range(secname)
 
                            for secname in self.conffile}
 
        self.date_ranges[default_secname] = self._parse_date_range(default_secname)
 

	
...
 
@@ -170,3 +202,3 @@ class Configuration:
 

	
 
    def get_default_date(self, section_name=None):
 
    def _get_from_dict(self, confdict, section_name=None):
 
        if section_name is None:
...
 
@@ -174,5 +206,11 @@ class Configuration:
 
        try:
 
            return self.dates[section_name]
 
            return confdict[section_name]
 
        except KeyError:
 
            return self.dates[self.conffile.default_section]
 
            return confdict[self.conffile.default_section]
 

	
 
    def date_in_want_range(self, date, section_name=None):
 
        return date.toordinal() in self._get_from_dict(self.date_ranges, section_name)
 

	
 
    def get_default_date(self, section_name=None):
 
        return self._get_from_dict(self.dates, section_name)
 

	
import2ledger/hooks/filter_by_date.py
Show inline comments
 
new file 100644
 
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()
tests/test_config.py
Show inline comments
...
 
@@ -2,2 +2,3 @@ import contextlib
 
import datetime
 
import itertools
 
import logging
...
 
@@ -65,2 +66,18 @@ def test_output_path_from_section():
 

	
 
@pytest.mark.parametrize('range_s,date_fmt', [
 
    (range_s.replace('/', sep), sep.join(['%Y', '%m', '%d']))
 
    for range_s, sep in itertools.product([
 
            '-',
 
            '2016/06/01-2016/06/30',
 
            '2016/06/01-',
 
            '-2016/06/30',
 
            ], '/-')
 
])
 
def test_date_in_want_range(range_s, date_fmt):
 
    config = config_from_file(os.devnull, ['--date-range=' + range_s, '--date-format', date_fmt])
 
    assert config.date_in_want_range(datetime.date(2016, 5, 31)) == range_s.startswith('-')
 
    assert config.date_in_want_range(datetime.date(2016, 6, 1))
 
    assert config.date_in_want_range(datetime.date(2016, 6, 30))
 
    assert config.date_in_want_range(datetime.date(2016, 7, 1)) == range_s.endswith('-')
 

	
 
@pytest.mark.parametrize('arglist,expect_date', [
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
 
from import2ledger.hooks import add_entity, default_date, filter_by_date
 

	
...
 
@@ -32,2 +32,32 @@ def test_add_entity(payee, expected):
 

	
 
class DateRangeConfig:
 
    def __init__(self, start_date=None, end_date=None):
 
        self.start_date = start_date
 
        self.end_date = end_date
 

	
 
    def date_in_want_range(self, date):
 
        return (
 
            ((self.start_date is None) or (date >= self.start_date))
 
            and ((self.end_date is None) or (date <= self.end_date))
 
        )
 

	
 

	
 
@pytest.mark.parametrize('entry_date,start_date,end_date,allowed', [
 
    (datetime.date(2016, 5, 10), datetime.date(2016, 1, 1), datetime.date(2016, 12, 31), True),
 
    (datetime.date(2016, 1, 1), datetime.date(2016, 1, 1), datetime.date(2016, 12, 31), True),
 
    (datetime.date(2016, 12, 31), datetime.date(2016, 1, 1), datetime.date(2016, 12, 31), True),
 
    (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
 

	
 
class DefaultDateConfig:
tests/test_main.py
Show inline comments
...
 
@@ -51 +51,14 @@ def test_fees_import():
 
    assert actual == expected_entries('test_main_fees_import.ledger')
 

	
 
def test_date_range_import():
 
    arglist = ARGLIST + [
 
        '-c', 'One',
 
        '--date-range', '2017/10/01-',
 
        pathlib.Path(DATA_DIR, 'PatreonEarnings.csv').as_posix(),
 
    ]
 
    exitcode, stdout, _ = run_main(arglist)
 
    assert exitcode == 0
 
    actual = entries2set(stdout)
 
    expected = {entry for entry in expected_entries('test_main_fees_import.ledger')
 
                if entry.startswith('2017/10/')}
 
    assert actual == expected
0 comments (0 inline, 0 general)