Changeset - fff9e37bf83e
[Not reviewed]
0 3 0
Brett Smith - 4 years ago 2020-07-16 14:11:39
brettcsmith@brettcsmith.org
data: Add Account.is_account and Account.load_options_map.

These work in concert to distinguish account names from other
colon-separated strings.
3 files changed with 67 insertions and 0 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/data.py
Show inline comments
...
 
@@ -30,6 +30,7 @@ from beancount.core import amount as bc_amount
 
from beancount.core import convert as bc_convert
 
from beancount.core import data as bc_data
 
from beancount.core import position as bc_position
 
from beancount.parser import options as bc_options
 

	
 
from typing import (
 
    cast,
...
 
@@ -40,6 +41,7 @@ from typing import (
 
    Iterator,
 
    MutableMapping,
 
    Optional,
 
    Pattern,
 
    Sequence,
 
    TypeVar,
 
    Union,
...
 
@@ -53,6 +55,7 @@ from .beancount_types import (
 
    MetaKey,
 
    MetaValue,
 
    Open,
 
    OptionsMap,
 
    Posting as BasePosting,
 
    Transaction,
 
)
...
 
@@ -153,8 +156,19 @@ class Account(str):
 
    """
 
    __slots__ = ()
 

	
 
    ACCOUNT_RE: Pattern
 
    SEP = bc_account.sep
 
    _meta_map: MutableMapping[str, AccountMeta] = {}
 
    _options_map: OptionsMap
 

	
 
    @classmethod
 
    def load_options_map(cls, options_map: OptionsMap) -> None:
 
        cls._options_map = options_map
 
        roots: Sequence[str] = bc_options.get_account_types(options_map)
 
        cls.ACCOUNT_RE = re.compile(
 
            r'^(?:{})(?:{}[A-Z0-9][-A-Za-z0-9]*)+$'.format(
 
                '|'.join(roots), cls.SEP,
 
            ))
 

	
 
    @classmethod
 
    def load_opening(cls, opening: Open) -> None:
...
 
@@ -178,6 +192,10 @@ class Account(str):
 
            elif isinstance(entry, bc_data.Close):
 
                cls.load_closing(entry)  # type:ignore[arg-type]
 

	
 
    @classmethod
 
    def is_account(cls, s: str) -> bool:
 
        return cls.ACCOUNT_RE.fullmatch(s) is not None
 

	
 
    @property
 
    def meta(self) -> AccountMeta:
 
        return self._meta_map[self]
...
 
@@ -286,6 +304,7 @@ class Account(str):
 
            return self
 
        else:
 
            return self[:stop]
 
Account.load_options_map(bc_options.OPTIONS_DEFAULTS)
 

	
 

	
 
class Amount(bc_amount.Amount):
tests/test_data_account.py
Show inline comments
...
 
@@ -21,6 +21,7 @@ from . import testutil
 
from datetime import date as Date
 

	
 
from beancount.core.data import Open, Close, Booking
 
from beancount.parser import options as bc_options
 

	
 
from conservancy_beancount import data
 

	
...
 
@@ -267,3 +268,48 @@ def test_load_openings_and_closings(clean_account_meta):
 
    check_account_meta('Income:Donations', entries[0])
 
    check_account_meta('Income:Other', entries[1])
 
    check_account_meta('Assets:Checking', entries[2], entries[-1])
 

	
 
@pytest.mark.parametrize('account_s', [
 
    'Assets:Bank:Checking',
 
    'Equity:Funds:Restricted',
 
    'Expenses:Other',
 
    'Income:Donations',
 
    'Liabilities:CreditCard:Visa',
 
])
 
def test_is_account(account_s):
 
    assert data.Account.is_account(account_s)
 

	
 
@pytest.mark.parametrize('account_s', [
 
    'Assets:Bank:12-345',
 
    'Equity:Funds:Restricted',
 
    'Expenses:Other',
 
    'Income:Donations',
 
    'Liabilities:CreditCard:Visa0123',
 
])
 
def test_is_account(clean_account_meta, account_s):
 
    assert data.Account.is_account(account_s)
 

	
 
@pytest.mark.parametrize('account_s', [
 
    'Assets:checking',
 
    'Assets::Cash',
 
    'Equity',
 
    'Liabilities:Credit Card',
 
    'income:Donations',
 
    'Expenses:Banking_Fees',
 
    'Revenue:Grants',
 
])
 
def test_is_not_account(clean_account_meta, account_s):
 
    assert not data.Account.is_account(account_s)
 

	
 
@pytest.mark.parametrize('account_s,expected', [
 
    ('Revenue:Donations', True),
 
    ('Costs:Other', True),
 
    ('Income:Donations', False),
 
    ('Expenses:Other', False),
 
])
 
def test_is_account_respects_configured_roots(clean_account_meta, account_s, expected):
 
    config = bc_options.OPTIONS_DEFAULTS.copy()
 
    config['name_expenses'] = 'Costs'
 
    config['name_income'] = 'Revenue'
 
    data.Account.load_options_map(config)
 
    assert data.Account.is_account(account_s) == expected
tests/testutil.py
Show inline comments
...
 
@@ -21,6 +21,7 @@ import re
 
import beancount.core.amount as bc_amount
 
import beancount.core.data as bc_data
 
import beancount.loader as bc_loader
 
import beancount.parser.options as bc_options
 

	
 
import odf.element
 
import odf.opendocument
...
 
@@ -43,6 +44,7 @@ TESTS_DIR = Path(__file__).parent
 
# it with different scopes. Typical usage looks like:
 
#   clean_account_meta = pytest.fixture([options])(testutil.clean_account_meta)
 
def clean_account_meta():
 
    data.Account.load_options_map(bc_options.OPTIONS_DEFAULTS)
 
    data.Account._meta_map.clear()
 

	
 
def _ods_cell_value_type(cell):
0 comments (0 inline, 0 general)