postrunner 0.10.1 → 0.11.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 800922f3ea3d5ff42ba5e706be3e2bff5dbed0ad
4
- data.tar.gz: 20e85ae14a306f2c662defabaa3c18b19d6bcec6
3
+ metadata.gz: d8de6ac38768c84e90eb47ddb49429d7a74160b1
4
+ data.tar.gz: 7816eba71ed5b6f90eef7d7ae15080c19d548624
5
5
  SHA512:
6
- metadata.gz: 3e4983e623b4c575937d1b9d419b7f874532affe4ebd8e1e849f58027fa59459365b38804396c794f88649949cc78ed43fa0019ef164c4f8a0f8ef6c3409e19d
7
- data.tar.gz: 3b35e7874e9418bc1d3cd39b1d8574ad169f69ca17b5375dee6423287697b475955126d29503e72242e0a04cfc893daf2195f45649e6c82594acf40548b06551
6
+ metadata.gz: 3302269a229d363cfc75bbea304879f06c37e8c2e6c1706ebece3ce6b662b02a026315df59e7494de230c5c3cab3fb81b925bd0ef89f4b81b1280805b422b138
7
+ data.tar.gz: e8179a0afb54a171ba200480cf498523688acffa9f3568b8a2ae1e272a5bdc4c863449c6c43b201dafe6223afeb64f64ca3520799e93317f4aec8114cdcb64f2
@@ -95,6 +95,7 @@ module PostRunner
95
95
  'challenge' => 'Challenge',
96
96
  'indoor_skiing' => 'Indoor Skiing',
97
97
  'cardio_training' => 'Cardio Training',
98
+ 'virtual_activity' => 'Virtual Activity',
98
99
  'all' => 'All'
99
100
  }
100
101
 
@@ -379,7 +380,7 @@ module PostRunner
379
380
  end
380
381
 
381
382
  def activity_sub_type
382
- ActivitySubTypes[@sub_sport] || 'Undefined'
383
+ ActivitySubTypes[@sub_sport] || 'Undefined "#{@sub_sport}"'
383
384
  end
384
385
 
385
386
  def distance(timestamp, unit_system)
@@ -87,6 +87,7 @@ module PostRunner
87
87
  { :metric => 'km', :statute => 'mi'}) ])
88
88
  end
89
89
  t.row([ 'Time:', secsToHMS(session.total_timer_time) ])
90
+ t.row([ 'Elapsed Time:', secsToHMS(session.total_elapsed_time) ])
90
91
  t.row([ 'Avg. Speed:',
91
92
  local_value(session, 'avg_speed', '%.1f %s',
92
93
  { :metric => 'km/h', :statute => 'mph' }) ])
@@ -145,6 +146,14 @@ module PostRunner
145
146
  t.row([ 'Aerobic Training Effect:', session.total_training_effect ])
146
147
  end
147
148
 
149
+ if (p_epoc = peak_epoc) > 0.0
150
+ t.row([ 'Peak EPOC:', "%.0f ml/kg" % p_epoc ])
151
+ end
152
+
153
+ if (trimp = trimp_exp) > 0.0
154
+ t.row([ 'TRIMP:', trimp.round ])
155
+ end
156
+
148
157
  rec_info = @fit_activity.recovery_info
149
158
  t.row([ 'Ignored Recovery Time:',
150
159
  rec_info ? secsToDHMS(rec_info * 60) : '-' ])
@@ -361,6 +370,89 @@ module PostRunner
361
370
  end
362
371
  end
363
372
 
