Skip to content

Commit

Permalink
add basic projection chart (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasgerstmayr authored Sep 24, 2023
1 parent 82655ef commit 5ac5263
Show file tree
Hide file tree
Showing 9 changed files with 1,627 additions and 113 deletions.
109 changes: 107 additions & 2 deletions example/dashboards.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ dashboards:
return {
tooltip: {
trigger: "axis",
valueFormatter: currencyFormat.format,
},
legend: {
Expand Down Expand Up @@ -554,7 +555,7 @@ dashboards:
SELECT root(account, 4) AS account, CONVERT(SUM(position), '{{ledger.ccy}}') AS value
WHERE account ~ '^Income:'
GROUP BY account
link: /beancount/account/{account}/changes/?time={time}
link: /beancount/account/{account}/?r=changes&time={time}
type: echarts
script: |
const currencyFormat = new Intl.NumberFormat(undefined, {
Expand Down Expand Up @@ -603,7 +604,7 @@ dashboards:
SELECT root(account, 3) AS account, CONVERT(SUM(position), '{{ledger.ccy}}') AS value
WHERE account ~ '^Expenses:'
GROUP BY account
link: /beancount/account/{account}/changes/?time={time}
link: /beancount/account/{account}/?r=changes&time={time}
type: echarts
script: |
const currencyFormat = new Intl.NumberFormat(undefined, {
Expand Down Expand Up @@ -1028,3 +1029,107 @@ dashboards:
window.open(link);
},
};
- name: Projection
panels:
- title: Net Worth 💰
link: /beancount/income_statement/
queries:
- bql: |
SELECT year, month,
CONVERT(LAST(balance), '{{ledger.ccy}}', DATE_ADD(YMONTH(DATE_ADD(YMONTH(FIRST(date)), 31)), -1)) AS value
WHERE account_sortkey(account) ~ '^[01]'
GROUP BY year, month
ORDER BY year, month
link: /beancount/balance_sheet/?time={time}
# ignore onetime income and expenses, for example winning the lottery or wedding expenses
- bql: |
SELECT year, month,
CONVERT(LAST(balance), '{{ledger.ccy}}', DATE_ADD(YMONTH(DATE_ADD(YMONTH(FIRST(date)), 31)), -1)) AS value
WHERE account_sortkey(account) ~ '^[01]' AND NOT 'wedding' IN tags
GROUP BY year, month
ORDER BY year, month
type: echarts
script: |
const currencyFormat = new Intl.NumberFormat(undefined, {
style: "currency",
currency: ledger.ccy,
maximumFractionDigits: 0,
});
const projectYears = 2; // number of years to project
// the beancount query only returns months where there was at least one matching transaction, therefore we group by month
const amounts = {};
const amountsEx = {};
for (let row of panel.queries[0].result) {
amounts[`${row.month}/${row.year}`] = row.value[ledger.ccy];
}
for (let row of panel.queries[1].result) {
amountsEx[`${row.month}/${row.year}`] = row.value[ledger.ccy];
}
const [year, month] = ledger.dateLast.split("-").map((x) => parseInt(x));
const projectUntil = `${year + projectYears}-${month}-1`;
const months = helpers.iterateMonths(ledger.dateFirst, projectUntil).map((m) => `${m.month}/${m.year}`);
const lastMonthIdx = months.findIndex((m) => m === `${month}/${year}`);
const ledgerDays = (new Date(ledger.dateLast) - new Date(ledger.dateFirst)) / (1000 * 60 * 60 * 24) + 1;
const results = panel.queries[0].result;
const finalAmount = results[results.length - 1].value[ledger.ccy];
const resultsEx = panel.queries[1].result;
const totalDiffEx = resultsEx[resultsEx.length - 1].value[ledger.ccy] - resultsEx[0].value[ledger.ccy];
const monthlyDiffEx = (totalDiffEx / ledgerDays) * (365 / 12);
const projection = [];
let sum = finalAmount;
for (let i = lastMonthIdx; i < months.length; i++) {
projection[months[i]] = sum;
sum += monthlyDiffEx;
}
return {
tooltip: {
valueFormatter: currencyFormat.format,
},
legend: {
top: "bottom",
},
xAxis: {
data: months,
},
yAxis: {
axisLabel: {
formatter: currencyFormat.format,
},
},
series: [
{
type: "line",
name: "Net Worth",
smooth: true,
connectNulls: true,
data: months.map((month) => amounts[month] || null),
},
{
type: "line",
name: "Excluding onetime txns",
smooth: true,
connectNulls: true,
data: months.map((month) => amountsEx[month]),
},
{
type: "line",
name: "Projection",
lineStyle: {
type: "dotted",
},
data: months.map((month) => projection[month]),
},
],
onClick: (event) => {
if (!panel.queries[0].link) return;
const [month, year] = event.name.split("/");
const link = panel.queries[0].link.replaceAll("#", "%23").replace("{time}", `${year}-${month.padStart(2, "0")}`);
window.open(link);
},
};
Binary file modified frontend/tests/e2e/__image_snapshots__/dashboard_assets.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/tests/e2e/__image_snapshots__/dashboard_overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/tests/e2e/__image_snapshots__/dashboard_sankey.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/tests/e2e/__image_snapshots__/dashboard_travelling.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 5ac5263

Please sign in to comment.