#!/bin/bash

#######################################################################
# to log the body wight and the body size of me and my family members
# needs gnuplot to render the graphics
#
# 2009 by Lukas Gantert at www.nofalab.ch
# GPL http://www.gnu.org/licenses/gpl.html
#######################################################################

VERSION=20100304

TODO='
- implement an install-routine

- put the configuration in a rc-file, located in the users $HOME directory

- internationalization

- enhance the comment function (vi in place of cat)

- allow to edit the $Member properties

- create a section "vaccinations"

- ask for blood group in the new-meber-dialog

- enhance the report function: pdf-output thru LaTeX;
  family report (averages);
  more details like last mesurements;
  possibility to customize the report

- commande-line options for help and version

- put all data in a sqlite database
'

BUGS='
- the -n option of the read command works only in bash

- css for printer (no background-colors)

- ...
'

# check for dependencies
gnuplot --version > /dev/null
if [ $? -ne 0 ] ; then
   echo -e "\a
   kann Programm nicht ausfuehren.
   Unerfuellte Abhaengigkeit.
   Bitte \"gnuplot\" installieren.
   "
   exit
fi

# substitution of: CMD=$(basename "$0") Thanks to ST.CH.
CMD="${0##*/}"

# set the terminal title
printf '\033]2;%s\007' "$CMD"

Browser=${BROWSER:-"firefox"}

# to allow spaces in Names
IFS='
'

# a funny field-separator in the $MEMBERS_FILE
SEP="@@@"

# files and dirs (other assignements in the function 'Select_member')
MAIN_DIR=${1:-$HOME/.health}
mkdir -p "$MAIN_DIR"

MEMBERS_FILE="$MAIN_DIR"/members.list
[ -f "$MEMBERS_FILE" ] || touch "$MEMBERS_FILE"

# units - to ensure a consistent use of units
WEIGHT_UNIT=kg
SIZE_UNIT=cm
TEMP_UNIT=°C

# colors for the output of 'printf'
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BLUE='\033[34m'
BOLD='\033[1m'
BACK='\033[0m'

# background-color for html-report
BG_COLOR=aedba7

# "now" in a human-readable format
function Now () {
NOW=$(date "+%Y.%m.%d-%H:%M:%S")
}

# horizontal rule, (c) Lukas Gantert
function Hrule () {
seq -s@ $(tput cols) | sed 's/[0-9]*[@]*/-/g'
}

function Start_Menu () {
printf "$BOLD%s$BACK%s $BOLD%s$BACK%s $BOLD%s$BACK%s $BOLD%s$BACK%s\n" \
"s" ":select-member" "n" ":new-member" "h" ":help" "q" ":quit"
}

function Member_Menu () {
printf "%s\n$BOLD$BLUE%s$BACK\n$BOLD%s$BACK%s $BOLD%s$BACK%s \
$BOLD%s$BACK%s $BOLD%s$BACK%s $BOLD%s$BACK%s\n" \
"press a key to select a action for the member" "$MEMBER" \
"w" ":wight" "l" ":size" "t" ":temperature" "c" ":comment" "r" ":report"
}