373
+ def trimp_exp
374
+ # According to Bannister/Morton
375
+ # TRIMPexp = sum(D x HRr x 0.64e^y)
376
+ # Where
377
+ #
378
+ # D is the duration in minutes at a particular Heart Rate
379
+ # HRr is the Heart Rate as a fraction of Heart Rate Reserve
380
+ # y is the HRr multiplied by 1.92 for men and 1.67 for women.
381
+ return 0.0 unless (user_data = @fit_activity.user_data.first)
382
+
383
+ user_profile = @fit_activity.user_profiles.first
384
+ hr_zones = @fit_activity.heart_rate_zones.first
385
+ session = @fit_activity.sessions[0]
386
+
387
+ unless (user_profile && (rest_hr = user_profile.resting_heart_rate)) ||
388
+ (hr_zones && (rest_hr = hr_zones.resting_heart_rate))
389
+ # We must have a valid resting heart rate to compute TRIMP.
390
+ return 0.0
391
+ end
392
+ unless (user_data && (max_hr = user_data.max_hr)) ||
393
+ (hr_zones && (max_hr = hr_zones.max_heart_rate))
394
+ # We must have a valid maximum heart rate to compute TRIMP.
395
+ return 0.0
396
+ end
397
+ unless (session && session.avg_heart_rate &&
398
+ avg_hr = session.avg_heart_rate)
399
+ return 0.0
400
+ end
401
+
402
+ sex_factor = user_data.gender == 'male' ? 1.92 : 1.67
403
+
404
+ # Instead of using the average heart rate for the whole activity we
405
+ # apply the equation for each heart rate sample and accumulate them.
406
+ sum = 0.0
407
+ prev_timestamp = nil
408
+ @activity.fit_activity.records.each do |r|
409
+ # We need a valid timestmap and a valid previous timestamp. If they
410
+ # are more than 10 seconds appart we discard the values as there was
411
+ # likely a pause in the activity.
412
+ if prev_timestamp && r.timestamp && r.heart_rate &&
413
+ r.timestamp - prev_timestamp <= 10
414
+ # Compute the heart rate as fraction of the heart rate reserve
415
+ hr_r = (r.heart_rate - rest_hr).to_f / (max_hr - rest_hr)
416
+
417
+ duration_min = (r.timestamp - prev_timestamp) / 60.0
418
+ #sum += duration_min * hr_r * 0.64 * Math.exp(sex_factor * hr_r)
419
+ sum += duration_min * hr_r * 0.64 * Math.exp(sex_factor * hr_r)
420
+ end
421
+
422
+ prev_timestamp = r.timestamp
423
+ end
424
+
425
+ sum
426
+
427
+ # Alternatively here is an avarage HR based implementation
428
+ # hr_r = (session.avg_heart_rate - rest_hr).to_f / (max_hr - rest_hr)
429
+ # duration_min = session.total_elapsed_time / 60.0
430
+ # duration_min * hr_r * 0.64 * Math.exp(sex_factor * hr_r)
431
+ end
432
+
433
+ def peak_epoc
434
+ # Peak EPOC value according to figure 2 in the following white paper by
435
+ # FristBeat:
436
+ # https://www.firstbeat.com/wp-content/uploads/2015/10/white_paper_training_effect.pdf
437
+ unless @fit_activity.physiological_metrics &&
438
+ (pm = @fit_activity.physiological_metrics.last) &&
439
+ (te = pm.aerobic_training_effect)
440
+ return 0.0
441
+ end
442
+ unless (user_data = @fit_activity.user_data.first) &&
443
+ (ac = user_data.activity_class)
444
+ return 0.0
445
+ end
446
+
447
+ # The following formula was taken from
448
+ # http://www.movescount.com/apps/app10020404-EPOC_from_TE
449
+ # It apparently approximates the graph in figure 2 in the FirstBeat
450
+ # paper.
451
+ epoc = -11.0 + te * (20.0 + te * (-47.0/4.0 + te * (3.0 - te / 4.0)))
452
+ (-102.0 + te * (759.0 / 4.0 + te * (-2867.0 / 24.0 +
453
+ te * (139.0 / 4.0 - 73.0 / 24.0 * te))) - epoc) / 10.0 * ac + epoc
454
+ end
455
+
364
456
  end
365
457
 
366
458
  end
@@ -290,10 +290,10 @@ EOT
290
290
  last_value = nil
291
291
  last_timestamp = nil
292
292
  @activity.fit_activity.records.each do |r|
293
- if last_timestamp && (r.timestamp - last_timestamp) > 5.0
293
+ if last_timestamp && (r.timestamp - last_timestamp) > 10.0
294
294
  # We have a gap in the values that is longer than 5 seconds. We'll
295
295
  # finish the line and start a new one later.
296
- data_set << [ (r.timestamp - start_time + 1).to_i * 1000, nil ]
296
+ data_set << [ (last_timestamp - start_time + 1).to_i * 1000, nil ]
297
297
  end
