hledger-vega/src as of git 2022-01-28 dd94a9e

simonmichael
Jan 31, 2022, 5:55 PM
IFCNRUJED6H45DCCO63OOFB2TDAXH6QBB6YP6NFOCDED5QVVLO5QC

Dependencies

Change contents

  • file addition: reports (d--r------)
    [2.1]
  • file addition: wealth.report (----------)
    [0.19]
    assets;Total assets including retirement
    superannuation;Total assets including retirement
    assets;Total assets
    liabilities;Total liabilities
    loan;Loan
  • file addition: wealth.json (----------)
    [0.19]
    {
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "name": "Wealth",
    "title": {
    "text": "Total wealth",
    "subtitle": "Total assets and liabilities (thousand $)"
    },
    "width": "container",
    "height": "container",
    "data": { "url": "../data/wealth.csv"},
    "transform": [
    { "calculate": "datum.value / 1000", "as": "thousands" }
    ],
    "layer": [
    {
    "encoding": {
    "x": {
    "field": "end_date",
    "title": null,
    "type": "temporal",
    "scale": { "domainMin": { "expr": "timeOffset('year', now(), -1)" } }
    },
    "y": {
    "field": "thousands",
    "title": null,
    "type": "quantitative",
    "aggregate": "sum",
    "scale": { "zero": false }
    },
    "color": {
    "field": "account",
    "type": "nominal",
    "legend": { "title": null, "orient": "bottom" }
    }
    },
    "layer": [
    {
    "mark": "line",
    "params": [{ "name": "grid", "select": "interval", "bind": "scales" }]
    },{
    "params": [{
    "name": "label",
    "select": { "type": "point", "encodings": ["x"], "nearest": true, "on": "mouseover" }
    }],
    "mark": "point",
    "encoding": { "opacity": { "condition": { "param": "label", "empty": false, "value": 1 }, "value": 0 } }
    }
    ]
    },{
    "transform": [{"filter": {"param": "label", "empty": false}}],
    "layer": [
    {
    "mark": {"type": "rule", "color": "gray"},
    "encoding": {
    "x": {"type": "temporal", "field": "end_date", "aggregate": "min"}
    }
    },{
    "encoding": {
    "text": {"type": "quantitative", "field": "value", "aggregate": "sum", "format": "$0.3s"},
    "x": {"type": "temporal", "field": "end_date"},
    "y": {"type": "quantitative", "field": "thousands", "aggregate": "sum"},
    "color": {"type": "nominal", "field": "account"}
    },
    "layer": [
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5, "strokeWidth": 2, "stroke": "white"} },
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5} }
    ]
    }
    ]
    }
    ]
    }
  • file addition: savings.report (----------)
    [0.19]
    expenses;expenses
    repayments;repayments
    revenue;revenue
    revenuesuper;revenuesuper
  • file addition: savings.json (----------)
    [0.19]
    {
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "name": "SavingsGoals",
    "title": {
    "text": "Savings goals",
    "subtitle": "Percentage of revenues put into savings, averaged over the last half year"
    },
    "width": "container",
    "height": "container",
    "data": { "url": "../data/savings.csv"},
    "transform": [
    {
    "window": [{"op": "sum", "field": "value", "as": "rolling_sum"}],
    "groupby": ["account"],
    "frame": [-181, 0]
    },
    {
    "window": [{"op": "count", "as": "window_size"}],
    "groupby": ["account"],
    "frame": [-181, 0]
    },
    { "filter": "datum.window_size >= 90" },
    { "pivot": "account", "groupby": ["end_date"], "value": "rolling_sum" },
    {
    "calculate": "datum.revenuesuper != 0 ? (datum.revenuesuper - datum.expenses) / datum.revenuesuper : 0",
    "as": "Including superannuation"
    },
    {
    "calculate": "datum.revenue != 0 ? (datum.revenue - datum.expenses) / datum.revenue : 0",
    "as": "Savings"
    },
    {
    "calculate": "datum.revenue != 0 ? (datum.revenue - datum.repayments) / datum.revenue : 0",
    "as": "Liquid savings"
    },
    {"fold": ["Savings", "Liquid savings", "Including superannuation"], "as": ["account", "value"]},
    {"calculate": "datum.value * 100", "as": "percentage"}
    ],
    "layer": [
    {
    "encoding": {
    "x": {
    "field": "end_date",
    "title": null,
    "type": "temporal",
    "scale": { "domainMin": { "expr": "timeOffset('year', now(), -1)" } }
    },
    "y": {
    "field": "percentage",
    "title": null,
    "type": "quantitative"
    },
    "color": {
    "field": "account",
    "type": "nominal",
    "legend": { "title": null, "orient": "bottom" }
    }
    },
    "layer": [
    {
    "mark": "line",
    "params": [{ "name": "grid", "select": "interval", "bind": "scales" }]
    },{
    "params": [{
    "name": "label",
    "select": { "type": "point", "encodings": ["x"], "nearest": true, "on": "mouseover" }
    }],
    "mark": "point",
    "encoding": { "opacity": { "condition": { "param": "label", "empty": false, "value": 1 }, "value": 0 } }
    }
    ]
    },{
    "transform": [{"filter": {"param": "label", "empty": false}}],
    "layer": [
    {
    "mark": {"type": "rule", "color": "gray"},
    "encoding": {
    "x": {"type": "temporal", "field": "end_date", "aggregate": "min"}
    }
    },{
    "encoding": {
    "text": {"type": "quantitative", "field": "value", "aggregate": "sum", "format": "0.1%"},
    "x": {"type": "temporal", "field": "end_date"},
    "y": {"type": "quantitative", "field": "percentage", "aggregate": "sum"},
    "color": {"type": "nominal", "field": "account"}
    },
    "layer": [
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5, "strokeWidth": 2, "stroke": "white"} },
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5} }
    ]
    }
    ]
    }
    ]
    }
  • file addition: netposition.report (----------)
    [0.19]
    netposition;Net position
    superannuation;Retirement
    netposition;Total
    superannuation;Total
  • file addition: netposition.json (----------)
    [0.19]
    {
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "name": "NetPosition",
    "title": {
    "text": "Net position",
    "subtitle": "Net position (thousand $)"
    },
    "width": "container",
    "height": "container",
    "data": { "url": "../data/netposition.csv"},
    "transform": [
    { "calculate": "datum.value / 1000", "as": "thousands" }
    ],
    "layer": [
    {
    "encoding": {
    "x": {
    "field": "end_date",
    "title": null,
    "type": "temporal",
    "scale": { "domainMin": { "expr": "timeOffset('year', now(), -1)" } }
    },
    "y": {
    "field": "thousands",
    "title": null,
    "type": "quantitative",
    "aggregate": "sum",
    "scale": { "zero": false }
    },
    "color": {
    "field": "account",
    "type": "nominal",
    "legend": { "title": null, "orient": "bottom" }
    }
    },
    "layer": [
    {
    "mark": "line",
    "params": [{ "name": "grid", "select": "interval", "bind": "scales" }]
    },{
    "params": [{
    "name": "label",
    "select": { "type": "point", "encodings": ["x"], "nearest": true, "on": "mouseover" }
    }],
    "mark": "point",
    "encoding": { "opacity": { "condition": { "param": "label", "empty": false, "value": 1 }, "value": 0 } }
    }
    ]
    },{
    "transform": [{"filter": {"param": "label", "empty": false}}],
    "layer": [
    {
    "mark": {"type": "rule", "color": "gray"},
    "encoding": {
    "x": {"type": "temporal", "field": "end_date", "aggregate": "min"}
    }
    },{
    "encoding": {
    "text": {"type": "quantitative", "field": "value", "aggregate": "sum", "format": "$0.3s"},
    "x": {"type": "temporal", "field": "end_date"},
    "y": {"type": "quantitative", "field": "thousands", "aggregate": "sum"},
    "color": {"type": "nominal", "field": "account"}
    },
    "layer": [
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5, "strokeWidth": 2, "stroke": "white"} },
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5} }
    ]
    }
    ]
    }
    ]
    }
  • file addition: netliquid.report (----------)
    [0.19]
    liquidassets;Liquid assets
    liquidliabilities;Short-term liabilities
    netliquid;Net liquid assets
  • file addition: netliquid.json (----------)
    [0.19]
    {
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "name": "NetLiquid",
    "title": {
    "text": "Liquid assets",
    "subtitle": "Liquid assets, net liquid assets, and short-term liabilities (thousand $)"
    },
    "width": "container",
    "height": "container",
    "data": { "url": "../data/netliquid.csv"},
    "transform": [
    { "calculate": "datum.value / 1000", "as": "thousands" }
    ],
    "layer": [
    {
    "encoding": {
    "x": {
    "field": "end_date",
    "title": null,
    "type": "temporal",
    "scale": { "domainMin": { "expr": "timeOffset('year', now(), -1)" } }
    },
    "y": {
    "field": "thousands",
    "title": null,
    "type": "quantitative",
    "aggregate": "sum"
    },
    "color": {
    "field": "account",
    "type": "nominal",
    "legend": { "title": null, "orient": "bottom" }
    }
    },
    "layer": [
    {
    "mark": "line",
    "params": [{ "name": "grid", "select": "interval", "bind": "scales" }]
    },{
    "params": [{
    "name": "label",
    "select": { "type": "point", "encodings": ["x"], "nearest": true, "on": "mouseover" }
    }],
    "mark": "point",
    "encoding": { "opacity": { "condition": { "param": "label", "empty": false, "value": 1 }, "value": 0 } }
    }
    ]
    },{
    "transform": [{"filter": {"param": "label", "empty": false}}],
    "layer": [
    {
    "mark": {"type": "rule", "color": "gray"},
    "encoding": {
    "x": {"type": "temporal", "field": "end_date", "aggregate": "min"}
    }
    },{
    "encoding": {
    "text": {"type": "quantitative", "field": "value", "aggregate": "sum", "format": "$0.3s"},
    "x": {"type": "temporal", "field": "end_date"},
    "y": {"type": "quantitative", "field": "thousands", "aggregate": "sum"},
    "color": {"type": "nominal", "field": "account"}
    },
    "layer": [
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5, "strokeWidth": 2, "stroke": "white"} },
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5} }
    ]
    }
    ]
    }
    ]
    }
  • file addition: incomestatement.report (----------)
    [0.19]
    expenses;Total expenses
    revenue;Total revenue
    repayments;Expenses and repayments
  • file addition: incomestatement.json (----------)
    [0.19]
    {
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "name": "IncomeStatement",
    "title": {
    "text": "Income statement",
    "subtitle": "Weekly revenues and expenses, rolling average over a two-week window ($)"
    },
    "width": "container",
    "height": "container",
    "data": { "url": "../data/incomestatement.csv"},
    "transform": [
    {
    "window": [{"op": "mean", "field": "value", "as": "rolling_mean"}],
    "groupby": ["account"],
    "frame": [-13, 0]
    },
    { "calculate": "datum.rolling_mean * 7", "as": "weekly" },
    { "calculate": "datum.weekly / 1000", "as": "thousands" }
    ],
    "layer": [
    {
    "encoding": {
    "x": {
    "field": "end_date",
    "title": null,
    "type": "temporal",
    "scale": { "domainMin": { "expr": "timeOffset('year', now(), -1)" } }
    },
    "y": {
    "field": "thousands",
    "title": null,
    "type": "quantitative",
    "aggregate": "sum"
    },
    "color": {
    "field": "account",
    "type": "nominal",
    "legend": { "title": null, "orient": "bottom" }
    }
    },
    "layer": [
    {
    "mark": "line",
    "params": [{ "name": "grid", "select": "interval", "bind": "scales" }]
    },{
    "params": [{
    "name": "label",
    "select": { "type": "point", "encodings": ["x"], "nearest": true, "on": "mouseover" }
    }],
    "mark": "point",
    "encoding": { "opacity": { "condition": { "param": "label", "empty": false, "value": 1 }, "value": 0 } }
    }
    ]
    },{
    "transform": [{"filter": {"param": "label", "empty": false}}],
    "layer": [
    {
    "mark": {"type": "rule", "color": "gray"},
    "encoding": {
    "x": {"type": "temporal", "field": "end_date", "aggregate": "min"}
    }
    },{
    "encoding": {
    "text": {"type": "quantitative", "field": "weekly", "aggregate": "sum", "format": "$0.3s"},
    "x": {"type": "temporal", "field": "end_date"},
    "y": {"type": "quantitative", "field": "thousands", "aggregate": "sum"},
    "color": {"type": "nominal", "field": "account"}
    },
    "layer": [
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5, "strokeWidth": 2, "stroke": "white"} },
    { "mark": {"type": "text", "align": "right", "dx": -5, "dy": -5} }
    ]
    }
    ]
    }
    ]
    }
  • file addition: expensecategories.report (----------)
    [0.19]
    expensecategories;
  • file addition: expensecategories.json (----------)
    [0.19]
    {
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "name": "ExpenseCategories",
    "title": {
    "text": "Expense breakdown",
    "subtitle": "Amount per week ($)"
    },
    "data": { "url": "../data/expensecategories.csv"},
    "transform": [
    { "calculate": "split(datum.account, ':', 2)", "as": "accountpair" },
    {
    "window": [{"op": "mean", "field": "value", "as": "rolling_mean"}],
    "groupby": ["accountpair"],
    "frame": [-13, 0]
    },
    { "calculate": "datum.rolling_mean * 7", "as": "weekly" }
    ],
    "facet": {
    "field": "accountpair.0",
    "type": "ordinal",
    "sort": { "op": "sum", "field": "weekly", "order": "descending" },
    "legend": { "title": null }
    },
    "columns": 2,
    "resolve": { "scale": { "y": "independent", "color": "independent" } },
    "params": [{
    "name": "grid",
    "select": "interval",
    "bind": "scales"
    }],
    "spec": {
    "width": 750,
    "height": 190,
    "mark": "line",
    "encoding": {
    "x": {
    "field": "end_date",
    "title": null,
    "type": "temporal",
    "scale": { "domainMin": { "expr": "timeOffset('year', now(), -1)" } }
    },
    "y": {
    "field": "weekly",
    "title": null,
    "type": "quantitative",
    "stack": true
    },
    "color": {
    "field": "accountpair.1",
    "type": "nominal",
    "legend": { "title": null }
    }
    }
    }
    }
  • file addition: index.html (----------)
    [2.1]
    <html>
    <head>
    <title>Hledger charts</title>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vega@5.21.0"></script>
    <script src="https://cdn.jsdelivr.net/npm/vega-lite@5.2.0"></script>
    <script src="https://cdn.jsdelivr.net/npm/vega-embed@6.20.2"></script>
    <style>
    .halfchart { float: left; width: 50%; height: 50%; }
    .fullchart { float: left; width: 100%; height: 50%; }
    </style>
    </head>
    <body>
    <div class="halfchart" id="vis0"></div>
    <div class="halfchart" id="vis1"></div>
    <div class="halfchart" id="vis2"></div>
    <div class="halfchart" id="vis3"></div>
    <div class="fullchart" id="vis4"></div>
    <div id="vis5"></div>
    <script type="text/javascript">
    vegaEmbed('#vis0', "reports/incomestatement.json");
    vegaEmbed('#vis1', "reports/savings.json");
    vegaEmbed('#vis2', "reports/netliquid.json");
    vegaEmbed('#vis3', "reports/netposition.json");
    vegaEmbed('#vis4', "reports/wealth.json");
    vegaEmbed('#vis5', "reports/expensecategories.json");
    </script>
    </body>
    </html>
  • file addition: hledger-vega (---r------)
    [2.1]
    #!/bin/sh
    umask 077
    dir="$(mktemp -d "${TMPDIR:-/tmp}/hledger-vega.XXXXXXXXXX")"
    trap "rm -r $dir" EXIT
    argsdir=./args
    reportdir=./reports
    outdir=./data
    # initialize a semaphore with a given number of tokens
    open_sem(){
    mkfifo $dir/pipe-$$
    exec 3<>$dir/pipe-$$
    rm $dir/pipe-$$
    local i=$1
    for((;i>0;i--)); do
    printf %s 000 >&3
    done
    }
    # run the given command asynchronously and pop/push tokens
    run_with_lock(){
    local x
    # this read waits until there is something to read
    read -u 3 -n 3 x && ((0==x)) || exit $x
    (
    ( "$@"; )
    # push the return code of the command to the semaphore
    printf '%.3d' $? >&3
    )&
    }
    # Create the data directory if it doesn't exist
    mkdir -p "$outdir"
    # Use 4 jobs
    N=4
    open_sem $N
    hledger_with_name() {
    local namewithext="$(basename "$1")"
    local name="${namewithext%.*}"
    hledger balance --output-format=csv --layout=tidy --daily @"$1" \
    > "$dir/$name.csv"
    if [[ $(wc -l < "$dir/$name.csv") -le 1 ]]; then
    >&2 echo "Warning: hledger produced no output from \"$1\""
    fi
    }
    make_report() {
    local namewithext="$(basename "$1")"
    local name="${namewithext%.*}"
    local IFS=";"
    local first=1
    while read -a line; do
    local csv="$dir/${line[0]}.csv"
    local label="${line[1]}"
    {
    if [[ $first -eq 1 ]]; then
    cat "$csv"
    else
    tail -n "+2" "$csv"
    fi
    } | sed -e "s/^\"...\"/\"$label\"/"
    first=0
    done < "$1" \
    > "$outdir/$name.csv"
    }
    for query in "$argsdir"/*.args; do
    run_with_lock hledger_with_name "$query"
    done
    wait
    for report in "$reportdir"/*.report; do
    run_with_lock make_report "$report"
    done
    wait
  • file addition: args (d--r------)
    [2.1]
  • file addition: superannuation.args (----------)
    [0.18022]
    @import/cumulative.args
    ^assets:.*retirement
  • file addition: revenuesuper.args (----------)
    [0.18022]
    @import/change.args
    --invert
    ^(revenue|income)
    not::taxes
  • file addition: revenue.args (----------)
    [0.18022]
    @import/change.args
    --invert
    ^(revenue|income)
    not::retirement
  • file addition: repayments.args (----------)
    [0.18022]
    @import/change.args
    --alias=/^liabilities:(.*:loan.*)/=expenses:\1
    ^expenses
  • file addition: netposition.args (----------)
    [0.18022]
    @import/cumulative.args
    ^assets
    not:^assets:.*retirement
    ^liabilities
  • file addition: netliquid.args (----------)
    [0.18022]
    @import/cumulative.args
    ^assets
    not:^assets:.*prepaids
    not:^assets:.*bonds
    not:^assets:.*retirement
    ^liabilities
    not:^liabilities:.*loan
  • file addition: loan.args (----------)
    [0.18022]
    @import/cumulative.args
    --invert
    ^liabilities:.*loan
  • file addition: liquidliabilities.args (----------)
    [0.18022]
    @import/cumulative.args
    --invert
    ^liabilities
    not:^liabilities:.*loan
  • file addition: liquidassets.args (----------)
    [0.18022]
    @import/cumulative.args
    ^assets
    not:^assets:.*prepaids
    not:^assets:.*bonds
    not:^assets:.*retirement
  • file addition: liabilities.args (----------)
    [0.18022]
    @import/cumulative.args
    --invert
    ^liabilities
  • file addition: import (d--r------)
    [0.18022]
  • file addition: cumulative.args (----------)
    [0.19188]
    --historical
    --value=end,$
    --depth=0
  • file addition: change.args (----------)
    [0.19188]
    --value=then,$
    --cost
    --depth=0
  • file addition: expenses.args (----------)
    [0.18022]
    @import/change.args
    ^expenses
  • file addition: expensecategories.args (----------)
    [0.18022]
    @import/change.args
    --alias=/^expenses:([^:]*)$/=expenses:\1:\1
    --alias=/^expenses$/=expenses:uncategorised:uncategorised
    --depth=3
    --drop=1
    ^expenses
  • file addition: assets.args (----------)
    [0.18022]
    @import/cumulative.args
    ^assets
  • file addition: Makefile (----------)
    [2.1]
    all: vega vega-lite vega-embed
    vegaVersion = 5
    vegaLiteVersion = 5
    vegaEmbedVersion = 6
    vega: .FORCE
    curl https://cdn.jsdelivr.net/npm/vega@$(vegaVersion) > vega@$(vegaVersion)
    vega-lite: .FORCE
    curl https://cdn.jsdelivr.net/npm/vega-lite@$(vegaLiteVersion) > vega-lite@$(vegaLiteVersion)
    vega-embed: .FORCE
    curl https://cdn.jsdelivr.net/npm/vega-embed@$(vegaEmbedVersion) > vega-embed@$(vegaEmbedVersion)
    .PHONY: all .FORCE
  • file addition: .ignore (----------)
    [2.1]
    .git
    .DS_Store