track/track

233 lines
5.9 KiB
Bash
Executable File

#!/bin/sh
# This file defines track - a minimalist data tracker
# Copyright (C) 2024 James Tomasino
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-----------------------------------------------------------------------
version="2024.04.15"
show_help () {
cat > /dev/stdout << END
track [options...] <metric> [<value>]
DIRECTORY:
If the environment variable \$TRACK_DIR is set, track will store
all data in that location. Otherwise the default is the current
working directory.
USAGE:
track weight 150 Log 150lbs for today's weight
track steps +500 Increase the numeric log of today's steps by 500
track -n 10 calories Show last 10 days calorie values
track mood Show last value logged for mood
OPTIONS:
-a Show all values of metric
-n N Show N values of metric
-x Remove metric file
-c Output CSV
-l List metrics being tracked
-h Show this help
-v Show current version info
-d Debug mode
END
}
# Determine tracking directory and ensure it is a directory
TRACK_DIR=${TRACK_DIR:-.}
if [ -f "$TRACK_DIR" ]; then
TRACK_DIR="$(dirname "${TRACK_DIR}")"
fi
arg_metric=""
arg_value=""
arg_listnum=""
arg_remove=""
flag_options="hvdan:x:lc"
flag_debug=0
flag_help=0
flag_listall=0
flag_listnum=0
flag_remove=0
flag_version=0
flag_shortlist=0
flag_csv=0
parse_input () {
if ! parsed=$(getopt ${flag_options} "$@"); then
printf "Invalid input\\n" >&2
exit 2
fi
eval set -- "${parsed}"
while true; do
case "$1" in
-h)
flag_help=1
shift
;;
-v)
flag_version=1
shift
;;
-d)
flag_debug=1
shift
;;
-a)
flag_listall=1
shift
;;
-n)
flag_listnum=1
shift
arg_listnum="$1"
shift
;;
-x)
flag_remove=1
shift
arg_remove="$1"
shift
;;
-l)
flag_shortlist=1
shift
;;
-c)
flag_csv=1
shift
;;
--)
shift
break
;;
*)
printf "Internal error: %s\\n" "$1" >&2
exit 3
;;
esac
done
while [ "$#" -gt 0 ]; do
if [ -z "${arg_metric}" ]; then
arg_metric="$(printf "%s" "${1}" | tr '[:upper:]' '[:lower:]')"
shift
else [ -z "${arg_value}" ]
arg_value="$*"
break
fi
done
}
main () {
parse_input "$@"
if [ ${flag_shortlist} -gt 0 ]; then
out=$(find "${TRACK_DIR}"/*.csv -maxdepth 1 2>/dev/null | sed -e 's/.*\///' | sed -e 's/\..*$//')
printf "%s\\n" "${out}"
exit 0
fi
if [ ${flag_version} -gt 0 ]; then
printf "%s\\n" "${version}"
exit 0
fi
if [ ${flag_help} -gt 0 ]; then
show_help
exit 0
fi
if [ ${flag_debug} -gt 0 ]; then
set -x
fi
if [ ${flag_remove} -gt 0 ]; then
rm "${TRACK_DIR}/${arg_remove}.csv"
fi
if [ "${arg_metric}" != "" ]; then
f="${TRACK_DIR}/${arg_metric}.csv"
# If new file, add header
if [ ! -f "$f" ]; then
printf "year,month,day,%s\\n" "${arg_metric}" > "$f"
fi
# Get arg parts
first_letter=$(printf %.1s "${arg_value}")
if [ "${arg_value}" != "" ]; then # add/update
d=$(date '+%Y,%0m,%0d')
l=$(sed -n "/$d/{=;}" "$f")
if [ "${first_letter}" = "+" ]; then # Add to existing value
rest="${arg_value#+}"
if [ -z "$l" ]; then
printf "%s,%s\\n" "$d" "${rest}" >> "$f"
else
val="$(sed "${l}q;d" "$f" | awk -F "," '{print $4}')" # get current value
# check if existing value is numeric before attempting to sum
if printf "%s" "$val" | grep -vEq '^[0-9]+([.][0-9]+)?$'; then
printf "Current value is not numeric. Abort.\\n"-
exit 1
fi
# check if new value is numeric before attempting to sum
if printf "%s" "$rest" | grep -vEq '^[0-9]+([.][0-9]+)?$'; then
printf "New value is not numeric. Abort.\\n"-
exit 1
fi
# sum new and previous values
newval=$((val+rest)) || exit 1
sed -i"" "$l c $d,${newval}" "$f"
fi
else # Replace existing value
if [ -z "$l" ]; then
printf "%s,%s\\n" "$d" "${arg_value}" >> "$f"
else
sed -i"" "$l c $d,${arg_value}" "$f"
fi
fi
else # display value
if [ ${flag_listall} -gt 0 ]; then
if [ ${flag_csv} -gt 0 ]; then
awk -F "," '{printf "%02d-%02d-%02d,%s\n", $1, $2, $3, $4;}' "$f"
else
awk -F "," '{printf "%02d-%02d-%02d\t%s\n", $1, $2, $3, $4;}' "$f"
fi
elif [ ${flag_listnum} -gt 0 ]; then
if [ ${flag_csv} -gt 0 ]; then
tail -n "${arg_listnum}" "$f" | awk -F "," '{printf "%02d-%02d-%02d,%s\n", $1, $2, $3, $4;}'
else
tail -n "${arg_listnum}" "$f" | awk -F "," '{printf "%02d-%02d-%02d\t%s\n", $1, $2, $3, $4;}'
fi
else
if [ ${flag_csv} -gt 0 ]; then
tail -n 1 "$f" | awk -F "," '{printf "%02d-%02d-%02d,%s\n", $1, $2, $3, $4;}'
else
tail -n 1 "$f" | awk -F "," '{printf "%02d-%02d-%02d\t%s\n", $1, $2, $3, $4;}'
fi
fi
fi
fi
}
main "$@"