function Help () {
IFS=" "
man -l -- - << EOF
.TH $CMD 1 $VERSION
.UC 4
.SH NAME
$CMD \- an interactive health data logger

.SH SYNOPSIS
.B $CMD
[directory]

.SH DESCRIPTION
.I $CMD
is a interactive health data logger with a simple
html-report-generator.  The first aim was to log the body growth of
my childrens. For now, with
.I $CMD
it is easy to track changes of the
body size and the body wight, and there is a possibility to take
notes.

The report shows two graphs, illustrating the
evolution of the body wight and the body size.
.I $CMD
use
.I gnuplot
to create the diagrams. Also, there will be listed all the notes and the
age of the person.

You are encouraged to modify this shellscript (bash) to log
whatever.

.SH ACTIONS
Press the appropriate key to perform an action.

.SS select-member
This action lists all the members registred in $MEMBERS_FILE and lets you
choise a member to perform a
.I member-action
on it.

.SS new-member
This action lets you append a
member to the member-list. ($MEMBERS_FILE) You will been asked for
the name, the birth date and the gender of the new member.

To preserve the consistency of the member-list, the name of the new
member is converted to upper-casei, and leading, padding and double
spaces are removed. If the name is unique, the data is appended to
the member-list-file and a directory with the new member's name is
created, to keep the data-files for the new member.  Spaces in the
name are allowed. For example, "  hanS      MeieR" gives "HANS MEIER"

.SH MEMBER-ACTIONS
Press the appropriate key to perform an action.

.SS weight
To append the current wight of the selected member together with a
date-time-stamp to the members wight-file.

.SS size
To append the current size of the selected membere together with a
date-time-stamp to the members size-file.

.SS comment
To note some comments (diseases, vaccinations, ... ). For now, its
only the archaic "cat" editor. He allows multiple lines but not a
navigation in the text. Use Ctrl-d twice to quit. The comment will
be appended to the comment-file together with a date-time-stamp.

.SS report
This action creates automatically a html-report for the selected
member.

.SH FILES
If
.I $CMD
is called without any directory as argument, the default is
\$HOME/.health.

To fit old personal data in the datafiles, edit those with a text editor.

.SH AUTHOR
Lukas Gantert
http://nofalab.ch

.SH VERSION
$CMD, Version $VERSION.

Ce programme est un logiciel libre.  Vous pouvez en redistribuer
des copies selon les termes de la License Publique Générale de GNU,
<http://www.gnu.org/licenses/gpl.html>.  AUCUNE GARANTIE n'est fournie
tel que permis selon la loi."

.SH TODO
$TODO

.SH BUGS
$BUGS

Please send bug-reports and sugestions to the author.

.SH SEE ALSO
gnuplot, bash.
EOF
IFS='
'
Start
}

#######################################################################
# begin of "create a new member"
#######################################################################
function Read_new_member_name () {
if [ ! -w "$MEMBERS_FILE" ]; then
   printf "\n$RED%s$BACK\n\n" \
   "Sorry, you have no rights to write to the members-list !"
   Start
fi
printf "\n%s\n" "give a new member name please:"
read NEW_MEMBER
# the next line ensure uniq names (without leading and trailing spaces
#+nor double spaces, and also converts all letters in capitals)
NEW_MEMBER="$(echo "$NEW_MEMBER" | tr '[:lower:]' '[:upper:]' |
tr -s '[:space:]' ' ' | sed -e 's/^ //;s/ $//')"
grep -q ^"$NEW_MEMBER"$ "$MEMBERS_FILE"
if [ $? -eq 0 ]; then
   printf "$RED%s$BACK\n" \
   "$NEW_MEMBER exists already; choose an other name!"
   mkdir -p "$MAIN_DIR"/"$NEW_MEMBER"
   Start
else
   Read_new_member_date_of_birth
fi
}

function Read_new_member_date_of_birth () {
read -p "give the date of birth for \""$NEW_MEMBER"\" (YYYYmmdd): " DATE_OF_BIRTH
date -d "$DATE_OF_BIRTH" > /dev/null
if [ $? -eq 0 ]; then
   Read_new_member_gender
else
   Read_new_member_date_of_birth
fi
}

function Read_new_member_gender () {
printf "give the gender for \""$NEW_MEMBER"\" f:femal m:mal:\n"
read -s -n1 GENDER
printf ""$GENDER"" | grep -q "^[fm]$"
if [ $? -eq 0 ]; then
   Write_new_member_data
else
   printf "\""$GENDER"\" is not valid.\n"
   Read_new_member_gender
fi
}

function Write_new_member_data () {
printf "%s$BLUE%s$BACK%s%s%s\n%s\n" \
"Add " \
"\"$NEW_MEMBER\"" \
", born at \"$DATE_OF_BIRTH\" " \
"with the gender \"$GENDER\" " \
"to the member-list?" \
"y:yes n:no"
read -s -n1 yes_no
case $yes_no in
   y|Y ) 
   printf ""$NEW_MEMBER""$SEP""$DATE_OF_BIRTH""$SEP""$GENDER"\n" >> "$MEMBERS_FILE"
   mkdir "$MAIN_DIR"/"$NEW_MEMBER"
   printf "$NEW_MEMBER added to the member-list.\n"
   Start
   ;;
   n|N ) Start ;;
   * ) Write_new_member_data ;;
