costs

Tool to help you see the impact of recurring costs
Log | Files | Refs | README

commit a4ef393da25779ad13bb108a7ce22f21ec2d6363
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date:   Fri, 10 Mar 2023 14:20:58 -0800

Add costs.awk, total.awk, percentage.awk, costs, and README

This, I believe, is a good start to the project and is fairly usable
as is. Some future changes I'm thinking about are listed in the
README, but by and large it's already good enough.

Diffstat:
AREADME | 33+++++++++++++++++++++++++++++++++
Acosts | 17+++++++++++++++++
Acosts.awk | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apercentage.awk | 22++++++++++++++++++++++
Atotal.awk | 18++++++++++++++++++
5 files changed, 147 insertions(+), 0 deletions(-)

diff --git a/README b/README @@ -0,0 +1,33 @@ +This project contains some scripts intended to help you know how +much you're spending by and by. + +It works like this; you create an expenses file containing entries +with four fields separated by tabs[1], the first being the +period--either daily, weekly, bi-weekly, monthly, quarterly, or +yearly--the second being the amount, say "30" (as these programs +have no knowledge of currency, this could be 30 USD, 30 EUR, 30 +sheep, whatever you want), the third being a unique identifier[2] +(food, gas, etc.), and the fourth being an arbitrary description. +(The description leaves room for expansion, I myself put tags within +square-brackets (i.e. '[tag]'), but there is no official specification +as of yet.) Blank lines and lines proceeded by a hash ('#') are +ignored. + +This file is processed by costs.awk, which outputs the amount that +each entry costs per "period" (by default, daily, but any may be +specified) proceeded by their unique name[3]. + +It can then be further processed by one of the additional scripts +provided, total.awk and percentage.awk. Total produces a single +entry 'total' that is the sum of all the entries, and percentage +replaces the amount from each entry with the percentage of the sum +of all the entries. These may be accessed using the wrapper script +'costs'; the -t and -p flags respectively. + +[1] I'm contemplating allowing spaces to be field separaters too, but + ignoring seperaters after the description field begins. + +[2] If these names are not unique, behavior is undefined. + +[3] I'm considering including their description in the output, as a + third field. diff --git a/costs b/costs @@ -0,0 +1,17 @@ +#!/bin/sh +# Copyright 2023 Jacob R. Edwards <jacob@jacobedwards.org> +# Wrapper script for costs.awk, total.awk, percentage.awk, etc. + +filter=cat +while test $# -gt 0 +do + case "$1" in + (-t) filter=./total.awk ;; + (-p) filter=./percentage.awk ;; + (-*) echo 'invalid option' 1>&2 ;; + (*) break ;; + esac + shift +done + +costs.awk "$@" | $filter diff --git a/costs.awk b/costs.awk @@ -0,0 +1,57 @@ +#!/usr/bin/awk -f +# Copyright 2023 Jacob R. Edwards <jacob@jacobedwards.org> + +function error(msg) { + print("error: " msg) > "/dev/stderr"; +} + +function die(msg) { + status = msg; + error(status); + exit 1 +} + +BEGIN { + FS = " "; + OFS = FS; + OFMT = "%.2f"; + + period = 1; + amount = 2; + name = 3; + desc = 4; + + t["daily"] = 1; + t["weekly"] = 7; + t["bi-weekly"] = 14; + t["monthly"] = 365 / 12 # 'spose 30 or 30.4 could produce easier numbers + t["quarterly"] = t["monthly"] * 3; + t["yearly"] = 365; + + if (! ARGV[1]) { + output = "daily"; + } else { + output = ARGV[1]; + if (! output in t) + die(output " not a valid period"); + delete ARGV[1]; + --ARGC; + } +} + +/^[^# ]/ && ! /^$/ { + if (! $period in t) + die($period " not a valid period"); + daily = $amount / t[$period]; + if (maketotal) + total += daily * t[output]; + else + print($name, daily * t[output]); +} + +END { + if (status) + exit 1 + if (maketotal) + print("total", total); +} diff --git a/percentage.awk b/percentage.awk @@ -0,0 +1,22 @@ +#!/usr/bin/awk -f +# Copyright 2023 Jacob R. Edwards +# Takes the entries produced by costs.awk and gives the percentage +# each takes out of the sum of all. + +BEGIN { + FS = " "; + OFS = FS; + OFMT = "%.2f" +} + +{ + names[NR] = $1; + values[$1] = $2; + total += $2; +} + +END { + div = total / 100; + for (i in names) + print names[i], values[names[i]] / div; +} diff --git a/total.awk b/total.awk @@ -0,0 +1,18 @@ +#!/usr/bin/awk -f +# Copyright 2023 Jacob R. Edwards +# Takes the entries produced by costs.awk and produces the total +# of them all. + +BEGIN { + FS = "\t"; + OFS = FS; + OFMT = "%.2f"; +} + +{ + t += $2 +} + +END { + print "total", t +}