298
298
  if (value = r.get_as(chart[:id], chart[:unit] || ''))
299
299
  if chart[:id] == 'pace'
@@ -310,7 +310,7 @@ EOT
310
310
  min_value = value if (min_value.nil? || min_value > value)
311
311
  end
312
312
  end
313
- unless last_value.nil? && value.nil?
313
+ if value
314
314
  data_set << [ (r.timestamp - start_time).to_i * 1000, value ]
315
315
  end
316
316
  last_value = value
@@ -337,7 +337,7 @@ EOT
337
337
  #{chart[:colors] ? "color: \"#{chart[:colors]}\"," : ''}
338
338
  lines: { show: true#{chart[:id] == 'pace' ? '' :
339
339
  ', fill: true'} } } ],
340
- { xaxis: { mode: "time" },
340
+ { xaxis: { mode: "time", min: 0.0 },
341
341
  grid: { markings: lap_marks, hoverable: true }
342
342
  EOT
343
343
  if chart[:id] == 'pace'
@@ -417,7 +417,7 @@ EOT
417
417
  " fillColor: \"#{chart[:colors][index][0]}\", " +
418
418
  " fill: true, radius: 2 } }"
419
419
  end.join(', ')
420
- s << "], { xaxis: { mode: \"time\" }, " +
420
+ s << "], { xaxis: { mode: \"time\", min: 0.0 }, " +
421
421
  (chart[:id] == 'gct_balance' ? gct_balance_yaxis(data_sets) : '') +
422
422
  " grid: { markings: lap_marks, hoverable: true } });\n"
423
423
  s << lap_mark_labels(chart_id, start_time)
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # = DeviceList.rb -- PostRunner - Manage the data from your Garmin sport devices.
5
5
  #
6
- # Copyright (c) 2014, 2015 by Chris Schlaeger <cs@taskjuggler.org>
6
+ # Copyright (c) 2014, 2015, 2018 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,6 +12,7 @@
12
12
 
13
13
  require 'uri'
14
14
  require 'net/http'
15
+ require 'net/https'
15
16
 
16
17
  module PostRunner
17
18
 
@@ -20,7 +21,7 @@ module PostRunner
20
21
  # devices pick up this file under GARMIN/GARMIN/REMOTESW/EPO.BIN.
21
22
  class EPO_Downloader
22
23
 
23
- @@URI = URI('http://omt.garmin.com/Rce/ProtobufApi/EphemerisService/GetEphemerisData')
24
+ @@URI = URI('https://omt.garmin.com/Rce/ProtobufApi/EphemerisService/GetEphemerisData')
24
25
  # This is the payload of the POST request. It was taken from
25
26
  # http://www.kluenter.de/garmin-ephemeris-files-and-linux/. It may contain
26
27
  # a product ID or serial number.
@@ -33,7 +34,8 @@ module PostRunner
33
34
 
34
35
  # Create an EPO_Downloader object.
35
36
  def initialize
36
- @http = Net::HTTP.new(@@URI.host, @@URI.port)
37
+ @https = Net::HTTP.new(@@URI.host, @@URI.port)
38
+ @https.use_ssl = true
37
39
  @request = Net::HTTP::Post.new(@@URI.path, initheader = @@HEADER)
38
40
  @request.body = @@POST_DATA
39
41
  end
@@ -57,7 +59,7 @@ module PostRunner
57
59
 
58
60
  def get_epo_from_server
59
61
  begin
60
- res = @http.request(@request)
62
+ res = @https.request(@request)
61
63
  rescue => e
62
64
  Log.error "Extended Prediction Orbit (EPO) data download error: " +
63
65
  e.message
@@ -93,6 +93,7 @@ module PostRunner
93
93
  'challenge' => 'Challenge',
94
94
  'indoor_skiing' => 'Indoor Skiing',
95
95
  'cardio_training' => 'Cardio Training',
96
+ 'virtual_activity' => 'Virtual Activity',
96
97
  'all' => 'All'
97
98
  }
98
99
 
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # = FFS_Device.rb -- PostRunner - Manage the data from your Garmin sport devices.
5
5
  #
6
- # Copyright (c) 2015, 2016 by Chris Schlaeger <cs@taskjuggler.org>
6
+ # Copyright (c) 2015, 2016, 2018 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
@@ -23,7 +23,7 @@ module PostRunner
23
23
  # dashes. All objects are transparently stored in the PEROBS::Store.
24
24
  class FFS_Device < PEROBS::Object
25
25
 
26
- attr_persist :activities, :monitorings, :short_uid, :long_uid
26
+ attr_persist :activities, :monitorings, :metrics, :short_uid, :long_uid
27
27
 
28
28
  # Create a new FFS_Device object.
29
29
  # @param p [PEROBS::Handle] p
@@ -41,6 +41,7 @@ module PostRunner
41
41
  def restore
42
42
  attr_init(:activities) { @store.new(PEROBS::Array) }
43
43
  attr_init(:monitorings) { @store.new(PEROBS::Array) }
44
+ attr_init(:metrics) { @store.new(PEROBS::Array) }
44
45
  end
45
46
 
46
47
  # Add a new FIT file for this device.
@@ -61,6 +62,11 @@ module PostRunner
61
62
  entities = @monitorings
62
63
  type = 'monitoring'
63
64
  new_entity_class = FFS_Monitoring
65
+ elsif fit_entity.is_a?(Fit4Ruby::Metrics)
66
+ entity = metrics_by_file_name(File.basename(fit_file_name))
67
+ entities = @metrics
68
+ type = 'metrics'
69
+ new_entity_class = FFS_Metrics
64
70
  else
65
71
  Log.fatal "Unsupported FIT entity #{fit_entity.class}"
66
72
  end
@@ -130,6 +136,13 @@ module PostRunner
130
136
  @monitorings.find { |a| a.fit_file_name == file_name }
131
137
  end
132
138
 
139
+ # Return the metrics with the given file name.
140
+ # @param file_name [String] Base name of the fit file.
141
+ # @return [FFS_Activity] Corresponding FFS_Metrics or nil.
142
+ def metrics_by_file_name(file_name)
143
+ @metrics.find { |a| a.fit_file_name == file_name }
144
+ end
145
+
133
146
  # Return all monitorings that overlap with the time interval given by
134
147
  # from_time and to_time.
135
148
  # @param from_time [Time] start time of the interval
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # = FitFileStore.rb -- PostRunner - Manage the data from your Garmin sport devices.
5
5
  #
6
- # Copyright (c) 2014, 2015, 2016 by Chris Schlaeger <cs@taskjuggler.org>
6
+ # Copyright (c) 2014, 2015, 2016, 2018 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
@@ -80,20 +80,15 @@ module PostRunner
80
80
  # @return [FFS_Activity or FFS_Monitoring] Corresponding entry in the
81
81
  # FitFileStore or nil if file could not be added.
82
82
  def add_fit_file(fit_file_name, fit_entity = nil, overwrite = false)
83
- md5sum = FitFileStore.calc_md5_sum(fit_file_name)
84
- if @store['fit_file_md5sums'].include?(md5sum)
85
- # The FIT file is already stored in the DB.
86
- return nil unless overwrite
87
- end
88
-
89
- # If we the file hasn't been read yet, read it in as a
83
+ # If the file hasn't been read yet, read it in as a
90
84
  # Fit4Ruby::Activity or Fit4Ruby::Monitoring entity.
91
85
  unless fit_entity
92
86
  return nil unless (fit_entity = read_fit_file(fit_file_name))
93
87
  end
94
88
 
95
89
  unless [ Fit4Ruby::Activity,
96
- Fit4Ruby::Monitoring_B ].include?(fit_entity.class)
90
+ Fit4Ruby::Monitoring_B,
91
+ Fit4Ruby::Metrics ].include?(fit_entity.class)
97
92
  Log.fatal "Unsupported FIT file type #{fit_entity.class}"
