#!/bin/bash # Scales of Truth # # (c) 2011 Thomas White # # 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 . # Prerequisites: "git", "perl", "bash" and "gnuplot" # # Setup instructions: # # Save this script somewhere sensible like your home directory's "bin" folder # and make sure it's in your PATH and marked as executable. # Edit the variables below. # Run "sot" on its own. # Put your height in metres into $SOTDIR/height_in_metres. # Run "sot " to input your first weight reading. # Go into your "SOTDIR", and push the "master" branch to a suitable remote # repository, and set up tracking. # # The final step is optional, but it'll complain at you until you do it. # Directory in which data will be stored, backed up by Git SOTDIR=~/.sot # Filename for raw data LOGFILE=${SOTDIR}/data # File containing your height in metres HEIGHTFILE=${SOTDIR}/height_in_metres # Units of measurement UNITS=kg # Number of kilojoules per "unit" of fat KJ_PER_UNIT=29288 # A conversion factor (don't touch this) KCAL_PER_KJ=4.184 # Graph vertical axis minimum and maximum G_MIN=40 G_MAX=200 function write_perl () { cat > $SOTDIR/add-reading.pl << EOF #!/usr/bin/perl -w use strict; use constant SECONDS_TO_DAYS => (60.0*60.0*24.0); my \$new_weight = \$ARGV[0]; my \$old_trend = \$ARGV[1]; my \$newtime = \$ARGV[2]; my \$oldtime = \$ARGV[3]; my \$file = \$ARGV[4]; my \$old_weight = \$ARGV[5]; my \$weightdiff = \$new_weight-\$old_weight; open(FH, ">> \$file") or die("Couldn't open \$file\n"); my \$timediff = int(((\$newtime - \$oldtime)/SECONDS_TO_DAYS)+0.5); if ( \$timediff > 1 ) { printf("%i days since last reading, interpolating...\n", \$timediff); my \$day; my \$wd = \$weightdiff / \$timediff; for ( \$day=\$timediff-1; \$day>=0; \$day-- ) { my \$this_weight = \$new_weight-\$wd*\$day; my \$this_trend = \$old_trend + 0.1*(\$this_weight-\$old_trend); printf(FH "%i %.2f %.2f %.2f\n", \$newtime-\$day*SECONDS_TO_DAYS, \$this_weight, \$this_trend, \$this_trend - \$old_trend); \$old_trend = \$this_trend; } } else { my \$this_trend = \$old_trend + 0.1*(\$new_weight-\$old_trend); printf(FH "%i %.2f %.2f %.2f\n", \$newtime, \$new_weight, \$this_trend, \$this_trend - \$old_trend); } close(FH); exit 0; EOF chmod +x $SOTDIR/add-reading.pl } function check_setup () { # Check git is installed git --help > /dev/null 2>&1 if [ $? -ne 0 ]; then echo "Git is not installed" exit 1 fi # Check bc is installed bc --help > /dev/null 2>&1 if [ $? -ne 0 ]; then echo "bc is not installed" exit 1 fi # Check dir exists, create it if not if [ ! -d $SOTDIR ]; then if [ -e $SOTDIR ]; then echo "$SOTDIR exists, but isn't a directory!" exit 1 fi mkdir $SOTDIR echo "Created $SOTDIR." cd $SOTDIR git init fi if [ ! -f $SOTDIR/add-reading.pl ]; then write_perl fi if [ ! -f $HEIGHTFILE ]; then echo "Now put your height in metres into $HEIGHTFILE:" echo "\$ echo 1.82 > $HEIGHTFILE" exit 1; fi } function show_change () { local CUR_TREND OLD_TREND CHANGE if [ "`wc -l $LOGFILE | cut -d" " -f 1`" -lt $1 ]; then echo "Not enough readings for $1 day statistics yet." else CUR_TREND=`tail -n 1 $LOGFILE | cut -d" " -f 3` OLD_TREND=`tail -n $1 $LOGFILE | head -n 1 | cut -d" " -f 3` CHANGE=`echo "scale=1; $CUR_TREND - $OLD_TREND" | bc` ENERGY_KJ=`echo "scale=1; ($CHANGE * $KJ_PER_UNIT)/$1" | bc` ENERGY_KCAL=`echo "scale=1; $ENERGY_KJ / $KCAL_PER_KJ" | bc` echo -n "$1-day change: $CHANGE $UNITS" echo " ($ENERGY_KJ kJ or $ENERGY_KCAL kcal per day)" fi } function show_stats () { local CUR_TREND OLD_TREND CHANGE BMI HEIGHT CUR_VAL CUR_TREND=`tail -n 1 $LOGFILE | cut -d" " -f 3` CUR_VAL=`tail -n 1 $LOGFILE | cut -d" " -f 2` HEIGHT=`cat $HEIGHTFILE` BMI=`echo "scale=1; $CUR_VAL / ($HEIGHT * $HEIGHT)" | bc` BMI="$BMI $UNITS/m^2" echo "Current mass estimate: $CUR_TREND $UNITS (instantaneous BMI $BMI)" show_change 7 show_change 30 } function do_backup () { cd $SOTDIR git push > /dev/null 2>&1 if [ $? -ne 0 ]; then echo "'git push' failed." echo -n "You need to manually set up the remote repository and" echo -n " branch tracking, otherwise you might lose data, which" echo "would suck." fi } function add_reading () { local NEW_WEIGHT OLD_TREND NEW_TREND DELTA OLD_DATETIME OLD_WEIGHT NEW_WEIGHT=`eval echo $1` # Get date/time DATETIME=`date +%s` if [ -f $LOGFILE ]; then # Get the most recent trend value OLD_TREND=`tail -n 1 $LOGFILE | cut -d" " -f 3` OLD_WEIGHT=`tail -n 1 $LOGFILE | cut -d" " -f 2` OLD_DATETIME=`tail -n 1 $LOGFILE | cut -d" " -f 1` ${SOTDIR}/add-reading.pl $NEW_WEIGHT $OLD_TREND \ $DATETIME $OLD_DATETIME \ $LOGFILE $OLD_WEIGHT # Calculate new trend NEW_TREND=`echo "$OLD_TREND+0.1*($NEW_WEIGHT-$OLD_TREND)" | bc` else NEW_TREND=$NEW_WEIGHT echo "${DATETIME} ${NEW_WEIGHT} ${NEW_TREND}" >> $LOGFILE fi cd $SOTDIR git add $LOGFILE if [ $? -ne 0 ]; then echo "'git add' failed!" exit 1 fi git commit -m "New measurement, time = $DATETIME" > /dev/null if [ $? -ne 0 ]; then echo "'git commit' failed!" exit 1 fi do_backup } function draw_graph () { echo 'set xdata time' > $SOTDIR/plot.gp echo 'set xlabel "Date"' >> $SOTDIR/plot.gp echo "set ylabel \"Mass / $UNITS\"" >> $SOTDIR/plot.gp echo "set y2label \"Energy surplus / kCal [red, thin]\"" \ >> $SOTDIR/plot.gp echo 'set timefmt "%s"' >> $SOTDIR/plot.gp echo 'set format x "%d %b %Y"' >> $SOTDIR/plot.gp echo 'set xtics nomirror rotate by -60' >> $SOTDIR/plot.gp echo 'set ytics nomirror' >> $SOTDIR/plot.gp echo 'unset key' >> $SOTDIR/plot.gp echo 'set grid' >> $SOTDIR/plot.gp echo 'set ytics' >> $SOTDIR/plot.gp echo 'set y2tics' >> $SOTDIR/plot.gp echo 'set y2range [-2000:+500]' >> $SOTDIR/plot.gp echo -n "plot [] [$G_MIN:$G_MAX] \"$LOGFILE\" ">> $SOTDIR/plot.gp echo "using 1:2 w lp pt 5 lc 0 axis x1y1" >> $SOTDIR/plot.gp echo "replot \"$LOGFILE\" using 1:3 w l lw 2 lc 3 axis x1y1" \ >> $SOTDIR/plot.gp echo -n "replot \"$LOGFILE\" using 1:(\$4*$KJ_PER_UNIT/$KCAL_PER_KJ)" \ >> $SOTDIR/plot.gp echo " w l lw 1 lc 8 axis x1y2" >> $SOTDIR/plot.gp # echo "fit a*x+b '$LOGFILE' using 1:3 via a,b" >> $SOTDIR/plot.gp # echo "replot a*x+1000" >> $SOTDIR/plot.gp gnuplot -persist < $SOTDIR/plot.gp } function show_help () { echo "`basename $0`: A dodgy shell script" } READING=false check_setup for OPT in `getopt -lhelp,graph,stats,backup -o hgs -- $@`; do if [ x$READING = "xtrue" ]; then add_reading $OPT show_stats fi if [ $OPT = "--help" -o $OPT = "-h" ]; then show_help exit 0 fi if [ $OPT = "--graph" -o $OPT = "-g" ]; then draw_graph exit 0 fi if [ $OPT = "--stats" -o $OPT = "-s" ]; then show_stats exit 0 fi if [ $OPT = "--backup" ]; then do_backup exit 0 fi if [ $OPT = "--" ]; then READING=true fi done exit 0