postrunner 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 10eb940a7f897be53510322b131e9307a844b3d3
4
- data.tar.gz: 85b3a20f60cd7b20bede30072eae6d51af9bf93c
3
+ metadata.gz: 5c193dbf151045522cfe8f47b729d244eb87f957
4
+ data.tar.gz: fb92f5b286258cac2c17e96dae821d858face419
5
5
  SHA512:
6
- metadata.gz: 1c59ca7d3109f211b7719bf1f5b526702c664d38b71766777784bfafef0edd72782efa580b79d9bfadfee5c9aacf79d1be62c9e7aaf9901275d2d04cec5cbe6d
7
- data.tar.gz: 3b1d77b712513e49a922bc94a1a44f8e88c9681d36c62df7c797bda14207b60721efb3cee5bf7393452fd8c1993a8f2dd597f1bdecef074113587fd7ce20c7ab
6
+ metadata.gz: 2a01b57478175e684b1253e30174e46ee5b3119dd8a2941a8cf6141255e7955c6628fff6a20167c3421feaee0ca7c857370ae8c05a4d7ae114fb6730df909953
7
+ data.tar.gz: b3f8c2ad6fa0effd9958a4102b6280e373c5bf11a3c90982bfdaaffaeb14d90f03fff37460963d51e6600cd9432f36b11338a86e3b5eb19536252fcd37e4c048
data/README.md CHANGED
@@ -1,10 +1,15 @@
1
1
  # PostRunner
2
2
 
3
- PostRunner is an application to manage FIT files such as those produced by Garmin products like the Forerunner 620. It allows you to import the files from the device and inspect them.
3
+ PostRunner is an application to manage FIT files such as those
4
+ produced by Garmin products like the Forerunner 620 (FR620). It allows you to
5
+ import the files from the device and inspect them.
4
6
 
5
7
  ## Installation
6
8
 