98
93
  end
99
94
 
@@ -451,7 +446,8 @@ module PostRunner
451
446
 
452
447
  def extract_fit_file_id(fit_entity)
453
448
  unless (fid = fit_entity.file_id)
454
- Log.fatal 'FIT file has no file_id section'
449
+ Log.error 'FIT file has no file_id section'
450
+ return nil
455
451
  end
456
452
 
457
453
  if fid.manufacturer == 'garmin' &&
@@ -470,10 +466,20 @@ module PostRunner
470
466
  }
471
467
  end
472
468
  end
473
- Log.fatal "Fit entity has no device info for 0"
469
+ Log.error "Fit entity has no device info for 0"
470
+ return nil
474
471
  else
475
472
  # And for all properly developed devices we can just look at the
476
473
  # file_id section.
474
+ if fid.manufacturer.nil? ||
475
+ fid.manufacturer[0..'Undocumented value'.length - 1] ==
476
+ 'Undocumented value'
477
+ Log.error "Cannot store FIT files for unknown manufacturer " +
478
+ fid.manufacturer
479
+ return nil
480
+ end
481
+ fid.serial_number ||= 0
482
+
477
483
  return {
478
484
  :manufacturer => fid.manufacturer,
479
485
  :product => fid.garmin_product || fid.product,
@@ -57,7 +57,9 @@ module PostRunner
57
57
  create_directory(@db_dir, 'PostRunner data')
58
58
  ensure_flat_file_db
59
59
  @db = PEROBS::Store.new(File.join(@db_dir, 'database'),
60
- { :engine => PEROBS::FlatFileDB })
60
+ { :engine => PEROBS::FlatFileDB,
61
+ :progressmeter =>
62
+ PEROBS::ConsoleProgressMeter.new })
61
63
  # Create a hash to store configuration data in the store unless it