esac
}

function New_member () {
Read_new_member_name
}

#######################################################################
# end of "create a new member"
#######################################################################

function Select_member () {
Hrule
[ -s "$MEMBERS_FILE" ] || ( printf "$RED%s$BACK\n" \
"no members in memberlist - create first a new member!" && Start; )
MEMBERLIST=$(awk -F "$SEP" '{print $1}' "$MEMBERS_FILE" | sort)
PS3="select a member: "
select MEMBER in $MEMBERLIST ; do
if [ -z $MEMBER ]; then
   Select_member
else
   WEIGHT_LOG="$MAIN_DIR"/"$MEMBER"/"$MEMBER"_weight.log
   WEIGHT_IMG="$MAIN_DIR"/"$MEMBER"/"$MEMBER"_weight.png
   SIZE_LOG="$MAIN_DIR"/"$MEMBER"/"$MEMBER"_size.log
   SIZE_IMG="$MAIN_DIR"/"$MEMBER"/"$MEMBER"_size.png
   TEMPERATURE_LOG="$MAIN_DIR"/"$MEMBER"/"$MEMBER"_temperature.log
   TEMPERATURE_IMG="$MAIN_DIR"/"$MEMBER"/"$MEMBER"_temperature.png
   COMMENT_LOG="$MAIN_DIR"/"$MEMBER"/"$MEMBER"_comment.log
   REPORT_HTML="$MAIN_DIR"/"$MEMBER"/"$MEMBER"_report.html
   Member_Actions
fi
done
}

function Member_Actions () {
Hrule; Member_Menu; Start_Menu
read -s -n 1 ANSWER
case $ANSWER in
   w ) Wight ;;
   l ) Size ;;
   t ) Temperature ;;
   c ) Comment ;;
   r ) Report ;;
   n ) New_member ;;
   s ) Select_member ;;
   h ) Help ;;
   v ) Version ;;
   q ) printf '\033]2;%s\007' "Terminal" ; exit ;;
   [[:upper:]] ) printf "\n$RED%s\n%s$BACK\n\n" \
   "Sorry, there are no capital letters accepted." \
   "have you Caps-look activated?"; Member_Actions ;;
   * ) Member_Actions ;;
esac
}

function Wight () {
read -p "give the todays wight of "$MEMBER" ("$WEIGHT_UNIT"): " WEIGHT
printf "%g\n" $WEIGHT 2> /dev/null
if [ $? -eq 0 ]; then
   printf "%s%s\n%s\n" \
   "do you want write the wight of $WEIGHT $WEIGHT_UNIT to the " \
   ""$MEMBER"'s log-file?" \
   "y:yes  all other keys:no :"
   read -s -n1 yes_no
   case $yes_no in
      y ) Now; printf "%s %3.1f %s\n" \
      "$NOW" "$WEIGHT" "$WEIGHT_UNIT" >> "$WEIGHT_LOG"
      printf "$GREEN%s$BACK%s\n" "$NOW $WEIGHT $WEIGHT_UNIT" " written to "$WEIGHT_LOG""
      ;;
      * ) Member_Actions
      ;;
   esac
else
   printf "$RED%s$BACK\n" "please enter a valid number (like \"72.3\")"
   Wight
fi
Member_Actions
}

function Size () {
read -p "give the todays size of "$MEMBER" ("$SIZE_UNIT"): " SIZE
printf "%g\n" $SIZE 2> /dev/null
if [ $? -eq 0 ]; then
   printf "%s\n%s\n" \
   "do you want write the size of $SIZE $SIZE_UNIT to "$MEMBER"'s log-file?" \
   "y:yes  all other keys:no :"
   read -s -n1 yes_no
   case $yes_no in
      y ) Now;
      printf "%s %3.1f %s\n" \
      "$NOW" "$SIZE" "$SIZE_UNIT" >> "$SIZE_LOG"
      printf "$GREEN%s$BACK%s\n" "$NOW $SIZE $SIZE_UNIT" " written to "$SIZE_LOG""
      ;;
      * ) Member_Actions
      ;;
   esac
else
   printf "$RED%s$BACK\n" "please enter a valid number (like \"164.3\")"
   Size
