Changeset - 69f597a47c53
[Not reviewed]
0 2 0
Brett Smith - 4 years ago 2020-10-24 15:43:46
brettcsmith@brettcsmith.org
rewrite: Support ``metakey in value`` conditions.

For now, works exactly like ``.account in value``.
2 files changed with 49 insertions and 2 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/reports/rewrite.py
Show inline comments
...
 
@@ -84,2 +84,9 @@ Conditions can always use Python's basic comparison operators:
 
                   respective subaccounts.
 
  ---------------- -------------------------------------------------------
 
  ``metakey in``   Works analogously for metadata values that are
 
                   structured as a colon-separated hierarchy like account
 
                   names. The operand is parsed as a space-separated list
 
                   of parts of the hierarchy. The condition matches when
 
                   the posting's ``metakey`` metadata is under any of
 
                   those parts of the hierarchy.
 
  ================ =======================================================
...
 
@@ -143,2 +150,3 @@ accounting equation, Equity = Assets - Liabilities.
 
import abc
 
import collections
 
import datetime
...
 
@@ -146,2 +154,3 @@ import decimal
 
import enum
 
import functools
 
import logging
...
 
@@ -292,5 +301,14 @@ class DateTest(Tester[datetime.date]):
 
class MetadataTest(Tester[Optional[MetaValue]]):
 
    _GETTERS: Mapping[MetaKey, Callable[[str], data.Account]] = \
 
        collections.defaultdict(lambda: functools.lru_cache()(data.Account))
 
    # We use this class variable to share one cache per metadata key being
 
    # tested.
 

	
 
    def __init__(self, key: MetaKey, operator: str, operand: str) -> None:
 
        super().__init__(operator, operand)
 
        self.key = key
 
        if operator == 'in':
 
            self._get_hierarchy = self._GETTERS[key]
 
            self.under_args = operand.split()
 
        else:
 
            super().__init__(operator, operand)
 

	
...
 
@@ -303,2 +321,14 @@ class MetadataTest(Tester[Optional[MetaValue]]):
 

	
 
    def __call__(self, post: data.Posting) -> bool:
 
        try:
 
            self.under_args
 
        except AttributeError:
 
            return super().__call__(post)
 
        else:
 
            meta_value = self.post_get(post)
 
            if isinstance(meta_value, str):
 
                return self._get_hierarchy(meta_value).is_under(*self.under_args) is not None
 
            else:
 
                return False
 

	
 

	
tests/test_reports_rewrite.py
Show inline comments
...
 
@@ -78,2 +78,20 @@ def test_metadata_condition(value, operator):
 

	
 
@pytest.mark.parametrize('value,expected', [
 
    ('Root', False),
 
    ('Root:Branch', True),
 
    ('Root:Branch:Leaf', True),
 
    ('Branch', False),
 
    ('RootBranch:Leaf', False),
 
    (None, False),
 
])
 
def test_metadata_in_condition(value, expected):
 
    key = 'testkey'
 
    meta = {} if value is None else {key: value}
 
    txn = testutil.Transaction(postings=[
 
        ('Income:Other', -5, meta),
 
    ])
 
    post, = data.Posting.from_txn(txn)
 
    tester = rewrite.MetadataTest(key, 'in', 'Root:Branch')
 
    assert tester(post) == expected
 

	
 
@pytest.mark.parametrize('value', ['4.5', '4.75', '5'])
...
 
@@ -120,3 +138,2 @@ def test_parse_good_condition(subject, operator, operand):
 
    '.number in 16',  # Bad operator
 
    'testkey in foo',  # Bad operator
 
    'units.number == 5',  # Bad subject (syntax)
0 comments (0 inline, 0 general)