diff --git a/conservancy_beancount/reports/core.py b/conservancy_beancount/reports/core.py index ec71f6c5e55fc6218fae873b2f4781c53fd05792..1d271a707bb1e7bf98a796416bc55845b5c04020 100644 --- a/conservancy_beancount/reports/core.py +++ b/conservancy_beancount/reports/core.py @@ -29,9 +29,14 @@ from typing import ( Mapping, Optional, Sequence, + Set, Tuple, Union, ) +from ..beancount_types import ( + MetaKey, + MetaValue, +) class Balance(Mapping[str, data.Amount]): """A collection of amounts mapped by currency @@ -134,3 +139,9 @@ class RelatedPostings(Sequence[data.Posting]): return balance except NameError: return Balance() + + def meta_values(self, + key: MetaKey, + default: Optional[MetaValue]=None, + ) -> Set[Optional[MetaValue]]: + return {post.meta.get(key, default) for post in self} diff --git a/tests/test_reports_related_postings.py b/tests/test_reports_related_postings.py index a9186196e17892a2df0d0750d5de0bb7227e40ac..f0bb60bf7ddb23e61732c7e4d47a9fa419f8c354 100644 --- a/tests/test_reports_related_postings.py +++ b/tests/test_reports_related_postings.py @@ -106,3 +106,64 @@ def test_iter_with_balance_credit_card(credit_card_cycle): def test_iter_with_balance_two_acccruals(two_accruals_three_payments): check_iter_with_balance(two_accruals_three_payments) + +def test_meta_values_empty(): + related = core.RelatedPostings() + assert related.meta_values('key') == set() + +def test_meta_values_no_match(): + related = core.RelatedPostings() + related.add(testutil.Posting('Income:Donations', -1, metakey='metavalue')) + assert related.meta_values('key') == {None} + +def test_meta_values_no_match_default_given(): + related = core.RelatedPostings() + related.add(testutil.Posting('Income:Donations', -1, metakey='metavalue')) + assert related.meta_values('key', '') == {''} + +def test_meta_values_one_match(): + related = core.RelatedPostings() + related.add(testutil.Posting('Income:Donations', -1, key='metavalue')) + assert related.meta_values('key') == {'metavalue'} + +def test_meta_values_some_match(): + related = core.RelatedPostings() + related.add(testutil.Posting('Income:Donations', -1, key='1')) + related.add(testutil.Posting('Income:Donations', -2, metakey='2')) + assert related.meta_values('key') == {'1', None} + +def test_meta_values_some_match_default_given(): + related = core.RelatedPostings() + related.add(testutil.Posting('Income:Donations', -1, key='1')) + related.add(testutil.Posting('Income:Donations', -2, metakey='2')) + assert related.meta_values('key', '') == {'1', ''} + +def test_meta_values_all_match(): + related = core.RelatedPostings() + related.add(testutil.Posting('Income:Donations', -1, key='1')) + related.add(testutil.Posting('Income:Donations', -2, key='2')) + assert related.meta_values('key') == {'1', '2'} + +def test_meta_values_all_match_one_value(): + related = core.RelatedPostings() + related.add(testutil.Posting('Income:Donations', -1, key='1')) + related.add(testutil.Posting('Income:Donations', -2, key='1')) + assert related.meta_values('key') == {'1'} + +def test_meta_values_all_match_default_given(): + related = core.RelatedPostings() + related.add(testutil.Posting('Income:Donations', -1, key='1')) + related.add(testutil.Posting('Income:Donations', -2, key='2')) + assert related.meta_values('key', '') == {'1', '2'} + +def test_meta_values_many_types(): + expected = { + datetime.date(2020, 4, 1), + Decimal(42), + testutil.Amount(5), + 'rt:42', + } + related = core.RelatedPostings() + for index, value in enumerate(expected): + related.add(testutil.Posting('Income:Donations', -index, key=value)) + assert related.meta_values('key') == expected