fi
Member_Actions
}

function Temperature () {
read -p "give the temperature of "$MEMBER" ("$TEMP_UNIT"): " TEMPERATURE
printf "%g\n" $TEMPERATURE 2> /dev/null
if [ $? -eq 0 ]; then
   printf "%s%s%s\n%s\n" \
   "do you want write the temperature of " \
   "$TEMPERATURE $TEMP_UNIT " \
   "to the "$MEMBER"'s log-file?" \
   "y:yes  all other keys:no :"
   read -s -n1 yes_no
   case $yes_no in
      y ) Now; printf "%s %2.1f %s\n" \
      "$NOW" "$TEMPERATURE" "$TEMP_UNIT" >> "$TEMPERATURE_LOG"
      printf "$GREEN%s$BACK%s\n" "$NOW $TEMPERATURE $TEMP_UNIT" " written to "$TEMPERATURE_LOG""
      ;;
      * ) Member_Actions
      ;;
   esac
else
   printf "$RED%s$BACK\n" "please enter a valid number (like \"38.35\")"
   Temperature
fi
Member_Actions
}

function Glucose () {
read -p "give the todays wight of "$MEMBER" ("$GLUCOSE_UNIT"): " GLUCOSE
printf "%g\n" $GLUCOSE 2> /dev/null
if [ $? -eq 0 ]; then
   printf "%s%s\n%s\n" \
   "do you want write the wight of $GLUCOSE $GLUCOSE_UNIT to the " \
   ""$MEMBER"'s log-file?" \
   "y:yes  all other keys:no :"
   read -s -n1 yes_no
   case $yes_no in
      y ) Now; printf "%s %3.1f %s\n" \
      "$NOW" "$GLUCOSE" "$GLUCOSE_UNIT" >> "$GLUCOSE_LOG"
      printf "$GREEN%s$BACK%s\n" "$NOW $GLUCOSE $GLUCOSE_UNIT" " written to "$GLUCOSE_LOG""
      ;;
      * ) Member_Actions
      ;;
   esac
else
   printf "$RED%s$BACK\n" "please enter a valid number (like \"72.3\")"
   Wight
fi
Member_Actions
}

function Comment () {
echo -e "\nwrite a note to the "$MEMBER"\' log:"
{
IFS=""
NOTE="$(cat)"
printf "\n%s\n$GREEN%s$BACK\n%s\n%s\n" \
"do you want to write" "$NOTE" "to "$MEMBER"'s log-file?" \
"y:yes  all other keys:no :"
read -s -n1 yes_no
case $yes_no in
   y ) Now; printf "\n%s\n%s\n%s\n" \
   "$NOW" "*******************" "$NOTE" >> "$COMMENT_LOG"
   printf "$GREEN%s\n%s\n%s$BACK\n%s\n" "$NOW" "*******************" "$NOTE" \
   "written to "$COMMENT_LOG""
   ;;
   * ) Member_Actions
   ;;
esac ;
}
Member_Actions
}