62
64
  # exists already.
63
65
  cfg = (@db['config'] ||= @db.new(PEROBS::Hash))
@@ -252,7 +254,7 @@ weekly [ <YYYY-MM-DD> ]
252
254
  week. If no date is given, yesterday's week will be used.
253
255
 
254
256
 
255
- <fit file> An absolute or relative name of a .FIT file.
257
+ <fit file> An absolute or relative name of a .FIT or .fit file.
256
258
 
257
259
  <ref> The index or a range of indexes to activities in the database.
258
260
  :1 is the newest imported activity
@@ -437,12 +439,12 @@ EOT
437
439
 
438
440
  def process_files(files_or_dirs, command)
439
441
  if files_or_dirs.empty?
440
- Log.abort("You must provide at least one .FIT file name.")
442
+ Log.abort("You must provide at least one .FIT or .fit file name.")
441
443
  end
442
444
 
443
445
  files_or_dirs.each do |fod|
444
446
  if File.directory?(fod)
445
- Dir.glob(File.join(fod, '*.FIT')).each do |file|
447
+ Dir.glob(File.join(fod, '*.FIT'), File::FNM_CASEFOLD).each do |file|
446
448
  process_file(file, command)
447
449
  end
448
450
  else
@@ -472,6 +474,12 @@ EOT
472
474
  # @return [TrueClass, FalseClass] true if file was successfully imported,
473
475
  # false otherwise
474
476
  def import_fit_file(fit_file_name)
477
+ md5sum = FitFileStore.calc_md5_sum(fit_file_name)
478
+ if @ffs.store['fit_file_md5sums'].include?(md5sum)
479
+ # The FIT file is already stored in the DB.
480
+ return nil unless @force
481
+ end
482
+
475
483
  begin
476
484
  fit_entity = Fit4Ruby.read(fit_file_name)
477
485
  rescue Fit4Ruby::Error
@@ -24,14 +24,14 @@ module PostRunner
24
24
  end
25
25
 
26
26
  def to_html(doc)
27
- return nil if @fit_activity.user_profiles.empty?
27
+ return nil if @fit_activity.user_data.empty?
28
28
 
29
29
  ViewFrame.new('user_profile', 'User Profile', 600, profile,
30
30
  true).to_html(doc)
31
31
  end
32
32
 
33
33
  def to_s
34
- return '' if @fit_activity.user_profiles.empty?
34
+ return '' if @fit_activity.user_data.empty?
35
35
  profile.to_s
36
36
  end
37
37
 
@@ -39,31 +39,44 @@ module PostRunner
39
39
 
40
40
  def profile
41
41
  t = FlexiTable.new
