#!/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 . #----------------------------------------------------------------------- version="2024.04.15" show_help () { cat > /dev/stdout << END track [options...] [] 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 "$@"