7
- PostRunner is a Ruby application. You need to have a Ruby 2.0 or later runtime environment installed.
9
+ PostRunner is a [http://www.ruby-lang.org](Ruby) application. You need
10
+ to have a Ruby 2.0 or later runtime environment installed. This
11
+ application was developed and tested on Linux but may work on other
12
+ operating systems as well.
8
13
 
9
14
  ```
10
15
  $ gem install postrunner
@@ -14,13 +19,20 @@ $ gem install postrunner
14
19
 
15
20
  ### Importing FIT files
16
21
 
17
- To get started you need to connect your device to your computer and mount it as a drive. Only devices that expose their data as FAT file system are supported. Older devices use proprietary drivers and are not supported by postrunner. Once the device is mounted find out the full path to the directory that contains your FIT files. You can then import all files on the device.
22
+ To get started you need to connect your device to your computer and
23
+ mount it as a disk drive. Only devices that expose their data as FAT file
24
+ system are supported. Older devices use proprietary drivers and are
25
+ not supported by postrunner. Once the device is mounted find out the
26
+ full path to the directory that contains your FIT files. You can then
27
+ import all files on the device.
18
28
 
19
29
  ```
20
30
  $ postrunner import /var/run/media/user/GARMIN/GARMIN/ACTIVITY/
21
31
  ```
22
32
 
23
- The above command assumes that your device is mounted as /var/run/media/user. Please replace this with the path to your device. Files that have been imported previously will not be imported again.
33
+ The above command assumes that your device is mounted as
34
+ /var/run/media/user. Please replace this with the path to your device.
35
+ Files that have been imported previously will not be imported again.
24
36
 
25
37
  ### Viewing FIT file data on the console
26
38
 
@@ -57,13 +69,16 @@ You can also get a full dump of the content of a FIT file.
57
69
  $ postrunner dump 1234568.FIT
58
70
  ```
59
71
 
60
- If the file is already in the data base you can also use the reference notation.
72
+ If the file is already in the data base you can also use the reference
73
+ notation.
61
74
 
62
75
  ```
63
76
  $ postrunner dump :1
64
77
  ```
65
78
 
66
- This will provide you with a lot more information contained in the FIT files that is not available through Garmin Connect or most other tools.
79
+ This will provide you with a lot more information contained in the FIT
80
+ files that is not available through Garmin Connect or most other
81
+ tools.
67
82
 
68
83
  ### Viewing FIT file data in your web browser
69
84
 
@@ -49,11 +49,17 @@ module PostRunner
49
49
  # Not all instance variables of Activity are stored in the file. The
50
50
  # normal constructor is not run during YAML::load_file. We have to
51
51
  # initialize those instance variables in a secondary step.
52
+ sync_needed = false
52
53
  @activities.each do |a|
53
54
  a.late_init(self)
55
+ # If the Activity has the data from the FIT file loaded, a value was
56
+ # missing in the YAML file. Set the sync flag so we can update the
57
+ # YAML file once we have checked all Activities.
58
+ sync_needed |= !a.fit_activity.nil?
54
59
  end
55
60
 
56
61
  @records = PersonalRecords.new(self)
62
+ sync if sync_needed
57
63
  end
58
64
 
59
65
  # Add a new FIT file to the database.
@@ -23,8 +23,10 @@ module PostRunner
23
23
 
24
24
  # This is a list of variables that provide data from the fit file. To
25
25
  # speed up access to it, we cache the data in the activity database.
26
- @@CachedVariables = %w( timestamp total_distance total_timer_time
27
- avg_speed )
26
+ @@CachedActivityValues = %w( sport timestamp total_distance
27
+ total_timer_time avg_speed )
28
+ # We also store some additional information in the archive index.
29
+ @@CachedAttributes = @@CachedActivityValues + %w( fit_file name )
28
30
 
29
31
  def initialize(db, fit_file, fit_activity, name = nil)
30
32
  @fit_file = fit_file
@@ -32,7 +34,7 @@ module PostRunner
32
34
  @name = name || fit_file
33
35
  late_init(db)
34
36
 
35
- @@CachedVariables.each do |v|
37
+ @@CachedActivityValues.each do |v|
36
38
  v_str = "@#{v}"
37
39
  instance_variable_set(v_str, fit_activity.send(v))
38
40
  self.class.send(:attr_reader, v.to_sym)
@@ -41,6 +43,9 @@ module PostRunner
41
43
  generate_html_view
42
44
  end
43
45
 
46
+ # YAML::load() does not call initialize(). We don't have all attributes
47
+ # stored in the YAML file, so we need to make sure these are properly set
48
+ # after a YAML::load().
44
49
  def late_init(db)
45
50
  @db = db
46
51
  @html_dir = File.join(@db.db_dir, 'html')
@@ -56,26 +61,45 @@ module PostRunner
56
61
  @fit_activity = load_fit_file(filter)
57
62
  end
58
63
 
59
- def yaml_initialize(tag, value)
60
- # Create attr_readers for cached variables.
61
- @@CachedVariables.each { |v| self.class.send(:attr_reader, v.to_sym) }
62
-
63
- # Load all attributes and assign them to instance variables.
64
- value.each do |a, v|
65
- instance_variable_set("@" + a, v)
64
+ # This method is called during YAML::load() to initialize the class
65
+ # objects. The initialize() is NOT called during YAML::load(). Any
66
+ # additional initialization work is done in late_init().
67
+ def init_with(coder)
68
+ @@CachedAttributes.each do |name_without_at|
69
+ name_with_at = '@' + name_without_at
70
+ # Create attr_readers for cached variables.
71
+ self.class.send(:attr_reader, name_without_at.to_sym)
72
+
73
+ if coder.map.include?(name_without_at)
74
+ # The YAML file has a value for the instance variable. So just set
75
+ # it.
76
+ instance_variable_set(name_with_at, coder[name_without_at])
77
+ else
78
+ if @@CachedActivityValues.include?(name_without_at)
79
+ # The YAML file does not yet have the instance variable cached.
80
+ # Load the Activity data and extract the value to set the instance
81
+ # variable.
82
+ @fit_activity = load_fit_file unless @fit_activity
83
+ instance_variable_set(name_with_at,
84
+ @fit_activity.send(name_without_at))
85
+ else
86
+ Log.fatal "Don't know how to initialize the instance variable " +
87
+ "#{name_with_at}."
88
+ end
89
+ end
66
90
  end
67
- # Use the FIT file name as activity name if none has been set yet.
68
- @name = @fit_file unless @name
69
91
  end
70
92
 
93
+ # This method is called during Activity::to_yaml() calls. It's being used
94
+ # to prevent some instance variables from being saved in the YAML file.
95
+ # Only attributes that are listed in @@CachedAttributes are being saved.
71
96
  def encode_with(coder)
72
- attr_ignore = %w( @db @fit_activity )
73
-
74
- instance_variables.each do |v|
75
- v = v.to_s
76
- next if attr_ignore.include?(v)
97
+ instance_variables.each do |a|
98
+ name_with_at = a.to_s
99
+ name_without_at = name_with_at[1..-1]
100
+ next unless @@CachedAttributes.include?(name_without_at)
77
101
 
78
- coder[v[1..-1]] = instance_variable_get(v)
102
+ coder[name_without_at] = instance_variable_get(name_with_at)
79
103
  end
80
104
  end
81
105
 
@@ -134,7 +134,7 @@ EOT
134
134
  i = @page_no < 0 ? 0 : @page_no * @page_size
135
135
  t = FlexiTable.new
136
136
  t.head
137
- t.row(%w( Ref. Activity Start Distance Duration Pace ),
137
+ t.row(%w( Ref. Activity Start Distance Duration Speed/Pace ),
138
138
  { :halign => :left })
139
139
  t.set_column_attributes([
140
140
  { :halign => :right },
@@ -155,7 +155,9 @@ EOT
155
155
  local_value(a.total_distance, 'm', '%.2f',
156
156
  { :metric => 'km', :statute => 'mi' }),
157
157
  secsToHMS(a.total_timer_time),
158
- pace(a.avg_speed) ])
158
+ a.sport == 'running' ? pace(a.avg_speed) :
159
+ local_value(a.avg_speed, 'm/s', '%.1f',
160
+ { :metric => 'km/h', :statute => 'mph' }) ])
159
161
  end
