postrunner 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+ #
4
+ # = ActivityLink.rb -- PostRunner - Manage the data from your Garmin sport devices.
5
+ #
6
+ # Copyright (c) 2014, 2015 by Chris Schlaeger <cs@taskjuggler.org>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of version 2 of the GNU General Public License as
10
+ # published by the Free Software Foundation.
11
+ #
12
+
13
+ require 'postrunner/HTMLBuilder'
14
+
15
+ module PostRunner
16
+
17
+ # Generates the name of an Activity with a link to the ActivityReport.
18
+ # Optionally, an icon can be shown for Activities that contain a current
19
+ # personal record.
20
+ class ActivityLink
21
+
22
+ def initialize(activity, show_record_icon = false)
23
+ @activity = activity
24
+ @show_record_icon = show_record_icon
25
+ end
26
+
27
+ # Add the ActivityLink as HTML Elements to the document.
28
+ # @param doc [HTMLBuilder] XML Document
29
+ def to_html(doc)
30
+ doc.unique(:activitylink_style) { doc.style(style) }
31
+
32
+ doc.a(@activity.name, { :class => 'activity_link',
33
+ :href => @activity.fit_file[0..-5] + '.html' })
34
+ if @show_record_icon && @activity.has_records?
35
+ doc.img(nil, { :src => 'icons/record-small.png',
36
+ :style => 'vertical-align:middle' })
37
+ end
38
+ end
39
+
40
+ # Convert the ActivityLink into a plain text form. Return the first 20
41
+ # characters of the Activity name.
42
+ def to_s
43
+ @activity.name[0..19]
44
+ end
45
+
46
+ private
47
+
48
+ def style
49
+ <<EOT
50
+ .activity_link {
51
+ padding: 0px 3px 0px 3px;
52
+ }
53
+ EOT
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
@@ -14,31 +14,15 @@ require 'fit4ruby'
14
14
 
15
15
  require 'postrunner/FlexiTable'
16
16
  require 'postrunner/HTMLBuilder'
17
- require 'postrunner/ViewWidgets'
17
+ require 'postrunner/ActivityLink'
18
18
 
19
19
  module PostRunner
20
20
 
21
+ # Generates a paged list of all Activity objects in the database. HTML and
22
+ # plain text output are supported.
21
23
  class ActivityListView
22
24
 
23
- class ActivityLink
24
-
25
- def initialize(activity)
26
- @activity = activity
27
- end
28
-
29
- def to_html(doc)
30
- doc.a(@activity.name, { :class => 'activity_link',
31
- :href => @activity.fit_file[0..-5] + '.html' })
32
- end
33
-
34
- def to_s
35
- @activity.name[0..19]
36
- end
37
-
38
- end
39
-
40
25
  include Fit4Ruby::Converters
41
- include ViewWidgets
42
26
 
43
27
  def initialize(db)
44
28
  @db = db
@@ -48,85 +32,42 @@ module PostRunner
48
32
  @last_page = (@db.activities.length - 1) / @page_size
49
33
  end
50
34
 
51
- def update_html_index
35
+ def update_index_pages
52
36
  0.upto(@last_page) do |page_no|
53
37
  @page_no = page_no
54
- generate_html_index_page
38
+ generate_html_index_page(page_no)
55
39
  end
56
40
  end
57
41
 
58
- def to_html(doc)
59
- generate_table.to_html(doc)
60
- end
61
-
62
42
  def to_s
63
43
  generate_table.to_s
64
44
  end
65
45
 
66
46
  private
67
47
 
68
- def generate_html_index_page
69
- doc = HTMLBuilder.new
48
+ def generate_html_index_page(page_index)
49
+ views = @db.views
50
+ views.current_page = 'index.html'
70
51
 
71
- doc.html {
72
- head(doc)
73
- body(doc)
74
- }
75
-
76
- write_file(doc)
77
- end
52
+ pages = PagingButtons.new((0..@last_page).map do |i|
53
+ "index#{i == 0 ? '' : "-#{i}"}.html"
54
+ end)
55
+ pages.current_page =
56
+ "index#{page_index == 0 ? '' : "-#{page_index}"}.html"
57
+ @view = View.new("PostRunner Activities", views, pages)
78
58
 
