Skip to content

Commit

Permalink
Add calculation and disply for average of incomes and expenses
Browse files Browse the repository at this point in the history
  • Loading branch information
vahidmah committed May 27, 2023
1 parent 6293d09 commit 94533e5
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 1 deletion.
16 changes: 16 additions & 0 deletions frontend/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
53 changes: 52 additions & 1 deletion src/fava/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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:
Expand Down
31 changes: 31 additions & 0 deletions src/fava/templates/income_statement.html
Original file line number Diff line number Diff line change
Expand Up @@ -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]%}

<svelte-component type="charts"><script type="application/json">{{
[
Expand All @@ -14,6 +16,35 @@
]|tojson
}}</script></svelte-component>

<div class="row">
<div class="column">
<div class="inc_exp_avg_header">Incomes Average</div>
{% for inc_average in incomes_expenses_averages[0] %}
<div class="inc_exp_avg_row">
<div class="inc_exp_avg_column">
{{inc_average}}
</div>
<div class="inc_exp_avg_column">
{{incomesAverage[inc_average] | format_currency}}
</div>
</div>
{% endfor %}
</div>
<div class="column">
<div class="inc_exp_avg_header">Expenses Average</div>
{% for exp_average in incomes_expenses_averages[1] %}
<div class="inc_exp_avg_row">
<div class="inc_exp_avg_column">
{{exp_average}}
</div>
<div class="inc_exp_avg_column">
{{expensesAverage[exp_average] | format_currency}}
</div>
</div>
{% endfor %}
</div>
</div>

<div class="row">
<div class="column">
{{ tree_table.tree(root_tree.get(options['name_income']), invert=invert) }}
Expand Down
18 changes: 18 additions & 0 deletions tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
from beancount import __version__ as beancount_version

from fava import __version__ as fava_version
from fava.application import _calculate_chart_average
from fava.application import create_app
from fava.application import SERVER_SIDE_REPORTS
from fava.application import static_url
from fava.context import g
from fava.template_filters import format_currency

if TYPE_CHECKING: # pragma: no cover
from pathlib import Path
Expand Down Expand Up @@ -264,3 +266,19 @@ def test_load_extension_reports(test_client: FlaskClient) -> None:
url = "/extension-report/extension/MissingExtension/"
result = test_client.get(url)
assert result.status_code == 404


def test_calculate_average_income_expenses(app: Flask) -> None:
with app.test_request_context("/long-example/?interval=year"):
app.preprocess_request()
averages = _calculate_chart_average()
income_averages = averages[0]
expenses_averages = averages[1]

assert format_currency(income_averages["IRAUSD"]) == "-3147.06"
assert format_currency(income_averages["USD"]) == "-18309.54"
assert format_currency(income_averages["VACHR"]) == "-18.24"

assert format_currency(expenses_averages["IRAUSD"]) == "2723.53"
assert format_currency(expenses_averages["USD"]) == "13142.98"
assert format_currency(expenses_averages["VACHR"]) == "23.06"

0 comments on commit 94533e5

Please sign in to comment.