function Report () {

# create infos about the age of $MEMBER
BIRTHDAY="$(date -d "$(awk -F "$SEP" '/'$MEMBER'/{print $2}' "$MEMBERS_FILE")" "+%s")"

eval "$(date '+x="%s" y="%c"')"
AGE="$(( $x - $BIRTHDAY ))"

SHOW_AGE=$(awk -v Nom="$MEMBER" -v Age="$AGE" -v Now="$y" '
BEGIN {
OFMT="%f"
print "\nNow, " Now ",\n" Nom " has the age of:\n"
printf "%10.0f    %s\n", Age/1, "seconds or"
printf "%10.0f    %s\n", Age/60, "minutes or"
printf "%10.0f    %s\n", Age/60/60, "hours or"
printf "%10.0f    %s\n", Age/60/60/24, "days or"
printf "%12.1f  %s\n", Age/60/60/24/30.416667, "month or"
printf "%13.2f %s\n", Age/60/60/24/30.416667/12, "years"
exit }')

# print graphs
gnuplot <<EOF

# general settings
set terminal png tiny size 700,330 x$BG_COLOR
set xlabel "date"
set xdata time
set timefmt "%Y.%m.%d-%H:%M:%S"
set format x "%Y.%m.%d\n%H:%M:%S"
set grid xtics mxtics ytics mytics
#set samples 900

# Weight
set output "$WEIGHT_IMG"
set title "$MEMBER: Weight [Kg]"
set ylabel "Wight [Kg]"
plot "$WEIGHT_LOG" using 1:2  title "Weight" with linespoints#, \
#"" using 1:2 smooth bezier title "non-linear interpolation"

# Size
set output "$SIZE_IMG"
set title "$MEMBER: Size [cm]"
set ylabel "Size [cm]"
plot "$SIZE_LOG" using 1:2 title "Size" with linespoints#, \
#"" using 1:2 smooth csplines title "non-linear interpolation"


# Temperature
#set output "$TEMPERATURE_IMG"
#set title "$MEMBER: Temperature [°C]"
#set ylabel "Temperature [°C]"
#plot "$TEMPERATURE_LOG" using 1:2 title "Temperature" with linespoints#, \
#"" using 1:2 smooth csplines title "non-linear interpolation"
EOF

# generate a html-file with Birthday infos and the plots
cat > "$REPORT_HTML" <<EOF
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
   <meta http-equiv="Content-Type" content= "text/html;
   charset=ISO-8859-1"/>
   <meta name="generator" content="$CMD at
   http://www.nofalab.ch"/>
   <title>Health data of "$MEMBER"</title>
   <style type="text/css">
      body {background-color: #$BG_COLOR}
      div.c1 {text-align: center}
   </style>
</head>

<body>
<a name="top"></a>
<div class="c1">
   <h1>Health data of $MEMBER</h1>
</div>
<a href="#toc1">Body Weight</a><br>
<a href="#toc2">Body Size</a><br>
<!-- <a href="#toc3">Body Temperature</a><br> -->
<a href="#toc4">Comments</a>

<pre>$SHOW_AGE</pre>

<div class="c1">
   <hr>
   <h2><a name="toc1">Body Weight of "$MEMBER"</a></h2>
   <img src="$(basename $WEIGHT_IMG |
   awk '{gsub(/ /, "%20"); print $0}')" alt="Weight Graph of $MEMBER">
</div>
<pre><small>$(cat "$WEIGHT_LOG")</small></pre>

<div class="c1">
   <hr>
   <h2><a name="toc2">Body Size of "$MEMBER"</a></h2>
   <img src="$(basename $SIZE_IMG |
   awk '{gsub(/ /, "%20"); print $0}')" alt="Size Graph of $MEMBER">
</div>
<pre><small>$(cat "$SIZE_LOG")</small></pre>

<!--
<div class="c1">
   <hr>
   <h2><a name="toc3">Body Temperature of "$MEMBER"</a></h2>
   <img src="$(basename $TEMPERATURE_IMG |
   awk '{gsub(/ /, "%20"); print $0}')" alt="Temperature Graph of $MEMBER">
</div>
<pre><small>$(cat "$TEMPERATURE_LOG")</small></pre>
-->

<div class="c1">
   <hr>
   <h2><a name="toc4">Comments on "$MEMBER"</a></h2>
</div>

<pre><small>$(fmt -s -w72 "$COMMENT_LOG")</small></pre>

<!-- footer -->
<div class="c1">
   <hr>
   created with $CMD by <a href="http://nofalab.ch">nofalab</a>, using
   <a href="http://www.gnu.org/software/bash/manual/bashref.html">bash </a>
   and <a href="http://www.gnuplot.info">gnuplot</a>
</div>
</body>
</html>
EOF

"$Browser" "$REPORT_HTML" &

Member_Actions
}

function Start () {
Hrule
Start_Menu
read -s -n 1 ANSWER # good only for bash
# read -n1 ANSWER
case $ANSWER in
   s ) Select_member ;;
   n ) New_member ;;
   h ) Help ;;
   q ) printf '\033]2;%s\007' "Terminal" ; exit ;;
   [[:upper:]] ) printf "\n$RED%s\n%s$BACK\n\n" \
   "Sorry, there are no capital letters accepted." \
   "is Caps-look activated?"; Start
   ;;
   * ) Start ;;
esac
}

Start