summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Schlaeger <chris@linux.com>2016-04-16 20:10:06 +0200
committerChris Schlaeger <chris@linux.com>2016-04-16 20:10:06 +0200
commit8852273d8b9d7d365e7c733593900be67ef4a382 (patch)
treef7a20a2cff90cc0e90447db201dca582d0b19f14
parent32f69405a525ca4dbf7bae53318bfa9c8f9dcb1c (diff)
downloadpostrunner-8852273d8b9d7d365e7c733593900be67ef4a382.zip
Use tables for the daily sleep report.
-rw-r--r--lib/postrunner/DailySleepAnalyzer.rb46
-rw-r--r--lib/postrunner/FitFileStore.rb4
-rw-r--r--lib/postrunner/SleepStatistics.rb69
3 files changed, 87 insertions, 32 deletions
diff --git a/lib/postrunner/DailySleepAnalyzer.rb b/lib/postrunner/DailySleepAnalyzer.rb
index ef8d279..a120d95 100644
--- a/lib/postrunner/DailySleepAnalyzer.rb
+++ b/lib/postrunner/DailySleepAnalyzer.rb
@@ -20,10 +20,11 @@ module PostRunner
class DailySleepAnalyzer
# Utility class to store the interval of a sleep/wake phase.
- class SleepPeriod < Struct.new(:from_time, :to_time, :phase)
+ class SleepInterval < Struct.new(:from_time, :to_time, :phase)
end
- include Fit4Ruby::Converters
+ attr_reader :sleep_intervals, :utc_offset,
+ :total_sleep, :deep_sleep, :light_sleep
# Create a new DailySleepAnalyzer object to analyze the given monitoring
# files.
@@ -31,7 +32,7 @@ module PostRunner
# @param day [String] Day to analyze as YY-MM-DD string
def initialize(monitoring_files, day)
@noon_yesterday = @noon_today = @utc_offset = nil
- @sleep_periods = []
+ @sleep_intervals = []
# Day as Time object. Midnight UTC.
day_as_time = Time.parse(day + "-00:00:00+00:00").gmtime
@@ -43,22 +44,6 @@ module PostRunner
calculate_totals
end
- def to_s
- unless @noon_today
- return 'No sleep data available for this day'
- end
-
- s = "Sleep periods for #{@noon_today.strftime('%Y-%m-%d')}\n"
- @sleep_periods.each do |p|
- s += "#{p.from_time.localtime(@utc_offset).strftime('%H:%M')} - " +
- "#{p.to_time.localtime(@utc_offset).strftime('%H:%M')} : " +
- "#{p.phase.to_s.gsub(/_/, ' ')}\n"
- end
- s += "\nTotal sleep time: #{secsToHM(@total_sleep)} (" +
- "Deep: #{secsToHM(@deep_sleep)}, " +
- "Light: #{secsToHM(@light_sleep)})"
- end
-
private
def extract_utc_offset(monitoring_file)
@@ -163,7 +148,7 @@ module PostRunner
def analyze
current_phase = :awake
current_phase_start = @noon_yesterday
- @sleep_periods = []
+ @sleep_intervals = []
@smoothed_sleep_activity.each_with_index do |v, idx|
if v < 0.25
@@ -176,20 +161,20 @@ module PostRunner
if current_phase != phase
t = @noon_yesterday + 60 * idx
- @sleep_periods << SleepPeriod.new(current_phase_start, t,
- current_phase)
+ @sleep_intervals << SleepInterval.new(current_phase_start, t,
+ current_phase)
current_phase = phase
current_phase_start = t
end
end
- @sleep_periods << SleepPeriod.new(current_phase_start, @noon_today,
- current_phase)
+ @sleep_intervals << SleepInterval.new(current_phase_start, @noon_today,
+ current_phase)
end
def trim_wake_periods_at_ends
first_deep_sleep_idx = last_deep_sleep_idx = nil
- @sleep_periods.each_with_index do |p, idx|
+ @sleep_intervals.each_with_index do |p, idx|
if p.phase == :deep_sleep ||
(p.phase == :light_sleep && ((p.to_time - p.from_time) > 15 * 60))
first_deep_sleep_idx = idx unless first_deep_sleep_idx
@@ -200,20 +185,21 @@ module PostRunner
return unless first_deep_sleep_idx && last_deep_sleep_idx
if first_deep_sleep_idx > 0 &&
- @sleep_periods[first_deep_sleep_idx - 1].phase == :light_sleep
+ @sleep_intervals[first_deep_sleep_idx - 1].phase == :light_sleep
first_deep_sleep_idx -= 1
end
- if last_deep_sleep_idx < @sleep_periods.length - 2 &&
- @sleep_periods[last_deep_sleep_idx + 1].phase == :light_sleep
+ if last_deep_sleep_idx < @sleep_intervals.length - 2 &&
+ @sleep_intervals[last_deep_sleep_idx + 1].phase == :light_sleep
last_deep_sleep_idx += 1
end
- @sleep_periods = @sleep_periods[first_deep_sleep_idx..last_deep_sleep_idx]
+ @sleep_intervals =
+ @sleep_intervals[first_deep_sleep_idx..last_deep_sleep_idx]
end
def calculate_totals
@total_sleep = @light_sleep = @deep_sleep = 0
- @sleep_periods.each do |p|
+ @sleep_intervals.each do |p|
if p.phase != :awake
seconds = p.to_time - p.from_time
@total_sleep += seconds
diff --git a/lib/postrunner/FitFileStore.rb b/lib/postrunner/FitFileStore.rb
index a088e7b..75bd104 100644
--- a/lib/postrunner/FitFileStore.rb
+++ b/lib/postrunner/FitFileStore.rb
@@ -18,7 +18,7 @@ require 'postrunner/DirUtils'
require 'postrunner/FFS_Device'
require 'postrunner/ActivityListView'
require 'postrunner/ViewButtons'
-require 'postrunner/DailySleepAnalyzer'
+require 'postrunner/SleepStatistics'
module PostRunner
@@ -338,7 +338,7 @@ module PostRunner
read_fit_file(File.join(fit_file_dir(m.fit_file_name, m.device.long_uid,
'monitor'), m.fit_file_name))
end
- puts DailySleepAnalyzer.new(monitoring_files, day).to_s
+ puts SleepStatistics.new(monitoring_files).daily(day)
end
private
diff --git a/lib/postrunner/SleepStatistics.rb b/lib/postrunner/SleepStatistics.rb
new file mode 100644
index 0000000..514733b
--- /dev/null
+++ b/lib/postrunner/SleepStatistics.rb
@@ -0,0 +1,69 @@
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
+#
+# = SleepStatistics.rb -- PostRunner - Manage the data from your Garmin sport devices.
+#
+# Copyright (c) 2016 by Chris Schlaeger <cs@taskjuggler.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+
+require 'fit4ruby'
+
+require 'postrunner/DailySleepAnalyzer'
+require 'postrunner/FlexiTable'
+
+module PostRunner
+
+ # This class can be used to generate reports for sleep data. It uses the
+ # DailySleepAnalyzer class to compute the data and generates the report for
+ # a certain time period.
+ class SleepStatistics
+
+ include Fit4Ruby::Converters
+
+ # Create a new SleepStatistics object.
+ # @param monitoring_files [Array of Fit4Ruby::Monitoring_B] FIT files
+ def initialize(monitoring_files)
+ @monitoring_files = monitoring_files
+ end
+
+ # Generate a report for a certain day.
+ # @param day [String] Date of the day as YYYY-MM-DD string.
+ def daily(day)
+ analyzer = DailySleepAnalyzer.new(@monitoring_files, day)
+
+ if analyzer.sleep_intervals.empty?
+ return 'No sleep data available for this day'
+ end
+
+ ti = FlexiTable.new
+ ti.head
+ ti.row([ 'From', 'To', 'Sleep phase' ])
+ ti.body
+ utc_offset = analyzer.utc_offset
+ analyzer.sleep_intervals.each do |i|
+ ti.cell(i[:from_time].localtime(utc_offset).strftime('%H:%M'))
+ ti.cell(i[:to_time].localtime(utc_offset).strftime('%H:%M'))
+ ti.cell(i[:phase])
+ ti.new_row
+ end
+
+ tt = FlexiTable.new
+ tt.head
+ tt.row([ 'Total Sleep', 'Deep Sleep', 'Light Sleep' ])
+ tt.body
+ tt.cell(secsToHM(analyzer.total_sleep), { :halign => :right })
+ tt.cell(secsToHM(analyzer.deep_sleep), { :halign => :right })
+ tt.cell(secsToHM(analyzer.light_sleep), { :halign => :right })
+ tt.new_row
+
+ "Sleep Statistics for #{day}\n\n#{ti}\n#{tt}"
+ end
+
+ end
+
+end
+