160
162
 
161
163
  t
@@ -29,16 +29,12 @@ module PostRunner
29
29
  end
30
30
 
31
31
  def to_s
32
- session = @fit_activity.sessions[0]
33
-
34
- summary(session).to_s + "\n" + laps.to_s
32
+ summary.to_s + "\n" + laps.to_s
35
33
  end
36
34
 
37
35
  def to_html(doc)
38
- session = @fit_activity.sessions[0]
39
-
40
36
  frame(doc, "Activity: #{@name}") {
41
- summary(session).to_html(doc)
37
+ summary.to_html(doc)
42
38
  }
43
39
  frame(doc, 'Laps') {
44
40
  laps.to_html(doc)
@@ -47,7 +43,9 @@ module PostRunner
47
43
 
48
44
  private
49
45
 
50
- def summary(session)
46
+ def summary
47
+ session = @fit_activity.sessions[0]
48
+
51
49
  t = FlexiTable.new
52
50
  t.enable_frame(false)
53
51
  t.body
@@ -56,7 +54,13 @@ module PostRunner
56
54
  local_value(session, 'total_distance', '%.2f %s',
57
55
  { :metric => 'km', :statute => 'mi'}) ])
58
56
  t.row([ 'Time:', secsToHMS(session.total_timer_time) ])
