Changeset - 73cd942a4606
[Not reviewed]
1 8 1
Brett Smith - 6 years ago 2018-04-03 20:17:23
brettcsmith@brettcsmith.org
hooks: Ledger output hook uses its own config section.
10 files changed with 120 insertions and 138 deletions:
0 comments (0 inline, 0 general)
import2ledger/config.py
Show inline comments
...
 
@@ -76,16 +76,2 @@ class Configuration:
 
        )
 
        out_args.add_argument(
 
            '--signed-currency', '--sign', metavar='CODE',
 
            action='append', dest='signed_currencies',
 
            help="Currency code to use currency sign for in Ledger entry amounts. "
 
            "Can be specified multiple times.",
 
        )
 
        out_args.add_argument(
 
            '--signed-currency-format', '--sign-format', '-S', metavar='FORMAT',
 
            help="Unicode number pattern to use for signed currencies in Ledger entry amounts",
 
        )
 
        out_args.add_argument(
 
            '--unsigned-currency-format', '--unsign-format', '-U', metavar='FORMAT',
 
            help="Unicode number pattern to use for unsigned currencies in Ledger entry amounts",
 
        )
 

	
...
 
@@ -99,6 +85,2 @@ class Configuration:
 
                'output_path': '-',
 
                'signed_currencies': ','.join(babel.numbers.get_territory_currencies(
 
                    self.LOCALE.territory, start_date=self.TODAY)),
 
                'signed_currency_format': '¤#,##0.###;¤-#,##0.###',
 
                'unsigned_currency_format': '#,##0.### ¤¤',
 
            })
import2ledger/hooks/ledger_entry.py
Show inline comments
...
 
@@ -279,2 +279,25 @@ class LedgerEntryHook:
 
        self.config = config
 
        self.config_section = config.get_section('Ledger output')
 
        if not any(value and not value.isspace()
 
                   for key, value in self.config_section.items()
 
                   if key.endswith(' ledger entry')):
 
            raise errors.NotConfiguredError("no Ledger entries in config", None)
 
        try:
 
            signed_currencies = (code.strip().upper() for code in
 
                                 self.config_section['signed currencies'].split(','))
 
        except KeyError:
 
            territory = self.config.LOCALE.territory
 
            if territory is None:
 
                signed_currencies = []
 
            else:
 
                signed_currencies = babel.numbers.get_territory_currencies(
 
                    territory, start_date=self.config.TODAY)
 
        self.template_kwargs = {
 
            'date_fmt': self.config_section['date format'],
 
            'signed_currencies': frozenset(signed_currencies),
 
            'signed_currency_fmt': self.config_section.get(
 
                'signed currency format', Template.SIGNED_CURRENCY_FMT),
 
            'unsigned_currency_fmt': self.config_section.get(
 
                'unsigned currency format', Template.UNSIGNED_CURRENCY_FMT),
 
        }
 

	
...
 
@@ -282,19 +305,8 @@ class LedgerEntryHook:
 
    @functools.lru_cache()
 
    def _load_template(config, section_name, config_key):
 
        section_config = config.get_section(section_name)
 
        try:
 
            template_s = section_config[config_key]
 
        except KeyError:
 
            raise errors.UserInputConfigurationError(
 
                "Ledger template not defined in [{}]".format(section_name),
 
                config_key,
 
            )
 
        return Template(
 
            template_s,
 
            date_fmt=section_config['date format'],
 
            signed_currencies=[code.strip().upper() for code in section_config['signed_currencies'].split(',')],
 
            signed_currency_fmt=section_config['signed_currency_format'],
 
            unsigned_currency_fmt=section_config['unsigned_currency_format'],
 
            template_name=config_key,
 
        )
 
    def _load_template(template_s, signed_currencies,
 
                       date_fmt,
 
                       signed_currency_fmt,
 
                       unsigned_currency_fmt,
 
                       template_name):
 
        return Template(**locals())
 

	
...
 