42
- profile = @fit_activity.user_profiles.first
43
- if profile.height
42
+
43
+ user_data = @fit_activity.user_data.first
44
+ user_profile = @fit_activity.user_profiles.first
45
+ hr_zones = @fit_activity.heart_rate_zones.first
46
+
47
+ if user_data.height
44
48
  unit = { :metric => 'm', :statute => 'ft' }[@unit_system]
45
- height = profile.get_as('height', unit)
49
+ height = user_data.get_as('height', unit)
46
50
  t.cell('Height:', { :width => '40%' })
47
51
  t.cell("#{'%.2f' % height} #{unit}", { :width => '60%' })
48
52
  t.new_row
49
53
  end
50
- if profile.weight
54
+ if user_data.weight
51
55
  unit = { :metric => 'kg', :statute => 'lbs' }[@unit_system]
52
- weight = profile.get_as('weight', unit)
56
+ weight = user_data.get_as('weight', unit)
53
57
  t.row([ 'Weight:', "#{'%.1f' % weight} #{unit}" ])
54
58
  end
55
- t.row([ 'Gender:', profile.gender ]) if profile.gender
56
- t.row([ 'Age:', "#{profile.age} years" ]) if profile.age
57
- t.row([ 'Max. Heart Rate:', "#{profile.max_hr} bpm" ]) if profile.max_hr
58
- if (lthr = profile.running_lactate_threshold_heart_rate)
59
+ t.row([ 'Gender:', user_data.gender ]) if user_data.gender
60
+ t.row([ 'Age:', "#{user_data.age} years" ]) if user_data.age
61
+ if (user_profile && (rest_hr = user_profile.resting_heart_rate)) ||
62
+ (hr_zones && (rest_hr = hr_zones.resting_heart_rate))
63
+ t.row([ 'Resting Heart Rate:', "#{rest_hr} bpm" ])
64
+ end
65
+ if (max_hr = user_data.max_hr) ||
66
+ (max_hr = hr_zones.max_heart_rate)
67
+ t.row([ 'Max. Heart Rate:', "#{max_hr} bpm" ])
68
+ end
69
+ if (lthr = user_data.running_lactate_threshold_heart_rate)
59
70
  t.row([ 'Running LTHR:', "#{lthr} bpm" ])
60
71
  end
61
- if profile.activity_class
62
- t.row([ 'Activity Class:', profile.activity_class ])
72
+ if (activity_class = user_data.activity_class)
73
+ t.row([ 'Activity Class:', activity_class ])
74
+ end
75
+ if (metmax = user_data.metmax)
76
+ t.row([ 'METmax:', "#{metmax} MET" ])
63
77
  end
64
- if profile.metmax
65
- t.row([ 'METmax:', "#{profile.metmax} MET" ])
66
- t.row([ 'VO2max:', "#{'%.1f' % (profile.metmax * 3.5)} ml/kg/min" ])
78
+ if (vo2max = @fit_activity.vo2max)
79
+ t.row([ 'VO2max:', "#{'%.1f' % vo2max} ml/kg/min" ])
67
80
  end
68
81
  t
69
82
  end
@@ -11,5 +11,5 @@
11
11
  #
12
12
 
13
13
  module PostRunner
14
- VERSION = '0.10.1'
14
+ VERSION = '0.11.0'
15
15
  end
@@ -28,8 +28,8 @@ operating systems as well.}
28
28
  spec.require_paths = ["lib"]
29
29
  spec.required_ruby_version = '>=2.0'
30
30
 
31
- spec.add_dependency 'fit4ruby', '~> 2.0.0'
32
- spec.add_dependency 'perobs', '~> 4.0.0'
31
+ spec.add_dependency 'fit4ruby', '~> 3.2.0'
32
+ spec.add_dependency 'perobs', '~> 4.1.0'
33
33
  spec.add_dependency 'nokogiri', '~> 1.6'
34
34
 
35
35
  spec.add_development_dependency 'bundler', '~> 1.6'
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.10.1
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Schlaeger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-17 00:00:00.000000000 Z
11
+ date: 2019-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fit4ruby
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.0
19
+ version: 3.2.0
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: 2.0.0
26
+ version: 3.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: perobs
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 4.0.0
33
+ version: 4.1.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 4.0.0
40
+ version: 4.1.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: nokogiri
43
43
  requirement: !ruby/object:Gem::Requirement