59
- t.row([ 'Avg. Pace:', pace(session, 'avg_speed') ])
57
+ if session.sport == 'running'
58
+ t.row([ 'Avg. Pace:', pace(session, 'avg_speed') ])
59
+ else
60
+ t.row([ 'Avg. Speed:',
61
+ local_value(session, 'avg_speed', '%.1f %s',
62
+ { :metric => 'km/h', :statute => 'mph' }) ])
63
+ end
60
64
  t.row([ 'Total Ascent:',
61
65
  local_value(session, 'total_ascent', '%.0f %s',
62
66
  { :metric => 'm', :statute => 'ft' }) ])
@@ -91,18 +95,26 @@ module PostRunner
91
95
  end
92
96
 
93
97
  def laps
98
+ session = @fit_activity.sessions[0]
99
+
94
100
  t = FlexiTable.new
95
101
  t.head
96
- t.row([ 'Lap', 'Duration', 'Distance', 'Avg. Pace', 'Stride', 'Cadence',
97
- 'Avg. HR', 'Max. HR' ])
102
+ t.row([ 'Lap', 'Duration', 'Distance',
103
+ session.sport == 'running' ? 'Avg. Pace' : 'Avg. Speed',
104
+ 'Stride', 'Cadence', 'Avg. HR', 'Max. HR' ])
98
105
  t.set_column_attributes(Array.new(8, { :halign => :right }))
99
106
  t.body
100
- @fit_activity.sessions[0].laps.each.with_index do |lap, index|
107
+ session.laps.each.with_index do |lap, index|
101
108
  t.cell(index + 1)
102
109
  t.cell(secsToHMS(lap.total_timer_time))
103
110
  t.cell(local_value(lap, 'total_distance', '%.2f',
104
111
  { :metric => 'km', :statute => 'mi' }))
105
- t.cell(pace(lap, 'avg_speed', false))
112
+ if session.sport == 'running'
113
+ t.cell(pace(lap, 'avg_speed', false))
114
+ else
115
+ t.cell(local_value(lap, 'avg_speed', '%.1f',
116
+ { :metric => 'km/h', :statute => 'mph' }))
117
+ end
106
118
  t.cell(local_value(lap, 'avg_stride_length', '%.2f',
107
119
  { :metric => 'm', :statute => 'ft' }))
