diff --git a/glucometer_graphs.pl b/glucometer_graphs.pl index 3c52f61..821eff1 100644 --- a/glucometer_graphs.pl +++ b/glucometer_graphs.pl @@ -1,4 +1,10 @@ #!/usr/bin/perl +# +# Perl script to convert CSV output from glucometer into graphs, using gnuplot. +# +# Author: Timothy Allen +# License: MIT +# use strict; use warnings; @@ -10,6 +16,8 @@ my $error = "Usage: $0 --input --output [--max ] [- my @lines; my %seen; my @data; +my @avg_data; +my $a1c_calc; my $gnuplot_data; my $total_graphs; my $count_graphs = 0; @@ -21,12 +29,14 @@ my $output = ""; my $max_glucose = 8; my $min_glucose = 4; my $graph_max = 21; +my $units = 'mmol/L'; my $days_per_page = 2; GetOptions ("input=s" => \$input, # The name of the CSV file from which to read values "output=s" => \$output, # The name of the PDF file to output "high:f" => \$max_glucose, # The high end of your target blood glucose level "low:f" => \$min_glucose, # The low end of your target blood glucose level "max:i" => \$graph_max, # The highest displayed glucose level on each graph + "units:s" => \$units, # mmol/L or mg/dL "graphs:i" => \$days_per_page) # The number of days printed on each page or die $error; @@ -43,15 +53,9 @@ close( $ifh ) # Set up basic gnuplot options for reading the CSV data push @data, qq( -set datafile separator "," -set xdata time -set timefmt "%Y-%m-%d %H:%M:%S" -#set timefmt "%H:%M:%S" +set terminal pdf size 29.7cm,21.0cm enhanced font 'Calibri,14' linewidth 1 +#set output '$output' -set format x "%s" timedate -set format y "%.2f" numeric - -set samples 10000 ); # Get the list of days for which to produce graphs @@ -68,7 +72,7 @@ $total_graphs = scalar keys %seen; foreach my $d ( sort keys %seen ) { my $label = "$1$2$3" if ( $d =~ m#(\d{4})-(\d{2})-(\d{2})# ); push @data, qq( -\$Data << EOD +\$Data$label << EOD "timestamp","blood glucose","meal","method","comment"); foreach my $row (@lines) { if ( $row =~ s#^"($d )#"$1#ms ) { @@ -78,13 +82,24 @@ foreach my $d ( sort keys %seen ) { push @data, qq(EOD); push @data, qq( +set datafile separator "," + +set timefmt "%Y-%m-%d %H:%M:%S" +set format x "%s" timedate +set format y "%.2f" numeric +set samples 10000 +set xdata +stats \$Data$label using 2 +Mean$label = STATS_mean + +set xdata time set table \$SmoothData$label -#plot \$Data using "timestamp":"blood glucose" -#plot \$Data using "timestamp":"blood glucose" smooth frequency -plot \$Data using "timestamp":"blood glucose" smooth mcsplines -#plot \$Data using "timestamp":"blood glucose" smooth bezier +#plot \$Data$label using "timestamp":"blood glucose" +#plot \$Data$label using "timestamp":"blood glucose" smooth frequency +plot \$Data$label using "timestamp":"blood glucose" smooth mcsplines +#plot \$Data$label using "timestamp":"blood glucose" smooth bezier unset table -undefine \$Data +undefine \$Data$label ); } @@ -96,23 +111,21 @@ push @data, qq( reset set datafile separator whitespace -set terminal pdf size 29.7cm,21.0cm enhanced font 'Calibri,14' linewidth 1 -#set output '$output' set key off set style data lines set xdata time set timefmt "%H:%M:%S" +set format x "%H:%M" timedate +set format y "%.0f" numeric + +set yrange [0:$graph_max] +# If extended to 23:59, the x grid overlaps with the border +set xrange ["00:00":"23:58"] set style line 100 dt 3 lw 1 lc rgb "#202020" set style line 101 dt 1 lw 1 lc rgb "#202020" set linetype 110 lc rgb "red" -set format x "%H:%M" timedate -set format y "%.0f" numeric -set yrange [0:$graph_max] -# If extended to 23:59, the x grid overlaps with the border -set xrange ["00:00":"23:58"] - set lmargin 12 set rmargin 10 set tmargin 5 @@ -131,17 +144,34 @@ foreach my $d ( sort keys %seen ) { $count_graphs++; + # Algorithms for calculating HbA1c + if ( $units =~ /mg/i ) { + $a1c_calc = "(AVG + 46.7) / 28.7"; + } else { # Assume mmol/L + $a1c_calc = "(AVG + 2.59) / 1.59"; + } + push @data, qq( set title "Daily Glucose Summary for $title" font "Calibri,18" set xlabel "Time" offset 0,-0.25 set ylabel "Blood glucose" set xtics left tc rgb "#000000" set ytics 2 tc rgb "#000000" -set grid ytics ls 100 +set grid ytics ls 100 front #set arrow from graph 0,first $min_glucose to graph 1,first $min_glucose ls 6 lw 2 nohead #set arrow from graph 0,first $max_glucose to graph 1,first $max_glucose ls 6 lw 2 nohead -set object 1 rect from 0,first $min_glucose to graph 1,first $max_glucose fc ls 6 fs solid 0.2 back +set object 1 rect from graph 0, first $min_glucose to graph 1,first $max_glucose fc ls 6 fs solid 0.2 back + +AVG = Mean$label +AVG_LABEL = gprintf("Average glucose: %.2f", AVG) +set object 2 rect at graph 0.9, graph 0.9 fc ls 2 fs transparent solid 0.5 front size char strlen(AVG_LABEL), char 3 +set label 2 AVG_LABEL at graph 0.9, graph 0.9 front center + +#A1C = $a1c_calc +#A1C_LABEL = gprintf("Average A1c: %.1f", A1C) +#set object 3 rect at graph 0.07, graph 0.9 fc ls 5 fs transparent solid 0.5 front size char strlen(A1C_LABEL), char 3 +#set label 3 A1C_LABEL at graph 0.07, graph 0.9 front center #plot \$SmoothData$label using 1:2:( \$2 > $max_glucose || \$2 < $min_glucose ? 110 : $count_graphs ) with linespoints ls 120 lc variable plot \$SmoothData$label using (strftime("%H:%M:%S", \$1)):2:( \$2 > $max_glucose || \$2 < $min_glucose ? 110 : 1 ) with lines lw 3 lc variable @@ -157,7 +187,7 @@ set xtics tc rgb "#ffffff00" set ytics tc rgb "#ffffff00" unset grid unset object 1 -set grid xtics ls 101 back +set grid xtics ls 101 plot 1/0 ); @@ -168,12 +198,116 @@ plot 1/0 } } + +# Output data averages by hour of the day +push @data, qq( +\$DataAvg << EOD +"timestamp","blood glucose","meal","method","comment"); +my @sorted_rows; +foreach my $row (@lines) { + if ( $row =~ s#^"\d{4}-\d{2}-\d{2} #"#ms ) { + push @sorted_rows, $row; + } +} +push @data, sort @sorted_rows; +push @data, qq(EOD); + +push @data, qq( +reset +set datafile separator "," + +set timefmt "%H:%M:%S" +set format x "%s" timedate +set format y "%.2f" numeric +set samples 10000 +set xdata +stats \$DataAvg using 2 +MeanTotal = STATS_mean + +set xdata time +set table \$SmoothDataAvg +plot \$DataAvg using 1:2 smooth bezier +unset table + +set table \$TableDataAvg +plot \$DataAvg using 1:2 smooth mcsplines +unset table + +undefine \$DataAvg + +reset +set datafile separator whitespace + +set key off +set style data lines +set xdata time +set timefmt "%H:%M:%S" +set format x "%H:%M" timedate +set format y "%.0f" numeric + +set yrange [0:$graph_max] +# If extended to 23:59, the x grid overlaps with the border +set xrange ["00:00":"23:58"] + +set style line 100 dt 3 lw 1 lc rgb "#202020" +set style line 101 dt 1 lw 1 lc rgb "#202020" +set linetype 110 lc rgb "red" + +set lmargin 12 +set rmargin 10 +set tmargin 5 +set bmargin 5 + +set multiplot title layout $days_per_page,1 + +set title "Average Daily Glucose" font "Calibri,18" +set xlabel "Time" offset 0,-0.25 +set ylabel "Blood glucose" +set xtics left tc rgb "#000000" +set ytics 2 tc rgb "#000000" +set grid ytics ls 100 front + +set object 1 rect from graph 0, first $min_glucose to graph 1,first $max_glucose fc ls 6 fs solid 0.2 back + +AVG = MeanTotal +AVG_LABEL = gprintf("Average glucose: %.2f", AVG) +set object 2 rect at graph 0.9, graph 0.9 fc ls 2 fs transparent solid 0.5 front size char strlen(AVG_LABEL), char 3 +set label 2 AVG_LABEL at graph 0.9, graph 0.9 front center + +A1C = $a1c_calc +A1C_LABEL = gprintf("Average A1c: %.1f", A1C) +set object 3 rect at graph 0.07, graph 0.9 fc ls 5 fs transparent solid 0.5 front size char strlen(A1C_LABEL), char 3 +set label 3 A1C_LABEL at graph 0.07, graph 0.9 front center + +plot \$SmoothDataAvg using ( strftime("%H:%M:%S", \$1) ):2:( \$2 > $max_glucose || \$2 < $min_glucose ? 110 : 1 ) with lines lw 3 lc variable +#plot \$TableDataAvg using (strftime("%H:%M:%S", \$1)):2 with lines lw 1 lc 5, \$SmoothDataAvg using (strftime("%H:%M:%S", \$1)):2:( \$2 > $max_glucose || \$2 < $min_glucose ? 110 : 1 ) with lines lw 3 lc variable + +undefine \$DataAvg +undefine \$SmoothDataAvg +undefine \$TableDataAvg + +# Add an x grid +set multiplot previous +set title " " +set xlabel " " offset 0,-0.25 +set ylabel " " +set xtics tc rgb "#ffffff00" +set ytics tc rgb "#ffffff00" +unset grid +unset object 1 +set grid xtics ls 101 +plot 1/0 + +); + push @data, qq( unset multiplot +#test ); # run the data through gnuplot $gnuplot_data = join "\n", @data; +print $gnuplot_data; open( my $ofh, '>', $output ) or die "Could not open file '$output' $!"; @@ -204,7 +338,8 @@ my $child_exit_status = $? >> 8; close( $ofh ) or warn "close failed: $!"; -#open (GNUPLOT, "|gnuplot"); +#open(GNUPLOT, "|gnuplot"); #print GNUPLOT $gnuplot_data; +#close(GNUPLOT); # vim: set expandtab shiftwidth=4 softtabstop=4 :