From bcdbd4bf81e8e2bdf30eb0cdcea9ccf2c2c3f0e8 Mon Sep 17 00:00:00 2001 From: Red S Date: Mon, 26 Dec 2022 01:21:16 -0800 Subject: [PATCH] chore: enable fava context and filters. Also upstream favaledger updates - Includes changes for upstream deprecation of favaledger in fava 1.22 Changes picked from: https://github.com/redstreet/fava_investor/commit/a03223affaed4953425f26ccd35c5cd68caf3520 https://github.com/redstreet/fava_investor/commit/757fc2035f7181492ed37869c17e7b196c3d75d6 https://github.com/redstreet/fava_investor/commit/7381a341c59eafb93124af99437d6787b6823e45 --- CHANGELOG.md | 17 +++------- fava_miler/__init__.py | 2 +- fava_miler/common/beancountinvestorapi.py | 28 ++++++++++++++-- fava_miler/common/favainvestorapi.py | 40 +++++++++++++++-------- requirements.txt | 2 +- setup.py | 2 +- 6 files changed, 59 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e929a4..9c063d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,11 @@ # Changelog - -## (unreleased) - - ### Other -- Dep conflicts. [Red S] -- Requirements/pigar. [Red S] -- Moved to click; added fava-miler as console-script. [Red S] -- Flake. [Red S] -- Pythonpackage workflow. [Red S] -- Pythonpackage.yml upgrade python version. [Red S] -- Add pythonpackage.yml. [Red S] -- Flake8 + requirements.txt. [Red S] -- Replace 9999-12-31 with the string no expiry. [Red S] +- feat: moved to click; added fava-miler as console-script. [Red S] +- feat: Replace 9999-12-31 with the string no expiry. [Red S] +- chore: enable fava context and filters. Also upstream favaledger updates. [Red S] + ## 0.2.3 (2022-04-03) diff --git a/fava_miler/__init__.py b/fava_miler/__init__.py index afaca7f..874b191 100644 --- a/fava_miler/__init__.py +++ b/fava_miler/__init__.py @@ -11,5 +11,5 @@ class Miler(FavaExtensionBase): # pragma: no cover def build_miles_tables(self): """Build main table""" - accapi = FavaInvestorAPI(self.ledger) + accapi = FavaInvestorAPI() return libmiler.get_miles_expirations(accapi, self.config) diff --git a/fava_miler/common/beancountinvestorapi.py b/fava_miler/common/beancountinvestorapi.py index 59f81a2..7d36ec4 100644 --- a/fava_miler/common/beancountinvestorapi.py +++ b/fava_miler/common/beancountinvestorapi.py @@ -6,17 +6,25 @@ from beancount.core import realization from beancount.query import query from beancount.core.data import Open +from beancount.core.data import Custom +import ast class AccAPI: def __init__(self, beancount_file, options): self.entries, _, self.options_map = loader.load_file(beancount_file) self.options = options - self.begin = self.end = None # Only used in fava + + def end_date(self): + return None # Only used in fava (UI selection context) def build_price_map(self): return prices.build_price_map(self.entries) + def build_filtered_price_map(self, pos, base_currency): + """Ignore filtering since we are not in fava. Return all prices""" + return prices.build_price_map(self.entries) + def get_commodity_directives(self): return getters.get_commodity_directives(self.entries) @@ -47,7 +55,7 @@ def get_account_open_close(self): def get_account_open(self): oc = getters.get_account_open_close(self.entries) - opens = [e for e in oc if isinstance(e, Open)] + opens = [v for k, v in oc.items() if isinstance(v[0], Open)] return opens # def cost_or_value(self, node, date, include_children): @@ -55,3 +63,19 @@ def get_account_open(self): # if include_children: # return cost_or_value(node.balance_children, date) # return cost_or_value(node.balance, date) + + def get_custom_config(self, module_name): + """Get fava config for the given plugin that can then be used on the command line""" + _extension_entries = [e for e in self.entries + if isinstance(e, Custom) and e.type == 'fava-extension'] + config_meta = {entry.values[0].value: + (entry.values[1].value if (len(entry.values) == 2) else None) + for entry in _extension_entries} + + all_configs = {k: ast.literal_eval(v) for k, v in config_meta.items() if 'fava_investor' in k} + + # extract conig for just this module: + module_config = [v[module_name] for k, v in all_configs.items() if module_name in v] + if module_config: + return module_config[0] + return {} diff --git a/fava_miler/common/favainvestorapi.py b/fava_miler/common/favainvestorapi.py index 4d0bfb1..849787e 100644 --- a/fava_miler/common/favainvestorapi.py +++ b/fava_miler/common/favainvestorapi.py @@ -1,40 +1,52 @@ from beancount.core import getters from fava.template_filters import cost_or_value +from fava import __version__ as fava_version +from packaging import version +from fava.context import g class FavaInvestorAPI: - def __init__(self, ledger, begin=None, end=None): - # TODO: are begin/end needed? - self.ledger = ledger - self.begin = begin - self.end = end - self.entries = self.ledger.root_tree - def build_price_map(self): - return self.ledger.price_map + return g.ledger.price_map + + def build_filtered_price_map(self, pcur, base_currency): + """pcur and base_currency are currency strings""" + return {(pcur, base_currency): g.filtered.prices(pcur, base_currency)} + + def end_date(self): + return g.filtered.end_date def get_commodity_directives(self): - return getters.get_commodity_directives(self.ledger.entries) + return {entry.currency: entry for entry in g.filtered.ledger.all_entries_by_type.Commodity} def realize(self): - return self.ledger.root_account + return g.filtered.root_account def root_tree(self): - return self.ledger.root_tree + return g.filtered.root_tree def query_func(self, sql): - contents, rtypes, rrows = self.ledger.query_shell.execute_query(sql) + # Based on the fava version, determine if we need to add a new + # positional argument to fava's execute_query() + if version.parse(fava_version) >= version.parse("1.22"): + _, rtypes, rrows = g.ledger.query_shell.execute_query(g.filtered.entries, sql) + else: + _, rtypes, rrows = g.ledger.query_shell.execute_query(sql) return rtypes, rrows def get_operating_currencies(self): - return self.ledger.options["operating_currency"] # TODO: error check + return g.ledger.options["operating_currency"] # TODO: error check def get_operating_currencies_regex(self): currencies = self.get_operating_currencies() return '(' + '|'.join(currencies) + ')' def get_account_open_close(self): - return getters.get_account_open_close(self.ledger.entries) + return getters.get_account_open_close(g.filtered.entries) + + def get_account_open(self): + # TODO: below is probably fava only, and needs to be made beancount friendly + return g.ledger.all_entries_by_type.Open def cost_or_value(self, node, date, include_children): if include_children: diff --git a/requirements.txt b/requirements.txt index f1ff998..daa1522 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ beancount == 2.3.5 # fava_miler/fava_miler/__init__.py: 3 # fava_miler/fava_miler/common/beancountinvestorapi.py: 27 # fava_miler/fava_miler/common/favainvestorapi.py: 2 -fava == 1.21 +fava == 1.23 # fava_miler/.eggs/setuptools_scm-6.4.2-py3.8.egg/setuptools_scm/_version_cls.py: 5 packaging == 20.9 diff --git a/setup.py b/setup.py index 5e513b1..304193e 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ include_package_data=True, install_requires=[ 'beancount>=2.3.1', - 'fava>=1.19', + 'fava>=1.23', 'Click', ], entry_points={