postrunner 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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