Fix a week length problem; load all data as space-separated to avoid separator changes; add icons for insulin and food.

This commit is contained in:
Timothy Allen 2017-12-19 00:57:04 +02:00
parent 75c49fe88f
commit fddd1c6b84
1 changed files with 38 additions and 41 deletions

View File

@ -60,11 +60,10 @@ GetOptions ("input=s" => \$input, # The name of the CSV file from wh
sub calculate_max_min {
my %opts = @_;
my $lines = $opts{lines};
my $fmt = $opts{format} || qq("%Y-%m-%d %H:%M:%S");
my $fmt = $opts{format} || qq(%Y-%m-%dT%H:%M:%S);
my %intervals;
foreach my $row ( @{$lines} ) {
my ( $key, $value ) = split /,/, $row;
$value =~ s#"(.*)"#$1#;
my ( $key, $value ) = split / /, $row;
my $date = Time::Piece->strptime( $key, $fmt );
my ( $hour, $minute ) = ( $date->hour, $date->min );
my $time = sprintf( "%02d:%02d:00", $hour, int($minute/$interval)*$interval );
@ -98,6 +97,15 @@ open( my $ifh, '<:encoding(UTF-8)', $input )
while ( my $row = <$ifh> ) {
chomp( $row );
# Clean up the comments
$row =~ s#\((Scan|Sensor)\)(; )?##i;
$row =~ s#\(Blood\)(; )?##i;
$row =~ s#Food \(.*?\)(; )?#{/: 🍎}#i;
$row =~ s#Rapid-acting insulin \(.*?\)(; )?#~{/: 💉}{-1{/:=10 Rapid}}#i;
$row =~ s#Long-acting insulin \(.*?\)(; )?#~{/: 💉}{-1{/:=10 Long}}#i;
# Parse CSV into whitespace-separated tokens to avoid conflicting separators
$row =~ s#^"(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})","([\d\.]+)",.*,"(.*?)"$#$1T$2 $3 "$4"#;
push @filelines, $row;
}
@ -126,8 +134,8 @@ if ( $units =~ /mg/i ) {
# Get the list of days for which to produce graphs
foreach my $row ( @filelines ) {
if ( $row =~ m#^"((\d{4})-(\d{2})-(\d{2}))#ms ) {
my ( $date,$year,$month,$day ) = ( $1, split /-/, $1 );
if ( $row =~ m#^((\d{4})-(\d{2})-(\d{2}))#ms ) {
my ( $date,$year,$month,$day ) = ( $1, $2, $3, $4 );
my $time = Time::Piece->strptime( $date, "%Y-%m-%d" );
my $week = $time->strftime("%W");
$seen_weeks{$year}{$week}++;
@ -144,7 +152,7 @@ foreach my $year ( sort keys %seen_weeks ) {
$total_day_graphs = scalar keys %seen_days;
$total_week_graphs = scalar keys %seen_weeks;
$intervals = calculate_max_min( 'lines' => \@filelines, 'format' => '"%Y-%m-%d %H:%M:%S"' );
$intervals = calculate_max_min( 'lines' => \@filelines, 'format' => '%Y-%m-%dT%H:%M:%S' );
# Set up basic gnuplot output options
push @data, qq(
@ -155,13 +163,11 @@ set terminal pdf size $page_size enhanced font 'Calibri,14' linewidth 1
# Read each line into a $Data variable for use by gnuplot
foreach my $d ( sort keys %seen_days ) {
my $label = "$1$2$3" if ( $d =~ m#(\d{4})-(\d{2})-(\d{2})# );
push @data, qq(
\$Data$label << EOD
"timestamp","blood glucose","meal","method","comment");
push @data, qq(\$Data$label << EOD);
@sortedlines = ();
foreach my $row (@filelines) {
if ( $row =~ m#^"$d .*$# ) {
push @sortedlines, qq($row);
if ( $row =~ m#^${d}T(.*)$# ) {
push @sortedlines, qq($1);
}
}
@sortedlines = map { " $_" } @sortedlines; # indent data structure
@ -170,13 +176,11 @@ foreach my $d ( sort keys %seen_days ) {
}
# Output data averages by hour of the day
push @data, qq(
\$DataAvg << EOD
"timestamp","blood glucose","meal","method","comment");
push @data, qq(\$DataAvg << EOD);
@sortedlines = ();
foreach my $row (@filelines) {
if ( $row =~ m#^(")\d{4}-\d{2}-\d{2} (.*)$# ) {
push @sortedlines, qq($1$2);
if ( $row =~ m#^\S+?T(\S+) (\S+)# ) {
push @sortedlines, qq($1 $2);
}
}
@sortedlines = map { " $_" } @sortedlines; # indent data structure
@ -184,12 +188,10 @@ push @data, join "\n", sort @sortedlines;
push @data, qq(EOD);
# Output the max and min glucose values for each $interval time period
push @data, qq(
\$DataMaxMin << EOD
"timestamp","max","min");
push @data, qq(\$DataMaxMin << EOD);
@sortedlines = ();
foreach my $time ( sort keys %$intervals ) {
push @sortedlines, qq("$time","$intervals->{$time}->{max}","$intervals->{$time}->{min}");
push @sortedlines, qq($time $intervals->{$time}->{max} $intervals->{$time}->{min});
}
@sortedlines = map { " $_" } @sortedlines; # indent data structure
push @data, join "\n", sort @sortedlines;
@ -206,37 +208,34 @@ foreach my $year ( sort keys %seen_weeks ) {
# Select data from the week in question
my @weeklines;
foreach my $row (@filelines) {
foreach my $dow ( 1 .. 7 ) {
foreach my $dow ( 0 .. 6 ) {
my $day = $mon + ( ONE_DAY * $dow );
my $d = $day->strftime("%Y-%m-%d");
if ( $row =~ m#^"$d .*$#ms ) {
if ( $row =~ m#^$d#ms ) {
push @weeklines, $row;
}
}
}
push @data, qq(
\$DataWeekAvg$label << EOD
"timestamp","blood glucose","meal","method","comment");
push @data, qq(\$DataWeekAvg$label << EOD);
@sortedlines = ();
foreach my $row (@weeklines) {
if ($row =~ m#^(")\d{4}-\d{2}-\d{2} (.*)$# ) {
push @sortedlines, qq($1$2);
if ($row =~ m#^\d{4}-\d{2}-\d{2}T(\S+) (\S+) # ) {
push @sortedlines, qq($1 $2);
}
}
my $week_intervals = calculate_max_min( 'lines' => \@sortedlines, 'format' => '"%H:%M:%S"' );
my $week_intervals = calculate_max_min( 'lines' => \@sortedlines, 'format' => '%H:%M:%S' );
@sortedlines = map { " $_" } @sortedlines; # indent data structure
push @data, join "\n", sort @sortedlines;
push @data, qq(EOD);
push @data, qq(
\$DataWeekMaxMin$label << EOD
"timestamp","max","min");
push @data, qq(\$DataWeekMaxMin$label << EOD);
@sortedlines = ();
foreach my $time ( sort keys %{$week_intervals} ) {
push @sortedlines, qq("$time","$intervals->{$time}->{max}","$intervals->{$time}->{min}");
push @sortedlines, qq($time $intervals->{$time}->{max} $intervals->{$time}->{min});
}
@sortedlines = map { " $_" } @sortedlines; # indent data structure
push @data, sort @sortedlines;
@ -247,10 +246,11 @@ foreach my $year ( sort keys %seen_weeks ) {
# Sample each day's values into a smoothed plot, and store each plot in a new table
push @data, qq(
set datafile separator ","
set datafile separator whitespace
# Read the CSV time format
set timefmt "%Y-%m-%d %H:%M:%S"
#set timefmt "%Y-%m-%dT%H:%M:%S"
set timefmt "%H:%M:%S"
# Store in table in seconds, as the value must be a number
set format x "%s" timedate
set format y "%.2f" numeric
@ -269,15 +269,13 @@ set xdata time
set table \$SmoothData$label
plot \$Data$label using 1:2 smooth mcsplines
unset table
set table 'SmoothData$label'
plot \$Data$label using 1:2 smooth mcsplines
unset table
);
}
# Sample the average $interval values into a smoothed plot, and store in a new table
push @data, qq(
set datafile separator ","
set datafile separator whitespace
# Read the CSV time format
set timefmt "%H:%M:%S"
@ -328,14 +326,13 @@ foreach my $year ( sort keys %seen_weeks ) {
my $mon = $time + ( ONE_WEEK * ( $week - 1 ) ) + ( ONE_DAY );
my $label = $mon->strftime("%Y%m%d");
push @data, qq(
set datafile separator ","
set datafile separator whitespace
set timefmt "%H:%M:%S"
set format x "%s" timedate
set format y "%.2f" numeric
set samples 10000
set xdata
print "DataWeekAvg$label"
stats \$DataWeekAvg$label using 2
MedianTotal$label = STATS_mean
MeanTotal$label = STATS_mean
@ -428,7 +425,7 @@ AVG_LABEL = gprintf("Median 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
plot \$SmoothData$label using (strftime("%H:%M:%S", \$1)):2:( \$2 > $max_glucose || \$2 < $min_glucose ? 110 : 1 ) with lines lw 3 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 , \$Data$label using 1:($graph_max-6):3 with labels font "Calibri,18" enhanced
# Add an x grid
set multiplot previous