Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tutorial: a household budget on the desktop

You will build a three-month household budget: a monthly budget per spending line, what was actually spent, and the income. Improv then works out what is left on every line and how much was saved each month, and you open the result in the desktop app.

The model is built with the improv command-line tool, one command per step, because each step shows one idea. The same file opens in improv-gui and improv-tui.

This page is tested: cargo test -p improv_cli runs every sh block below in order and checks that each output block matches what the command before it printed (docs/tutorials/mdtest.py, called from crates/cli/tests/tutorials.rs).

You need the improv binary on your PATH (see Getting Started).

1. Create the model file

A model lives in one file. init creates it:

DB=budget.db
improv init "$DB"
initialized store at 'budget.db'

2. The categories and their items

A budget has two dimensions: the month, and the spending line. Each is a category, and its members are items. Every object takes a numeric id that you choose; the categories here are 1 and 2, the months 10 to 12, the lines 20 to 23.

improv add-category "$DB" 1 Month
improv add-category "$DB" 2 Line
improv add-item "$DB" 10 1 Jan
improv add-item "$DB" 11 1 Feb
improv add-item "$DB" 12 1 Mar
improv add-item "$DB" 20 2 Rent
improv add-item "$DB" 21 2 Groceries
improv add-item "$DB" 22 2 Utilities
improv add-item "$DB" 23 2 Fun

3. The input measures

A measure is a quantity, laid out over some of the categories. The budget is set once per line, so Budget is over Line only. What was spent varies by month and line. Income is per month.

improv add-measure "$DB" 100 Budget number input Line
improv add-measure "$DB" 101 Spent  number input Month Line
improv add-measure "$DB" 102 Income number input Month

Now type in the numbers. --at names the cell's coordinate, one Category=Item pair per dimension:

improv set "$DB" 100 1500 --at Line=Rent
improv set "$DB" 100 600  --at Line=Groceries
improv set "$DB" 100 200  --at Line=Utilities
improv set "$DB" 100 300  --at Line=Fun

improv set "$DB" 102 4200 --at Month=Jan
improv set "$DB" 102 4200 --at Month=Feb
improv set "$DB" 102 4500 --at Month=Mar

while read -r month line amount; do
  improv set "$DB" 101 "$amount" --at "Month=$month,Line=$line"
done <<'EOF'
Jan Rent      1500
Jan Groceries  640
Jan Utilities  230
Jan Fun        180
Feb Rent      1500
Feb Groceries  575
Feb Utilities  210
Feb Fun        420
Mar Rent      1500
Mar Groceries  590
Mar Utilities  160
Mar Fun        250
EOF

4. Formulas, written once

In a grid spreadsheet you would copy a formula into every cell. In Improv a formula is written once, over names, and applies to every cell of its measure. Left is what remains of each line's budget in each month; the budget has no Month, so the same budget figure is used for every month:

improv define "$DB" 200 "Left = Budget - Spent"
improv eval   "$DB" 200
defined derived measure 200 'Left'
eval 200 'Left' (12 cells):
  Left[Month=Feb, Line=Fun] = -120
  Left[Month=Feb, Line=Groceries] = 25
  Left[Month=Feb, Line=Rent] = 0
  Left[Month=Feb, Line=Utilities] = -10
  Left[Month=Jan, Line=Fun] = 120
  Left[Month=Jan, Line=Groceries] = -40
  Left[Month=Jan, Line=Rent] = 0
  Left[Month=Jan, Line=Utilities] = -30
  Left[Month=Mar, Line=Fun] = 50
  Left[Month=Mar, Line=Groceries] = 10
  Left[Month=Mar, Line=Rent] = 0
  Left[Month=Mar, Line=Utilities] = 40

eval lists cells sorted by item name. A negative number is an overspent line. OVER adds a measure up along a category. SUM(Spent OVER Line) is a month's total spending, so the savings are:

improv define "$DB" 201 "Saved = Income - SUM(Spent OVER Line)"
improv eval   "$DB" 201
defined derived measure 201 'Saved'
eval 201 'Saved' (3 cells):
  Saved[Month=Feb] = 1495
  Saved[Month=Jan] = 1650
  Saved[Month=Mar] = 2000