79
- def head(doc)
80
- doc.head {
81
- doc.meta({ 'http-equiv' => 'Content-Type',
82
- 'content' => 'text/html; charset=utf-8' })
83
- doc.title("PostRunner Activities")
84
- style(doc)
85
- }
86
- end
59
+ @view.doc.head { @view.doc.style(style) }
60
+ body(@view.doc)
87
61
 
88
- def style(doc)
89
- view_widgets_style(doc)
90
- doc.style(<<EOT
91
- body {
92
- font-family: verdana,arial,sans-serif;
93
- margin: 0px;
94
- }
95
- .main {
96
- text-align: center;
97
- }
98
- .widget_frame {
99
- width: 900px;
100
- }
101
- .activity_link {
102
- padding: 0px 3px 0px 3px;
103
- }
104
- .ft_cell {
105
- height: 30px
106
- }
107
- EOT
108
- )
62
+ output_file = File.join(@db.cfg[:html_dir], pages.current_page)
63
+ @view.write(output_file)
109
64
  end
110
65
 
111
66
  def body(doc)
112
- doc.body {
113
- first_page = @page_no == 0 ? nil: 'index.html'
114
- prev_page = @page_no == 0 ? nil :
115
- @page_no == 1 ? 'index.html' :
116
- "index#{@page_no - 1}.html"
117
- prev_page = @page_no == 0 ? nil :
118
- @page_no == 1 ? 'index.html' :
119
- "index#{@page_no - 1}.html"
120
- next_page = @page_no < @last_page ? "index#{@page_no + 1}.html" : nil
121
- last_page = @page_no == @last_page ? nil : "index#{@last_page}.html"
122
- titlebar(doc, first_page, prev_page, nil, next_page, last_page)
123
-
67
+ @view.body {
124
68
  doc.div({ :class => 'main' }) {
125
- frame(doc, 'Activities') {
126
- generate_table.to_html(doc)
127
- }
69
+ ViewFrame.new('Activities', 900, generate_table).to_html(doc)
128
70
  }
129
- footer(doc)
130
71
  }
131
72
  end
132
73
 
@@ -134,11 +75,11 @@ EOT
134
75
  i = @page_no < 0 ? 0 : @page_no * @page_size
135
76
  t = FlexiTable.new
136
77
  t.head
137
- t.row(%w( Ref. Activity Start Distance Duration Speed/Pace ),
78
+ t.row(%w( Ref. Activity Type Start Distance Duration Speed/Pace ),
138
79
  { :halign => :left })
139
80
  t.set_column_attributes([
140
81
  { :halign => :right },
141
- {}, {},
82
+ {}, {}, {},
142
83
  { :halign => :right },
143
84
  { :halign => :right },
144
85
  { :halign => :right }
@@ -150,7 +91,8 @@ EOT
150
91
  activities.each do |a|
151
92
  t.row([
152
93
  i += 1,
153
- ActivityLink.new(a),
94
+ ActivityLink.new(a, true),
95
+ a.activity_type,
154
96
  a.timestamp.strftime("%a, %Y %b %d %H:%M"),
155
97
  local_value(a.total_distance, 'm', '%.2f',
156
98
  { :metric => 'km', :statute => 'mi' }),
@@ -163,14 +105,19 @@ EOT
163
105
  t
164
106
  end
165
107
 
166
- def write_file(doc)
167
- output_file = File.join(@db.html_dir,
168
- "index#{@page_no == 0 ? '' : @page_no}.html")
169
- begin
170
- File.write(output_file, doc.to_html)
171
- rescue IOError
172
- Log.fatal "Cannot write activity index file '#{output_file}: #{$!}"
173
- end
108
+ def style
109
+ <<EOT
110
+ body {
111
+ font-family: verdana,arial,sans-serif;
112
+ margin: 0px;
113
+ }
114
+ .main {
115
+ text-align: center;
116
+ }
117
+ .ft_cell {
118
+ height: 30px
119
+ }
120
+ EOT
174
121
  end
175
122
 
176
123
  def local_value(value, from_unit, format, units)
@@ -13,18 +13,19 @@
13
13
  require 'fit4ruby'
14
14
 
15
15
  require 'postrunner/FlexiTable'
16
- require 'postrunner/ViewWidgets'
16
+ require 'postrunner/ViewFrame'
17
17
 
18
18
  module PostRunner
19
19
 
20
20
  class ActivitySummary
21
21
 
22
22
  include Fit4Ruby::Converters
23
- include ViewWidgets
24
23
 
25
- def initialize(fit_activity, name, unit_system)
24
+ def initialize(fit_activity, unit_system, custom_fields)
26
25
  @fit_activity = fit_activity
27
- @name = name
26
+ @name = custom_fields[:name]
27
+ @type = custom_fields[:type]
28
+ @sub_type = custom_fields[:sub_type]
28
29
  @unit_system = unit_system
29
30
  end
30
31
 
@@ -33,12 +34,9 @@ module PostRunner
33
34
  end
34
35
 
35
36
  def to_html(doc)
36
- frame(doc, "Activity: #{@name}") {
37
- summary.to_html(doc)
38
- }
39
- frame(doc, 'Laps') {
40
- laps.to_html(doc)
41
- }
37
+ width = 600
38
+ ViewFrame.new("Activity: #{@name}", width, summary).to_html(doc)
39
+ ViewFrame.new('Laps', width, laps).to_html(doc)
42
40
  end
43
41
 
44
42
  private
@@ -49,7 +47,9 @@ module PostRunner
49
47
  t = FlexiTable.new
50
48
  t.enable_frame(false)
51
49
  t.body
52
- t.row([ 'Date:', session.timestamp])
50
+ t.row([ 'Type:', @type ])
51
+ t.row([ 'Sub Type:', @sub_type ])
52
+ t.row([ 'Date:', session.timestamp ])
53
53
  t.row([ 'Distance:',
54
54
  local_value(session, 'total_distance', '%.2f %s',
55
55
  { :metric => 'km', :statute => 'mi'}) ])
@@ -76,7 +76,7 @@ module PostRunner
76
76
  session.total_training_effect : '-' ])
77
77
  t.row([ 'Avg. Run Cadence:',
78
78
  session.avg_running_cadence ?
79
- "#{session.avg_running_cadence.round} spm" : '-' ])
79
+ "#{(2 * session.avg_running_cadence).round} spm" : '-' ])
80
80
  t.row([ 'Avg. Vertical Oscillation:',
81
81
  local_value(session, 'avg_vertical_oscillation', '%.1f %s',
82
82
  { :metric => 'cm', :statute => 'in' }) ])
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # = ActivityView.rb -- PostRunner - Manage the data from your Garmin sport devices.
5
5
  #
6
- # Copyright (c) 2014 by Chris Schlaeger <cs@taskjuggler.org>
6
+ # Copyright (c) 2014, 2015 by Chris Schlaeger <cs@taskjuggler.org>
7
7
  #
8
8
  # This program is free software; you can redistribute it and/or modify
9
9
  # it under the terms of version 2 of the GNU General Public License as
@@ -12,67 +12,75 @@
12
12
 
13
13
  require 'fit4ruby'
14
14
 
15
- require 'postrunner/HTMLBuilder'
15
+ require 'postrunner/View'
16
16
  require 'postrunner/ActivitySummary'
17
17
  require 'postrunner/DeviceList'
18
18
  require 'postrunner/UserProfileView'
19
- require 'postrunner/ViewWidgets'
20
19
  require 'postrunner/TrackView'
21
20
  require 'postrunner/ChartView'
22
21
 
23
22
  module PostRunner
24
23
 
25
- class ActivityView
24
+ class ActivityView < View
26
25
 
27
- include ViewWidgets
28
-
29
- def initialize(activity, unit_system, predecessor, successor)
26
+ def initialize(activity, unit_system)
30
27
  @activity = activity
28
+ db = @activity.db
31
29
  @unit_system = unit_system
32
- @predecessor = predecessor
33
- @successor = successor
34
- @output_dir = activity.html_dir
35
- @output_file = nil
36
30
 
37
- @doc = HTMLBuilder.new
31
+ views = db.views
32
+ views.current_page = nil
33
+
34
+ # Sort activities in reverse order so the newest one is considered the
35
+ # last report by the pagin buttons.
36
+ activities = db.activities.sort do |a1, a2|
37
+ a1.timestamp <=> a2.timestamp
38
+ end
39
+
40
+ pages = PagingButtons.new(activities.map do |a|
41
+ "#{a.fit_file[0..-5]}.html"
42
+ end, false)
43
+ pages.current_page = "#{@activity.fit_file[0..-5]}.html"
44
+
45
+ super("PostRunner Activity: #{@activity.name}", views, pages)
38
46
  generate_html(@doc)
39
- write_file
47
+ write(File.join(db.cfg[:html_dir], pages.current_page))
40
48
  end
41
49
 
42
50
  private
43
51
 
44
52
  def generate_html(doc)
45
- @report = ActivitySummary.new(@activity.fit_activity, @activity.name,
46
- @unit_system)
47
- @device_list = DeviceList.new(@activity.fit_activity)
48
- @user_profile = UserProfileView.new(@activity.fit_activity, @unit_system)
49
- @track_view = TrackView.new(@activity)
50
- @chart_view = ChartView.new(@activity, @unit_system)
51
-
52
- doc.html {
53
- head(doc)
54
- body(doc)
55
- }
56
- end
57
-
58
- def head(doc)
59
- doc.head {
60
- doc.meta({ 'http-equiv' => 'Content-Type',
61
- 'content' => 'text/html; charset=utf-8' })
62
- doc.meta({ 'name' => 'viewport',
63
- 'content' => 'width=device-width, ' +
64
- 'initial-scale=1.0, maximum-scale=1.0, ' +
65
- 'user-scalable=0' })
66
- doc.title("PostRunner Activity: #{@activity.name}")
67
- view_widgets_style(doc)
68
- @chart_view.head(doc)
69
- @track_view.head(doc)
70
- style(doc)
53
+ doc.head { doc.style(style) }
54
+ #doc.meta({ 'name' => 'viewport',
55
+ # 'content' => 'width=device-width, ' +
56
+ # 'initial-scale=1.0, maximum-scale=1.0, ' +
57
+ # 'user-scalable=0' })
58
+
59
+ body {
60
+ doc.body({ :onload => 'init()' }) {
61
+ # The main area with the 2 column layout.
62
+ doc.div({ :class => 'main' }) {
63
+ doc.div({ :class => 'left_col' }) {
64
+ ActivitySummary.new(@activity.fit_activity, @unit_system,
65
+ { :name => @activity.name,
66
+ :type => @activity.activity_type,
67
+ :sub_type => @activity.activity_sub_type
68
+ }).to_html(doc)
69
+ TrackView.new(@activity).to_html(doc)
70
+ DeviceList.new(@activity.fit_activity).to_html(doc)
71
+ UserProfileView.new(@activity.fit_activity, @unit_system).
72
+ to_html(doc)
73
+ }
74
+ doc.div({ :class => 'right_col' }) {
75
+ ChartView.new(@activity, @unit_system).to_html(doc)
76
+ }
77
+ }
78
+ }
71
79
  }
72
80
  end
73
81
 
74
- def style(doc)
75
- doc.style(<<EOT
82
+ def style
83
+ <<EOT
76
84
  body {
77
85
  font-family: verdana,arial,sans-serif;
78
86
  margin: 0px;
@@ -90,37 +98,6 @@ body {
90
98
  width: 600px;
91
99
  }
92
100
  EOT
93
- )
94
- end
95
-
96
- def body(doc)
97
- doc.body({ :onload => 'init()' }) {
98
- prev_page = @predecessor ? @predecessor.fit_file[0..-5] + '.html' : nil
99
- next_page = @successor ? @successor.fit_file[0..-5] + '.html' : nil
100
- titlebar(doc, nil, prev_page, 'index.html', next_page)
101
- # The main area with the 2 column layout.
102
- doc.div({ :class => 'main' }) {
103
- doc.div({ :class => 'left_col' }) {
104
- @report.to_html(doc)
105
- @track_view.div(doc)
106
- @device_list.to_html(doc)
107
- @user_profile.to_html(doc)
108
- }
109
- doc.div({ :class => 'right_col' }) {
110
- @chart_view.div(doc)
111
- }
112
- }
113
- footer(doc)
114
- }
115
- end
116
-
117
- def write_file
118
- @output_file = File.join(@output_dir, "#{@activity.fit_file[0..-5]}.html")
119
- begin
120
- File.write(@output_file, @doc.to_html)
121
- rescue IOError
122
- Log.fatal "Cannot write activity view file '#{@output_file}: #{$!}"
123
- end
124
101
  end
125
102
 
126
103
  end