108
120
  t.cell(lap.avg_running_cadence && lap.avg_fractional_cadence ?
@@ -1,5 +1,4 @@
1
- #!/usr/bin/env ruby -w
2
- # encoding: UTF-8
1
+ #!/usr/bin/env ruby -w # encoding: UTF-8
3
2
  #
4
3
  # = ChartView.rb -- PostRunner - Manage the data from your Garmin sport devices.
5
4
  #
@@ -20,6 +19,7 @@ module PostRunner
20
19
 
21
20
  def initialize(activity, unit_system)
22
21
  @activity = activity
22
+ @sport = activity.fit_activity.sessions[0].sport
23
23
  @unit_system = unit_system
24
24
  @empty_charts = {}
25
25
  end
@@ -35,7 +35,11 @@ module PostRunner
35
35
  end
36
36
 
37
37
  def div(doc)
38
- chart_div(doc, 'pace', "Pace (#{select_unit('min/km')})")
38
+ if @sport == 'running'
39
+ chart_div(doc, 'pace', "Pace (#{select_unit('min/km')})")
40
+ else
41
+ chart_div(doc, 'speed', "Speed (#{select_unit('km/h')})")
42
+ end
39
43
  chart_div(doc, 'altitude', "Elevation (#{select_unit('m')})")
40
44
  chart_div(doc, 'heart_rate', 'Heart Rate (bpm)')
41
45
  chart_div(doc, 'run_cadence', 'Run Cadence (spm)')
@@ -51,7 +55,7 @@ module PostRunner
51
55
  when :metric
52
56
  metric_unit
53
57
  when :statute
54
- { 'min/km' => 'min/mi', 'm' => 'ft', 'cm' => 'in',
58
+ { 'min/km' => 'min/mi', 'm' => 'ft', 'cm' => 'in', 'km/h' => 'mph',
55
59
  'bpm' => 'bpm', 'spm' => 'spm', 'ms' => 'ms' }[metric_unit]
56
60
  else
57
61
  Log.fatal "Unknown unit system #{@unit_system}"
@@ -91,22 +95,27 @@ EOT
91
95
  def java_script
92
96
  s = "$(function() {\n"
93
97
 
94
- s << line_graph('pace', 'min/km', '#0A7BEE' )
95
- s << line_graph('altitude', 'm', '#5AAA44')
96
- s << line_graph('heart_rate', 'bpm', '#900000')
97
- s << point_graph('run_cadence', 'spm',
98
+ s << tooltip_div
99
+ if @sport == 'running'
100
+ s << line_graph('pace', 'Pace', 'min/km', '#0A7BEE' )
101
+ else
102
+ s << line_graph('speed', 'Speed', 'km/h', '#0A7BEE' )
103
+ end
104
+ s << line_graph('altitude', 'Elevation', 'm', '#5AAA44')
105
+ s << line_graph('heart_rate', 'Heart Rate', 'bpm', '#900000')
106
+ s << point_graph('run_cadence', 'Run Cadence', 'spm',
98
107
  [ [ '#EE3F2D', 151 ],
99
108
  [ '#F79666', 163 ],
100
109
  [ '#A0D488', 174 ],
101
110
  [ '#96D7DE', 185 ],
102
111
  [ '#A88BBB', nil ] ])
103
- s << point_graph('vertical_oscillation', 'cm',
112
+ s << point_graph('vertical_oscillation', 'Vertical Oscillation', 'cm',
104
113
  [ [ '#A88BBB', 67 ],
105
114
  [ '#96D7DE', 84 ],
106
115
  [ '#A0D488', 101 ],
107
116
  [ '#F79666', 118 ],
108
117
  [ '#EE3F2D', nil ] ])
109
- s << point_graph('stance_time', 'ms',
118
+ s << point_graph('stance_time', 'Ground Contact Time', 'ms',
110
119
  [ [ '#A88BBB', 208 ],
111
120
  [ '#96D7DE', 241 ],
112
121
  [ '#A0D488', 273 ],
@@ -118,19 +127,52 @@ EOT
118
127
  s
119
128
  end
120
129
 
121
- def line_graph(field, unit, color = nil)
130
+ def tooltip_div
131
+ <<"EOT"
132
+ function timeToHMS(usecs) {
133
+ var secs = parseInt(usecs / 1000.0);
134
+ var s = secs % 60;
135
+ var mins = parseInt(secs / 60);
136
+ var m = mins % 60;
137
+ var h = parseInt(mins / 60);
138
+ s = (s < 10) ? "0" + s : s;
139
+ if (h == 0) {
140
+ return ("" + m + ":" + s);
141
+ } else {
142
+ m = (m < 10) ? "0" + m : m;
143
+ return ("" + h + ":" + m + ":" + s);
144
+ }
145
+ };
146
+ $("<div id='tooltip'></div>").css({
147
+ position: "absolute",
148
+ display: "none",
149
+ border: "1px solid #888",
150
+ padding: "2px",
151
+ "background-color": "#EEE",
152
+ opacity: 0.90,
153
+ "font-size": "8pt"
154
+ }).appendTo("body");
155
+ EOT
156
+ end
157
+
158
+ def line_graph(field, y_label, unit, color = nil)
122
159
  s = "var #{field}_data = [\n"
123
160
 
124
161
  data_set = []
125
162
  start_time = @activity.fit_activity.sessions[0].start_time.to_i
163
+ min_value = nil
126
164
  @activity.fit_activity.records.each do |r|
127
165
  value = r.get_as(field, select_unit(unit))
128
166
  if field == 'pace'
129
- if value > 20.0
167
+ # Slow speeds lead to very large pace values that make the graph
168
+ # hard to read. We cap the pace at 20.0 min/km to keep it readable.
169
+ if value > (@unit_system == :metric ? 20.0 : 36.0 )
130
170
  value = nil
131
171
  else
132
172
  value = (value * 3600.0 * 1000).to_i
133
173
  end
174
+ else
175
+ min_value = value if value && (min_value.nil? || min_value > value)
134
176
  end
135
177
  data_set << [ ((r.timestamp.to_i - start_time) * 1000).to_i, value ]
136
178
  end
@@ -144,25 +186,31 @@ EOT
144
186
  "[ #{set[0]}, #{set[1] ? set[1] : 'null'} ]"
145
187
  end.join(', ')
146
188
 
189
+ chart_id = "#{field}_chart"
147
190
  s << <<"EOT"
148
191
  ];
149
192
 
150
- $.plot("##{field}_chart",
193
+ $.plot(\"##{chart_id}\",
151
194
  [ { data: #{field}_data,
152
195
  #{color ? "color: \"#{color}\"," : ''}
153
196
  lines: { show: true#{field == 'pace' ? '' :
154
197
  ', fill: true'} } } ],
155
- { xaxis: { mode: "time" }
198
+ { xaxis: { mode: "time" },
199
+ grid: { hoverable: true }
156
200
  EOT
157
201
  if field == 'pace'
158
202
  s << ", yaxis: { mode: \"time\",\n" +
159
203
  " transform: function (v) { return -v; },\n" +
160
204
  " inverseTransform: function (v) { return -v; } }"
205
+ else
206
+ # Set the minimum slightly below the lowest found value.
207
+ s << ", yaxis: { min: #{0.9 * min_value} }"
161
208
  end
162
209
  s << "});\n"
210
+ s << hover_function(chart_id, y_label, select_unit(unit)) + "\n"
163
211
  end
164
212
 
165
- def point_graph(field, unit, colors)
213
+ def point_graph(field, y_label, unit, colors)
166
214
  # We need to split the field values into separate data sets for each
167
215
  # color. The max value for each color determines which set a data point
168
216
  # ends up in.
@@ -207,14 +255,17 @@ EOT
207
255
  s << " ];\n"
208
256
  end
209
257
 
210
- s << "$.plot(\"##{field}_chart\", [\n"
258
+ chart_id = "#{field}_chart"
259
+ s << "$.plot(\"##{chart_id}\", [\n"
211
260
  s << data_sets.map do |index, ds|
212
261
  "{ data: #{field}_data_#{index},\n" +
213
262
  " color: \"#{colors[index][0]}\",\n" +
214
263
  " points: { show: true, fillColor: \"#{colors[index][0]}\", " +
215
264
  " fill: true, radius: 2 } }"
216
265
  end.join(', ')
217
- s << "], { xaxis: { mode: \"time\" } });\n"
266
+ s << "], { xaxis: { mode: \"time\" },
267
+ grid: { hoverable: true } });\n"
268
+ s << hover_function(chart_id, y_label, select_unit(unit))
218
269
 
219
270
  s
220
271
  end
@@ -228,6 +279,24 @@ EOT
228
279
  }
229
280
  end
230
281
 
282
+ def hover_function(chart_id, y_label, y_unit)
283
+ <<"EOT"
284
+ $("##{chart_id}").bind("plothover", function (event, pos, item) {
285
+ if (item) {
286
+ var x = timeToHMS(item.datapoint[0]);
287
+ var y = #{y_label == 'Pace' ? 'timeToHMS(item.datapoint[1] / 60)' :
288
+ 'item.datapoint[1].toFixed(0)'};
289
+ $("#tooltip").html("<b>#{y_label}:</b> " + y + " #{y_unit}<br/>" +
290
+ "<b>Time:</b> " + x + " h:m:s")
291
+ .css({top: item.pageY-20, left: item.pageX+15})
292
+ .fadeIn(200);
293
+ } else {
294
+ $("#tooltip").hide();
295
+ }
296
+ });
297
+ EOT
298
+ end
299
+
231
300
  end
232
301
 
233
302
  end
@@ -83,9 +83,9 @@ EOT
83
83
  end
84
84
 
85
85
  opts.separator ""
86
- opts.separator "Options for the 'import' and 'rename' command:"
86
+ opts.separator "Options for the 'import' command:"
87
87
  opts.on('--name name', String,
88
- 'Name or rename the activity to the specified name') do |n|
88
+ 'Name the activity to the specified name') do |n|
89
89
  @name = n
90
90
  end
91
91
 
@@ -163,6 +163,12 @@ EOT
163
163
 
164
164
  def execute_command(args)
165
165
  @activities = ActivitiesDB.new(@db_dir, @cfg)
166
+ if @cfg.get_option(:version) != VERSION
167
+ Log.warn "Version upgrade detected. Database conversion started..."
168
+ @activities.generate_all_html_reports
169
+ @cfg.set_option(:version, VERSION)
170
+ Log.info "Version upgrade completed."
171
+ end
166
172
 
167
173
  case (cmd = args.shift)
168
174
  when 'check'
@@ -194,6 +200,9 @@ EOT
194
200
  when 'records'
195
201
  @activities.show_records
196
202
  when 'rename'
203
+ unless (@name = args.shift)
204
+ Log.fatal "You must provide a new name for the activity"
205
+ end
197
206
  process_activities(args, :rename)
198
207
  when 'show'
199
208
  if args.empty?
@@ -23,6 +23,7 @@ module PostRunner
23
23
  # @param dir [String] the directory to hold the config.yml file.
24
24
  def initialize(dir)
25
25
  @options = {
26
+ :version => '0.0.0',
26
27
  :unit_system => :metric,
27
28
  :import_dir => nil
28
29
  }
@@ -117,19 +117,19 @@ EOT
117
117
  def track_points(script)
118
118
  first = true
119
119
  @activity.fit_activity.sessions.each do |session|
120
- session.laps.each do |lap|
121
- lap.records.each do |record|
122
- long = record.position_long
123
- lat = record.position_lat
124
- if first
125
- first = false
126
- else
127
- script << ","
128
- end
129
- script << <<"EOT"
120
+ session.records.each do |record|
121
+ long = record.position_long
122
+ lat = record.position_lat
123
+ next unless long && lat
124
+
125
+ if first
126
+ first = false
127
+ else
128
+ script << ","
129
+ end
130
+ script << <<"EOT"
130
131
  new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(#{long}, #{lat}).transform(geographic, mercator))
131
132
  EOT
132
- end
133
133
  end
134
134
  end
135
135
  end
@@ -11,5 +11,5 @@
11
11
  #
12
12
 
13
13
  module PostRunner
14
- VERSION = "0.0.5"
14
+ VERSION = "0.0.6"
15
15
  end
@@ -7,9 +7,9 @@ Gem::Specification.new do |spec|
7
7
  spec.name = "postrunner"
8
8
  spec.version = PostRunner::VERSION
9
9
  spec.authors = ["Chris Schlaeger"]
10
- spec.email = ["chris@linux.com"]
10
+ spec.email = ["chris@taskjuggler.org"]
11
11
  spec.summary = %q{Application to manage and analyze Garmin FIT files.}
12
- spec.description = %q{This application will allow you to manage and analyze .FIT files as generated by Garmin GPS devices. The application was developed for the Garmin Forerunner 620. Other devices may or may not work. Only devices that expose themselves as USB Mass Storage devices are supported.}
12
+ spec.description = %q{This application will allow you to manage and analyze .FIT files such as those generated by Garmin GPS devices. The application was developed for the Garmin Forerunner 620. Other devices may work as well. They need to export the data as USB Mass Storage devices.}
13
13
  spec.homepage = 'https://github.com/scrapper/postrunner'
14
14
  spec.license = "GNU GPL version 2"
15
15
 
@@ -19,11 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
  spec.required_ruby_version = '>=2.0'
21
21
 
22
- spec.add_dependency "fit4ruby", "~> 0.0.3"
23
- spec.add_dependency "nokogiri", "~> 1.6"
22
+ spec.add_dependency 'fit4ruby', '~> 0.0.4'
23
+ spec.add_dependency 'nokogiri', '~> 1.6'
24
24
 
25
- spec.add_development_dependency "bundler", "~> 1.6"
26
- spec.add_development_dependency "rake"
27
- spec.add_development_dependency "rspec"
28
- spec.add_development_dependency "yard"
25
+ spec.add_development_dependency 'bundler', '~> 1.6'
26
+ spec.add_development_dependency 'rake', '~> 0.9.6'
27
+ spec.add_development_dependency 'rspec', '~> 2.14.1'
28
+ spec.add_development_dependency 'yard', '~> 0.8.7'
29
29
  end
@@ -100,7 +100,7 @@ describe PostRunner::Main do
100
100
  end
101
101
 
102
102
  it 'should rename FILE2.FIT activity' do
103
- postrunner(%w( rename :1 --name foobar ))
103
+ postrunner(%w( rename foobar :1 ))
104
104
  list = postrunner(%w( list ))
105
105
  list.index('FILE2.FIT').should be_nil
106
106
  list.index('foobar').should be_a(Fixnum)
@@ -122,5 +122,21 @@ describe PostRunner::Main do
122
122
  postrunner(%w( units metric ))
123
123
  end
124
124
 
125
+ it 'should properly upgrade to a new version' do
126
+ # Change version in config file to 0.0.0.
127
+ rc = PostRunner::RuntimeConfig.new(@db_dir)
128
+ rc.set_option(:version, '0.0.0')
129
+ # Check that the config file really was changed.
130
+ rc = PostRunner::RuntimeConfig.new(@db_dir)
131
+ rc.get_option(:version).should == '0.0.0'
132
+
133
+ # Run some command.
134
+ postrunner(%w( list ))
135
+
136
+ # Check that version matches the current version again.
137
+ rc = PostRunner::RuntimeConfig.new(@db_dir)
138
+ rc.get_option(:version).should == PostRunner::VERSION
139
+ end
140
+
125
141
  end
126
142
 
@@ -38,7 +38,7 @@ def create_fit_activity(date, duration_minutes)
38
38
  end
39
39
  ts += 60
40
40
  end
41
- a.new_session({ :timestamp => ts })
41
+ a.new_session({ :timestamp => ts, :sport => 'running' })
42
42
  a.new_event({ :timestamp => ts, :event => 'recovery_time',
43
43
  :event_type => 'marker',
44
44
  :data => 2160 })
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postrunner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Schlaeger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-29 00:00:00.000000000 Z
11
+ date: 2014-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fit4ruby
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.3
19
+ version: 0.0.4
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.3
26
+ version: 0.0.4
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: nokogiri
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -56,50 +56,50 @@ dependencies:
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 0.9.6
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ~>
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 0.9.6
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ~>
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: 2.14.1
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ~>
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: 2.14.1
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: yard
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ~>
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: 0.8.7
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ~>
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
97
- description: This application will allow you to manage and analyze .FIT files as generated
98
- by Garmin GPS devices. The application was developed for the Garmin Forerunner 620.
99
- Other devices may or may not work. Only devices that expose themselves as USB Mass
100
- Storage devices are supported.
96
+ version: 0.8.7
97
+ description: This application will allow you to manage and analyze .FIT files such
98
+ as those generated by Garmin GPS devices. The application was developed for the
99
+ Garmin Forerunner 620. Other devices may work as well. They need to export the data
100
+ as USB Mass Storage devices.
101
101
  email:
102
- - chris@linux.com
102
+ - chris@taskjuggler.org
103
103
  executables:
104
104
  - postrunner
105
105
  extensions: []