Formulas can use other formulas. The quarter's totals add up along Month:

improv define "$DB" 202 "QuarterSpent = SUM(Spent OVER Month)"
improv define "$DB" 203 "QuarterSaved = SUM(Saved OVER Month)"
improv eval   "$DB" 202
improv eval   "$DB" 203
defined derived measure 202 'QuarterSpent'
defined derived measure 203 'QuarterSaved'
eval 202 'QuarterSpent' (4 cells):
  QuarterSpent[Line=Fun] = 850
  QuarterSpent[Line=Groceries] = 1805
  QuarterSpent[Line=Rent] = 4500
  QuarterSpent[Line=Utilities] = 600
eval 203 'QuarterSaved' (1 cells):
  QuarterSaved[] = 5145

5. Change an input

A concert in February was refunded, so Fun in February is 300, not 420. Change the one cell; everything that depends on it follows:

improv set  "$DB" 101 300 --at Month=Feb,Line=Fun
improv eval "$DB" 201
improv eval "$DB" 203
set measure 101
eval 201 'Saved' (3 cells):
  Saved[Month=Feb] = 1615
  Saved[Month=Jan] = 1650
  Saved[Month=Mar] = 2000
eval 203 'QuarterSaved' (1 cells):
  QuarterSaved[] = 5265

6. Save views for the desktop app

A view is a saved layout of one measure: which categories run down the rows and across the columns. Save two, one for spending and one for what is left:

improv save-view "$DB" 1 "Spending"  Spent --rows Line --cols Month
improv save-view "$DB" 2 "Left over" Left  --rows Line --cols Month
improv list "$DB"
saved view 1 'Spending'
saved view 2 'Left over'
categories:
  1 Month
  2 Line
items:
  10 Jan (category 1)
  11 Feb (category 1)
  12 Mar (category 1)
  20 Rent (category 2)
  21 Groceries (category 2)
  22 Utilities (category 2)
  23 Fun (category 2)
measures:
  100 Budget [input] Number
  101 Spent [input] Number
  102 Income [input] Number
  200 Left [derived] Number
  201 Saved [derived] Number
  202 QuarterSpent [derived] Number
  203 QuarterSaved [derived] Number
views:
  1 Spending (measure Spent, 1 matrices)
  2 Left over (measure Left, 1 matrices)
inputs:
  100::{Line=Fun} = 300
  100::{Line=Groceries} = 600
  100::{Line=Rent} = 1500
  100::{Line=Utilities} = 200
  101::{Month=Feb,Line=Fun} = 300
  101::{Month=Feb,Line=Groceries} = 575
  101::{Month=Feb,Line=Rent} = 1500
  101::{Month=Feb,Line=Utilities} = 210
  101::{Month=Jan,Line=Fun} = 180
  101::{Month=Jan,Line=Groceries} = 640
  101::{Month=Jan,Line=Rent} = 1500
  101::{Month=Jan,Line=Utilities} = 230
  101::{Month=Mar,Line=Fun} = 250
  101::{Month=Mar,Line=Groceries} = 590
  101::{Month=Mar,Line=Rent} = 1500
  101::{Month=Mar,Line=Utilities} = 160
  102::{Month=Feb} = 4200
  102::{Month=Jan} = 4200
  102::{Month=Mar} = 4500

7. Open it in the desktop app

improv-gui budget.db

The model browser at the bottom left lists the two views, Spending - Worksheet and Left over - Worksheet. Double-click one to open it: the lines run down the rows and the months across. Click a Spent cell, type a new number and press Enter; the Left over worksheet and the savings update at once. The GUI saves each edit to budget.db as you make it, so improv eval sees it straight away; Ctrl+Z undoes it. The Desktop GUI describes every window.

In a terminal, improv-tui budget.db opens the same file, on the Spending view, as the GUI does.

What you used

CommandDid
initCreated the model file.
add-category, add-itemMade the dimensions.
add-measureMade an input measure over some categories.
setTyped a value into one cell.
defineAdded a formula, written once over names.
evalComputed a formula's cells.
save-viewSaved a layout for the GUI and TUI.

Every command is in the command-line reference. The formula language, including OVER and the other reductions, is in The Formula Language.