@@ -302,5 +314,5 @@ class LedgerEntryHook:
 
        try:
 
            template_key = entry_data['ledger template']
 
            template_name = entry_data['ledger template']
 
        except KeyError:
 
            template_key = '{} {} ledger entry'.format(
 
            template_name = '{} {} ledger entry'.format(
 
                strparse.rslice_words(entry_data['importer_module'], -1, '.', 1),
...
 
@@ -308,13 +320,7 @@ class LedgerEntryHook:
 
            )
 
        try:
 
            template = self._load_template(self.config, None, template_key)
 
        except errors.UserInputConfigurationError as error:
 
            if error.strerror.startswith('Ledger template not defined '):
 
                have_template = False
 
            else:
 
                raise
 
        else:
 
            have_template = not template.is_empty()
 
        if not have_template:
 
            logger.warning("no Ledger template defined as %r", template_key)
 
        template_s = self.config_section.get(template_name, '')
 
        template = self._load_template(
 
            template_s, template_name=template_name, **self.template_kwargs)
 
        if template.is_empty():
 
            logger.warning("no Ledger template defined as %r", template_name)
 
        else:
tests/__init__.py
Show inline comments
...
 
@@ -2,3 +2,5 @@ import collections
 
import configparser
 
import datetime
 
import decimal
 
import operator
 
import pathlib
...
 
@@ -6,2 +8,3 @@ import re
 

	
 
import babel.core
 
from import2ledger import __main__ as i2lmain
...
 
@@ -11,2 +14,3 @@ decimal.setcontext(i2lmain.decimal_context())
 
DATA_DIR = pathlib.Path(__file__).with_name('data')
 
START_DATE = datetime.date.today()
 

	
...
 
@@ -16,2 +20,5 @@ def normalize_whitespace(s):
 
class Config:
 
    LOCALE = babel.core.Locale('en_US_POSIX')
 
    TODAY = START_DATE
 

	
 
    def __init__(self, options_dict=None):
...
 
@@ -31 +38,3 @@ class Config:
 
            return self.config[configparser.DEFAULTSECT]
 

	
 
    __getitem__ = property(operator.attrgetter('config.__getitem__'))
tests/data/templates.ini
Show inline comments
 
deleted file
tests/data/templates.yml
Show inline comments
 
new file 100644
 
---
 
DEFAULT:
 
  date format: "%%Y-%%m-%%d"
 
Ledger output:
 
  signed currencies: USD, CAD
 
  signed currency format: "¤#,##0.###"
 
  Simplest ledger entry: |
 
    Accrued:Accounts Receivable  {amount}
 
    Income:Donations  -{amount}
 
  FiftyFifty ledger entry: |
 
    Accrued:Accounts Receivable  {amount}
 
    Income:Donations  -.5 * {amount}
 
    Income:Sales  -.5*{amount}
 
  Complex ledger entry: |
 
    ;Tag: Value
 
    ;TransactionID: {txid}
 
    Accrued:Accounts Receivable  {amount}
 
    ;Entity: Supplier
 
    Income:Donations:{program}    -.955*  {amount}
 
    ;Program: {program}
 
    ;Entity: {entity}
 
    Income:Donations:General     -.045  * {amount}
 
    ;Entity: {entity}
 
  Multivalue ledger entry: |
 
    Expenses:Taxes  {tax}
 
    ;TaxAuthority: IRS
 
    Accrued:Accounts Receivable  {amount} - {tax}
 
    Income:RBI         -.1*{amount}
 
    Income:Donations   -.9*{amount}
 
  Custom Payee ledger entry: |
 
    {custom_date}  {payee} - Custom
 
    Accrued:Accounts Receivable  {amount}
 
    Income:Donations  -{amount}
 
  Multisplit ledger entry: |
 
    Assets:Cash  {amount}
 
    Income:Sales  -{amount} + {item_sales}
 
    ; :NonItem:
 
    Income:Sales  -{item_sales}
 
    ; :Item:
 
  Empty ledger entry: ""
tests/data/test_main.ini
Show inline comments
...
 
@@ -3,5 +3,5 @@ date format = %%Y/%%m/%%d
 
loglevel = critical
 
signed_currencies = USD
 
signed currencies = USD
 

	
 
[One]
 
[Ledger output]
 
patreon cardfees ledger entry =
tests/test_config.py
Show inline comments
...
 
@@ -7,4 +7,2 @@ import pathlib
 

	
 
START_DATE = datetime.date.today()
 

	
 
from unittest import mock
...
 
@@ -14,3 +12,3 @@ from import2ledger import config, errors, strparse
 

	
 
from . import DATA_DIR
 
from . import DATA_DIR, START_DATE
 

	
tests/test_hooks.py
Show inline comments
...
 
@@ -19,3 +19,5 @@ def test_load_all():
 
    config_dict = date_hooks.DateHookTestBase().new_config()
 
    return _run_order_test(Config(config_dict), [
 
    config = Config(config_dict)
 
    config['DEFAULT']['test ledger entry'] = 'Income  {amount}'
 
    return _run_order_test(config, [
 
        default_date.DefaultDateHook,
tests/test_hooks_ledger_entry.py
Show inline comments
 
import collections
 
import configparser
 
import contextlib
...
 
@@ -9,2 +8,3 @@ import pathlib
 
import pytest
 
import yaml
 
from import2ledger import errors
...
 
@@ -12,12 +12,23 @@ from import2ledger.hooks import ledger_entry
 

	
 
from . import DATA_DIR, normalize_whitespace
 
from . import DATA_DIR, normalize_whitespace, Config as BaseConfig
 

	
 
DATE = datetime.date(2015, 3, 14)
 
with pathlib.Path(DATA_DIR, 'templates.yml').open() as conffile:
 
    _config_dict = yaml.load(conffile)
 

	
 
class Config(BaseConfig):
 
    def __init__(self, options_dict=_config_dict):
 
        super().__init__(options_dict)
 
        self.stdout = io.StringIO()
 

	
 
config = configparser.ConfigParser(comment_prefixes='#')
 
with pathlib.Path(DATA_DIR, 'templates.ini').open() as conffile:
 
    config.read_file(conffile)
 
    @contextlib.contextmanager
 
    def open_output_file(self):
 
        yield self.stdout
 

	
 

	
 
DATE = datetime.date(2015, 3, 14)
 

	
 
def template_from(section_name, *args, **kwargs):
 
    return ledger_entry.Template(config[section_name]['template'], *args, **kwargs)
 
def template_from(template_name, *args, **kwargs):
 
    section = Config().get_section('Ledger output')
 
    template_s = section['{} ledger entry'.format(template_name)]
 
    return ledger_entry.Template(template_s, *args, **kwargs)
 

	
...
 
@@ -29,3 +40,2 @@ def template_vars(payee, amount, currency='USD', date=DATE, other_vars=None):
 
        'payee': payee,
 
        'ledger template': 'template',
 
    }
...
 
@@ -206,20 +216,8 @@ def test_bad_amount_expression(amount_expr):
 

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

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

	
 
    def get_section(self, name=None):
 
        return config[self.section_name]
 

	
 

	
 
def run_hook(entry_data, config_section):
 
    hook_config = Config(config_section)
 
    hook = ledger_entry.LedgerEntryHook(hook_config)
 
def run_hook(entry_data, template_name):
 
    config = Config()
 
    entry_data['ledger template'] = '{} ledger entry'.format(template_name)
 
    hook = ledger_entry.LedgerEntryHook(config)
 
    assert hook.run(entry_data) is None
 
    stdout = hook_config.stdout.getvalue()
 
    stdout = config.stdout.getvalue()
 
    return normalize_whitespace(stdout).splitlines()
...
 
@@ -244 +242,5 @@ def test_hook_handles_template_undefined():
 

	
 
def test_unconfigured():
 
    config = Config({})
 
    with pytest.raises(errors.NotConfiguredError):
 
        ledger_entry.LedgerEntryHook(config)
tests/test_main.py
Show inline comments
...
 
@@ -56,6 +56,3 @@ def test_fees_import():
 
    source_path = pathlib.Path(DATA_DIR, 'PatreonEarnings.csv')
 
    arglist = ARGLIST + [
 
        '-c', 'One',
 
        source_path.as_posix(),
 
    ]
 
    arglist = ARGLIST + [source_path.as_posix()]
 
    exitcode, stdout, _ = run_main(arglist)
...
 
@@ -69,3 +66,2 @@ def test_date_range_import():
 
    arglist = ARGLIST + [
 
        '-c', 'One',
 
        '--date-range', '2017/10/01-',
0 comments (0 inline, 0 general)