From 94533e5938efbcab09d04b8839209fc3982e559d Mon Sep 17 00:00:00 2001
From: Vahid <24546202+vahidmah@users.noreply.github.com>
Date: Fri, 26 May 2023 22:17:14 -0400
Subject: [PATCH] Add calculation and disply for average of incomes and
expenses
---
frontend/css/base.css | 16 +++++++
src/fava/application.py | 53 +++++++++++++++++++++++-
src/fava/templates/income_statement.html | 31 ++++++++++++++
tests/test_application.py | 18 ++++++++
4 files changed, 117 insertions(+), 1 deletion(-)
diff --git a/frontend/css/base.css b/frontend/css/base.css
index e244aa6c8..5b92369dd 100644
--- a/frontend/css/base.css
+++ b/frontend/css/base.css
@@ -461,3 +461,19 @@ td .status-indicator {
.options td:nth-child(2) {
white-space: normal;
}
+/*
+ * Incomes Expenses Average
+ */
+.inc_exp_avg_row {
+ display: flex;
+ flex-wrap: wrap;
+}
+.inc_exp_avg_column {
+ flex: 1;
+}
+.inc_exp_avg_header {
+ font-weight: 400;
+ background-color: var(--table-header-background);
+ border: 1px solid var(--table-border);
+ text-align: center;
+}
diff --git a/src/fava/application.py b/src/fava/application.py
index ff03fceac..ce8315f74 100644
--- a/src/fava/application.py
+++ b/src/fava/application.py
@@ -11,10 +11,12 @@
"""
from __future__ import annotations
+from collections import defaultdict
from dataclasses import fields
from datetime import date
from datetime import datetime
from functools import lru_cache
+from functools import reduce
from io import BytesIO
from pathlib import Path
from threading import Lock
@@ -25,6 +27,7 @@
from urllib.parse import urlunparse
import markdown2 # type: ignore[import]
+from _decimal import Decimal
from beancount import __version__ as beancount_version
from beancount.utils.text_utils import replace_numbers
from flask import abort
@@ -66,6 +69,9 @@
from flask.wrappers import Response
from werkzeug import Response as WerkzeugResponse
+ from fava.core.charts import DateAndBalanceWithBudget
+ from fava.internal_api import ChartData
+
setup_logging()
@@ -164,8 +170,53 @@ def _setup_template_config(fava_app: Flask) -> None:
@fava_app.context_processor
def _template_context() -> dict[str, FavaLedger | type[ChartApi]]:
+ incomes_expenses_averages = _calculate_chart_average()
"""Inject variables into the template context."""
- return {"ledger": g.ledger, "chart_api": ChartApi}
+ return {
+ "ledger": g.ledger,
+ "chart_api": ChartApi,
+ "incomes_expenses_averages": incomes_expenses_averages,
+ }
+
+
+def _calculate_chart_average() -> (
+ tuple[dict[str, Decimal], dict[str, Decimal]]
+):
+ income_interval_totals: ChartData = ChartApi.interval_totals(
+ g.interval, g.ledger.options["name_income"], ""
+ )
+ expense_interval_totals: ChartData = ChartApi.interval_totals(
+ g.interval, g.ledger.options["name_expenses"], ""
+ )
+
+ def sum_balances(
+ total_balances: dict[str, Decimal], d: DateAndBalanceWithBudget
+ ) -> dict[str, Decimal]:
+ for key, value in d.balance.items():
+ total_balances[key] = total_balances[key] + value
+ return total_balances
+
+ income_balances = reduce(
+ sum_balances,
+ income_interval_totals.data,
+ defaultdict(lambda: Decimal(0)),
+ )
+ expense_balances = reduce(
+ sum_balances,
+ expense_interval_totals.data,
+ defaultdict(lambda: Decimal(0)),
+ )
+
+ income_averages = {
+ ib[0]: ib[1] / len(income_interval_totals.data)
+ for ib in income_balances.items()
+ }
+ expense_averages = {
+ ib[0]: ib[1] / len(expense_interval_totals.data)
+ for ib in expense_balances.items()
+ }
+
+ return income_averages, expense_averages
def _setup_filters(fava_app: Flask, read_only: bool, incognito: bool) -> None:
diff --git a/src/fava/templates/income_statement.html b/src/fava/templates/income_statement.html
index 9ecb80431..bdcfaf18a 100644
--- a/src/fava/templates/income_statement.html
+++ b/src/fava/templates/income_statement.html
@@ -3,6 +3,8 @@
{% set root_tree = g.filtered.root_tree %}
{% set options = ledger.options %}
{% set invert = ledger.fava_options.invert_income_liabilities_equity %}
+{% set incomesAverage = incomes_expenses_averages[0]%}
+{% set expensesAverage = incomes_expenses_averages[1]%}