Skip to content

Commit

Permalink
use ledger start/end date if date filter is greater than ledger time …
Browse files Browse the repository at this point in the history
…frame (#69)

Adjust the dates in case the date filter is set to e.g. 2023-2024,
however the ledger only contains data up to summer 2024.
Without this, all averages in the dashboard are off,
because of a wrong number of days between dateFirst and dateLast.
  • Loading branch information
andreasgerstmayr authored Jun 30, 2024
1 parent ba95401 commit 21f5fbe
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 16 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ The following variables and functions are available:
* `ext`: the Fava [`ExtensionContext`](https://github.com/beancount/fava/blob/main/frontend/src/extensions.ts)
* `ext.api.get("query", {bql: "SELECT ..."}`: executes the specified BQL query
* `panel`: the current (augmented) panel definition. The results of the BQL queries can be accessed with `panel.queries[i].result`.
* `ledger.dateFirst`: first date in the current date filter
* `ledger.dateLast`: last date in the current date filter
* `ledger.dateFirst`: start date of the current date filter, or first transaction date of the ledger
* `ledger.dateLast`: end date of the current date filter, or last transaction date of the ledger
* `ledger.operatingCurrencies`: configured operating currencies of the ledger
* `ledger.ccy`: shortcut for the first configured operating currency of the ledger
* `ledger.accounts`: declared accounts of the ledger
Expand Down
59 changes: 45 additions & 14 deletions src/fava_dashboards/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict
from typing import Any, Dict, List
import datetime
from dataclasses import dataclass
from collections import namedtuple
Expand All @@ -7,6 +7,9 @@
from beancount.core.inventory import Inventory # type: ignore
from beanquery.query import run_query # type: ignore
from fava.application import render_template_string
from fava.beans.abc import Directive
from fava.beans.abc import Price
from fava.beans.abc import Transaction
from fava.context import g
from fava.core import FavaLedger
from fava.core.conversion import simple_units
Expand Down Expand Up @@ -114,38 +117,66 @@ def process_panel(self, ctx: PanelCtx):
self.process_jinja2(ctx)
self.sanitize_panel(ctx)

def bootstrap(self, dashboard_id):
ext_config = self.read_ext_config()
def get_ledger_duration(self, entries: List[Directive]):
date_first = None
date_last = None
for entry in entries:
if isinstance(entry, Transaction):
date_first = entry.date
break
for entry in reversed(entries):
if isinstance(entry, (Transaction, Price)):
date_last = entry.date
break
if not date_first or not date_last:
raise FavaAPIError("no transaction found")
return (date_first, date_last)

def get_ledger(self):
operating_currencies = self.ledger.options["operating_currency"]

if len(operating_currencies) == 0:
raise FavaAPIError("no operating currency specified in the ledger")
# pylint: disable=protected-access
if not g.filtered._date_first or not g.filtered._date_last:
raise FavaAPIError(
"cannot determine first/last day of ledger, is the ledger empty?"

if g.filtered.date_range:
date_first = g.filtered.date_range.begin
date_last = g.filtered.date_range.end - datetime.timedelta(days=1)

# Adjust the dates in case the date filter is set to e.g. 2023-2024,
# however the ledger only contains data up to summer 2024.
# Without this, all averages in the dashboard are off,
# because of a wrong number of days between dateFirst and dateLast.
ledger_date_first, ledger_date_last = self.get_ledger_duration(
self.ledger.all_entries
)
if not (date_last < ledger_date_first or date_first > ledger_date_last):
date_first = max(date_first, ledger_date_first)
date_last = min(date_last, ledger_date_last)
else:
# Use filtered ledger here, as another filter (e.g. tag filter) could be applied.
date_first, date_last = self.get_ledger_duration(g.filtered.entries)

commodities = {c.currency: c for c in self.ledger.all_entries_by_type.Commodity}
accounts = self.ledger.accounts
ledger = {
# pylint: disable=protected-access
"dateFirst": g.filtered._date_first,
# pylint: disable=protected-access
"dateLast": g.filtered._date_last - datetime.timedelta(days=1),
return {
"dateFirst": date_first,
"dateLast": date_last,
"operatingCurrencies": operating_currencies,
"ccy": operating_currencies[0],
"accounts": accounts,
"commodities": commodities,
}

def bootstrap(self, dashboard_id):
ext_config = self.read_ext_config()
ledger = self.get_ledger()

dashboards_yaml = self.read_dashboards_yaml(ext_config.dashboards_path)
dashboards = dashboards_yaml.get("dashboards", [])
if not 0 <= dashboard_id < len(dashboards):
raise FavaAPIError(f"invalid dashboard ID: {dashboard_id}")

for panel in dashboards[dashboard_id].get("panels", []):
ctx = PanelCtx(ledger, self.ledger, panel)
ctx = PanelCtx(ledger=ledger, favaledger=self.ledger, panel=panel)
try:
self.process_panel(ctx)
except Exception as ex:
Expand Down

0 comments on commit 21f5fbe

Please sign in to comment.