andreasgerstmayr / fava-dashboards Goto Github PK
View Code? Open in Web Editor NEWCustom Dashboards for Beancount in Fava
License: MIT License
Custom Dashboards for Beancount in Fava
License: MIT License
Not an issue. Just showing my soultion to write complex dashboard.yml which is using TSX to generate yaml.
https://github.com/fengkx/tsx-yaml-example-fava-dashboards
In this way we can get type checking and better code editing experience.
I'm running fava 1.24.4 and beancount 2.3.5 on windows 11 with python 3.10. If I clone this repository and open the fava-dashboards/example direction and run
fava example.beancount
then when I go to the dashboard page on my web browser, I get the following error
$ fava example.beancount
Starting Fava on http://127.0.0.1:5000
Exception on /beancount/extension/FavaDashboards/ [GET]
Traceback (most recent call last):
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\app.py", line 2077, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\app.py", line 1525, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\app.py", line 1523, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\app.py", line 1509, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\fava\application.py", line 370, in extension_report
content = Markup(render_template_string(template, extension=extension))
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\templating.py", line 166, in render_template_string
return _render(ctx.app.jinja_env.from_string(source), context, ctx.app)
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\templating.py", line 128, in _render
rv = template.render(context)
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\jinja2\environment.py", line 1291, in render
self.environment.handle_exception()
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\jinja2\environment.py", line 925, in handle_exception
raise rewrite_traceback_stack(source=source)
File "<template>", line 6, in top-level template code
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\site-packages\fava_dashboards\__init__.py", line 21, in static_file
return f.read()
File "C:\Users\username\AppData\Local\Programs\Python\Python310\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 183817: character maps to <undefined>
I tried downgrading to fava 1.22 since they made a few changes to the rendering engine that could break plugins, but I get the same error either way.
Thanks,
link
, e.g. {{fava_path]}/account/ABC
Awesome work!
Question.
If I add a time condition (e.g., year > 2017
) in the bql
query for the Portfolio Gains panel, the gains are "translated" so they start at roughly $0 for the first sample.
SELECT year, month, CONVERT(LAST(balance), '{{fava.ccy}}') AS market_value, COST(LAST(balance)) AS book_value WHERE year > 2017 AND account ~ 'Assets:' AND currency != '{{fava.ccy}}' GROUP BY year, month
vs
SELECT year, month, CONVERT(LAST(balance), '{{fava.ccy}}') AS market_value, COST(LAST(balance)) AS book_value WHERE account ~ 'Assets:' AND currency != '{{fava.ccy}}' GROUP BY year, month
Not adding the time condition makes the query run very slowly for me, as I have data as far back as 1994...
Hi - I would like to know if it is possible to add multiple dashboards to the same beancount ledger? If so, how to accomplish that. I am able to add a single dashboard and it runs fine. Thanks for the great extension.
I've written a few helper functions to post-process the BQL results into the format I want for e-charts. Would it be feasible to have a separate JS file containing these that can be included in the YAML on a per-panel basis? I think this has a few benefits
script
, but this increases runtime unnecessarily)I really like this dashboard plugin. I was wondering if there's a way to use some of fava's included templates as an output with fava_templates? What I'd like to do is run a bql query and have it output a 'pretty' table like is does when you run a custom query in fava. Your setup it so flexible, I can run a bpl query and iterate over the results and generate the same html, but I'm wondering if there's a cleaner / simpler way to do this by hooking into the _query_table.html template in fava instead of effectively recreating it in each script.
Thanks!
as per above, is that possible to refer to the fava's current filters, including time, account and filter?
Thanks
dashboards:
foo: 1
- name: Lorem
bar: 2
panels:
baz: 3
- title: Ipsum
type: jinja2
template: {{ dashboards.foo }} {{ dashboards.lorem.bar }} {{ panel.baz }}
Currently (as documented), from the above template, only panel.baz
is available to jinja2.
For greater DRY it'd be great to be able to use variables from higher up in the YAML.
(My use case: reusing query components).
Is it possible to access the bql query in our HTML scrips? I'd like to be able to have the user input a date and re-run the query as of that date. I think that you can do something like this by using the fava search parameters, but I want to be able to display different queries side by side (e.g., current year balance sheet vs last year). Any way to accomplish this? Thanks!
* Getting build dependencies for wheel...
* Building wheel...
running bdist_wheel
running build
running build_py
creating build
creating build/lib
creating build/lib/fava_dashboards
copying fava_dashboards/__init__.py -> build/lib/fava_dashboards
running egg_info
creating fava_dashboards.egg-info
writing fava_dashboards.egg-info/PKG-INFO
writing dependency_links to fava_dashboards.egg-info/dependency_links.txt
writing requirements to fava_dashboards.egg-info/requires.txt
writing top-level names to fava_dashboards.egg-info/top_level.txt
writing manifest file 'fava_dashboards.egg-info/SOURCES.txt'
adding license file 'LICENSE.md'
writing manifest file 'fava_dashboards.egg-info/SOURCES.txt'
/usr/lib/python3.11/site-packages/setuptools/command/build_py.py:201: _Warning: Package 'fava_dashboards.templates' is absent from the `packages` configuration.
!!
********************************************************************************
############################
# Package would be ignored #
############################
Python recognizes 'fava_dashboards.templates' as an importable package[^1],
but it is absent from setuptools' `packages` configuration.
This leads to an ambiguous overall configuration. If you want to distribute this
package, please make sure that 'fava_dashboards.templates' is explicitly added
to the `packages` configuration field.
Alternatively, you can also rely on setuptools' discovery methods
(for example by using `find_namespace_packages(...)`/`find_namespace:`
instead of `find_packages(...)`/`find:`).
You can read more about "package discovery" on setuptools documentation page:
- https://setuptools.pypa.io/en/latest/userguide/package_discovery.html
If you don't want 'fava_dashboards.templates' to be distributed and are
already explicitly excluding 'fava_dashboards.templates' via
`find_namespace_packages(...)/find_namespace` or `find_packages(...)/find`,
you can try to use `exclude_package_data`, or `include-package-data=False` in
combination with a more fine grained `package-data` configuration.
You can read more about "package data files" on setuptools documentation page:
- https://setuptools.pypa.io/en/latest/userguide/datafiles.html
[^1]: For Python, any directory (with suitable naming) can be imported,
even if it does not contain any `.py` files.
On the other hand, currently there is no concept of package data
directory, all directories are treated like packages.
********************************************************************************
!!
check.warn(importable)
copying fava_dashboards/FavaDashboards.js -> build/lib/fava_dashboards
[...]
Hi, I would like to suggest an historical net worth allocation chart, but I am not sure how to implement it.
We already have an historical income and expense stacked bar chart,
and I think it could be repurposed for assets and liabilities.
For the latter I had to take snapshots of the amounts in the "Assets Allocation" chart at 31st December of 2021, 2022 and 2023. I then used a csv file, pandas
and matplotlib
to group and plot the values.
I like your implementation of the stacked bar chart, as liabilities could appear as a second stack for each period.
I know you grouped income and expenses by year and month, but I guess this cannot be done so easily for assets and liabilities.
Can we have such a chart?
Hi Andreas, thank you for this wonderful Fava extension.
I started using Fava/beancount way before my first job.
Therefore, the figures I see are way different from the reality of the last 9 months.
I would like to trim the Sankey diagram to the last 9 months, to get a realistic overview of my saving rate and recent expenses.
If I specify a date, it should be used in both the query and the days
calculation. If I only add it to the query, days
still reports the number of days since my very first transaction, way before 9 months ago.
My solution is to:
AND date > 2023-02-01
to the querynew Date(ledger.dateFirst)
with new Date("2023-02-01")
Is it possible to specify the date once as a parameter of the query, the JS code and maybe also the title?
Related to #23
First off thanks for this amazing project! It automates so much of the front-end boilerplate for dashboards
I'm new to BQL and can tell that my queries (both number and complexity) are rather inefficient based on the load time for a dashboard page. After all the plumbing works, I'd like to go back and optimize this. It'd be really convenient if fava-dashboards
recorded the total query time and made it accessible through Jinja like {{queries[i].time}}
. This way I could render it on the page itself as feedback
Hi !
I have discovered fava-dashboard a couple of weeks ago, and I love it. Thank you for this great addition to fava and beancount.
I am currently creating my own dashboards panels, and i have been able to reach pretty much all my expectations except for my last graph:
I want to create a stacked area graph showing the variation of my various assets over time.
My issue is that for some of the assets, i have some months without any returned balance as there is no transaction that month.
Trying to stack curve with missing points tends to brake the graph.
Is there a good way to fill the gaps in the dataset in order to get a proper result ?
Here is my (broken) graph and the corresponding code.
- title: Actifs 📈
width: 100%
link: /comptes-famille-gros/balance_sheet/
queries:
- bql: |
SELECT year, month,
CONVERT(LAST(balance), '{{ledger.ccy}}', DATE_ADD(YMONTH(DATE_ADD(YMONTH(FIRST(date)), 31)), -1)) AS value
WHERE account ~ '^Actif:Crypto'
GROUP BY year, month
name: Crypto
link: /comptes-famille-gros/balance_sheet/?time={time}
stack: detail
areaStyle: {}
- bql: |
SELECT year, month,
CONVERT(LAST(balance), '{{ledger.ccy}}', DATE_ADD(YMONTH(DATE_ADD(YMONTH(FIRST(date)), 31)), -1)) AS value
WHERE account ~ '^Actif:Bink'
GROUP BY year, month
name: PEA+CTO
link: /comptes-famille-gros/balance_sheet/?time={time}
stack: detail
areaStyle: {}
- bql: |
SELECT year, month,
CONVERT(LAST(balance), '{{ledger.ccy}}', DATE_ADD(YMONTH(DATE_ADD(YMONTH(FIRST(date)), 31)), -1)) AS value
WHERE account ~ '^Actif:(Boursorama:AVTim2|Boursorama:AVAnna1|Linxea)'
GROUP BY year, month
name: Assurances Vie
link: /comptes-famille-gros/balance_sheet/?time={time}
stack: detail
areaStyle: {}
- bql: |
SELECT year, month,
CONVERT(LAST(balance), '{{ledger.ccy}}', DATE_ADD(YMONTH(DATE_ADD(YMONTH(FIRST(date)), 31)), -1)) AS value
WHERE account ~ '^Actif:(Boursorama:CC|Boursorama:AVAnna2|Boursorama:LDD|Boursorama:CEL|Boursorama:LA|BanquePostale:LAAnna)'
GROUP BY year, month
name: Monétaire
link: /comptes-famille-gros/balance_sheet/?time={time}
stack: detail
areaStyle: {}
- bql: |
SELECT year, month,
CONVERT(SUM(position), '{{ledger.ccy}}') AS value
WHERE account ~ '^Actif:Immobilier'
GROUP BY year, month
name: Immobilier
link: /comptes-famille-gros/balance_sheet/?time={time}
stack: detail
areaStyle: {}
type: echarts
script: |
const currencyFormat = new Intl.NumberFormat(undefined, {
style: "currency",
currency: ledger.ccy,
maximumFractionDigits: 0,
});
const months = helpers.iterateMonths(ledger.dateFirst, ledger.dateLast).map((m) => `${m.month}/${m.year}`);
// the beancount query only returns months where there was at least one matching transaction, therefore we group by month
const amounts = {};
for (let query of panel.queries) {
amounts[query.name] = {};
for (let row of query.result) {
amounts[query.name][`${row.month}/${row.year}`] = row.value[ledger.ccy];
}
}
return {
tooltip: {
valueFormatter: currencyFormat.format,
},
legend: {
top: "bottom",
},
xAxis: {
data: months,
},
yAxis: {
axisLabel: {
formatter: currencyFormat.format,
},
},
series: panel.queries.map((query) => ({
type: "line",
name: query.name,
stack: query.stack,
areaStyle: query.areaStyle,
data: months.map((month) => amounts[query.name][month]),
connectNulls: true,
})),
onClick: (event) => {
const query = panel.queries.find((q) => q.name === event.seriesName);
if (query) {
const [month, year] = event.name.split("/");
const link = query.link.replaceAll("#", "%23").replace("{time}", `${year}-${month.padStart(2, "0")}`);
window.open(link);
}
},
};
If this is not the right place to get support for your tool, let me know, and i will repost my message accordingly.
For the life of me, I can't figure out where to place the configuration document.
Josh
Fava Version: 1.26
Python Version: 3.10.12
Following the installation instruction by pulling the most up to date version of fava-dashboards directly from github, the following error is reported within fava when running the example files(example.beancount & dashboards.yaml). Any recommendations on how to further debug the issue or a resolution?
Importing module "fava_dashboards" failed.
Error: "cannot import name 'simple_units' from 'fava.core.conversion' (/usr/local/lib/python3.10/dist-packages/fava/core/conversion.py)"
Thank you in advance for your time;
Regards -
Hi! I'm starting to use the plugin and it's awesome to create custom reports for my monthly expenses, etc.
I'd like to be able to load the plugin twice, as to have two tabs in Fava. In my case, I have to track most things twice, in my home currency, which is very volatile, and in dollars. This would allow me to duplicate most dashboards but changing the currency.
Maybe this is better suited for the Fava repo, but also I think, doing something like this would work perfectly and be very flexible:
2023-01-02 custom "fava-extension" "fava_dashboards" "[
{'config': 'plugins/fava_dashboards/example01.yaml', name: "Dashboards 01"},
{'config': 'plugins/fava_dashboards/example02.yaml', name: "Dashboards 02"}
]"
I cannot open anymore the extension, as it returns the following:
# Starting Fava on http://127.0.0.1:5000
Exception on /accounting/extension/FavaDashboards/ [GET]
Traceback (most recent call last):
File "/usr/lib/python3.11/site-packages/flask/app.py", line 2190, in wsgi_app
response = self.full_dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/flask/app.py", line 1486, in full_dispatch_request
rv = self.handle_user_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/flask/app.py", line 1484, in full_dispatch_request
rv = self.dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/flask/app.py", line 1469, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/fava/application.py", line 348, in extension_report
content = Markup(template.render(ledger=g.ledger, extension=ext))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/jinja2/environment.py", line 1301, in render
self.environment.handle_exception()
File "/usr/lib/python3.11/site-packages/jinja2/environment.py", line 936, in handle_exception
raise rewrite_traceback_stack(source=source)
File "/usr/lib/python3.11/site-packages/fava_dashboards/templates/FavaDashboards.html", line 6, in top-level template code
{{ {"ledger": bootstrap["ledger"], "dashboard": bootstrap["dashboards"][dashboard_id]}|tojson }}
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/jinja2/filters.py", line 1688, in do_tojson
return htmlsafe_json_dumps(value, dumps=dumps, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/jinja2/utils.py", line 658, in htmlsafe_json_dumps
dumps(obj, **kwargs)
File "/usr/lib/python3.11/site-packages/fava/core/charts.py", line 84, in dumps
return dumps(obj)
^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/fava/core/charts.py", line 67, in dumps
return simplejson_dumps(
^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/simplejson/__init__.py", line 395, in dumps
**kw).encode(obj)
^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 300, in encode
chunks = list(chunks)
^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 714, in _iterencode
for chunk in _iterencode_dict(o, _current_indent_level):
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 668, in _iterencode_dict
for chunk in chunks:
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 668, in _iterencode_dict
for chunk in chunks:
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 544, in _iterencode_list
for chunk in chunks:
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 668, in _iterencode_dict
for chunk in chunks:
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 544, in _iterencode_list
for chunk in chunks:
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 668, in _iterencode_dict
for chunk in chunks:
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 544, in _iterencode_list
for chunk in chunks:
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 668, in _iterencode_dict
for chunk in chunks:
File "/usr/lib/python3.11/site-packages/simplejson/encoder.py", line 585, in _iterencode_dict
if not dct:
File "/usr/lib/python3.11/site-packages/beancount/core/inventory.py", line 137, in __bool__
raise NotImplementedError("Use explicit is_empty() method instead.")
NotImplementedError: Use explicit is_empty() method instead.
Hi! Apparently, v1.2.5 breaks fava_dashboards. For example, I've seen that FavaAPIException
is not FavaAPIError
in helpers.py
. After fixing that, I got this error:
Traceback (most recent call last):
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/flask/app.py", line 2190, in wsgi_app
response = self.full_dispatch_request()
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/flask/app.py", line 1486, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/flask/app.py", line 1484, in full_dispatch_request
rv = self.dispatch_request()
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/flask/app.py", line 1469, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/fava/application.py", line 327, in extension_report
content = Markup(render_template_string(template, extension=ext))
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/flask/templating.py", line 163, in render_template_string
return _render(app, template, context)
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/flask/templating.py", line 132, in _render
rv = template.render(context)
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/jinja2/environment.py", line 1301, in render
self.environment.handle_exception()
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/jinja2/environment.py", line 936, in handle_exception
raise rewrite_traceback_stack(source=source)
File "<template>", line 26, in top-level template code
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/fava/application.py", line 136, in url_for
return _cached_url_for(endpoint, **values)
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/flask/helpers.py", line 225, in url_for
return current_app.url_for(
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/flask/app.py", line 1697, in url_for
return self.handle_url_build_error(error, endpoint, values)
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/flask/app.py", line 1686, in url_for
rv = url_adapter.build( # type: ignore[union-attr]
File "/home/calvo/Nextcloud/Finanzas/Beans/.venv/lib/python3.10/site-packages/werkzeug/routing/map.py", line 950, in build
raise BuildError(endpoint, values, method, self)
werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'extension_report' with values ['bfile', 'dashboard', 'report_name']. Did you forget to specify values ['extension_name']?
I'd like to help fix it. I'm going to work right now, but I'll take another crack at it tomorrow if you want.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.