git-fit 0.10.3 → 0.10.5
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 +4 -4
- data/lib/git-fit.rb +8 -0
- data/lib/git_fit/auth/garmin_token.rb +32 -0
- data/lib/git_fit/auth/strava.rb +223 -0
- data/lib/git_fit/cli/export.rb +20 -1
- data/lib/git_fit/cli/sync.rb +54 -0
- data/lib/git_fit/cli.rb +29 -0
- data/lib/git_fit/config_template.rb +9 -0
- data/lib/git_fit/dedup/field_elector.rb +81 -0
- data/lib/git_fit/dedup/group_consolidator.rb +58 -0
- data/lib/git_fit/dedup/log_writer.rb +33 -0
- data/lib/git_fit/dedup/matcher.rb +90 -0
- data/lib/git_fit/dedup/service.rb +207 -0
- data/lib/git_fit/dedup/trajectory.rb +255 -0
- data/lib/git_fit/export/gpx.rb +133 -0
- data/lib/git_fit/export/json.rb +31 -8
- data/lib/git_fit/export.rb +1 -0
- data/lib/git_fit/fit.rb +6 -0
- data/lib/git_fit/source/keep.rb +117 -0
- data/lib/git_fit/source.rb +1 -0
- data/lib/git_fit/sync/base.rb +11 -0
- data/lib/git_fit/sync/igpsport.rb +1 -1
- data/lib/git_fit/sync/keep.rb +4 -106
- data/lib/git_fit/sync/strava.rb +31 -18
- data/lib/git_fit/sync/xoss.rb +1 -1
- data/lib/git_fit/version.rb +1 -1
- metadata +25 -1
data/lib/git_fit/export/json.rb
CHANGED
|
@@ -7,10 +7,11 @@ module GitFit
|
|
|
7
7
|
class JSON
|
|
8
8
|
PLACEHOLDER_POLYLINE = 'gqqrFurkeU??'
|
|
9
9
|
|
|
10
|
-
def initialize(db:, output:, activity_filter: nil)
|
|
10
|
+
def initialize(db:, output:, activity_filter: nil, tracks_output: nil)
|
|
11
11
|
@db = db
|
|
12
12
|
@output = output
|
|
13
13
|
@activity_filter = activity_filter
|
|
14
|
+
@tracks_output = tracks_output
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def call
|
|
@@ -24,23 +25,40 @@ module GitFit
|
|
|
24
25
|
|
|
25
26
|
group_members_cache = {}
|
|
26
27
|
|
|
28
|
+
tracks = @tracks_output ? {} : nil
|
|
29
|
+
|
|
27
30
|
formatted = activities.map do |a|
|
|
28
31
|
gid = run_id_to_group[a[:run_id]]
|
|
29
32
|
if gid
|
|
30
33
|
group_members_cache[gid] ||= @db[:dedup_group_members].where(group_id: gid).all
|
|
31
|
-
composite_activity(a, gid, group_members_cache[gid])
|
|
34
|
+
composite_activity(a, gid, group_members_cache[gid], tracks)
|
|
32
35
|
else
|
|
33
|
-
format_activity(a)
|
|
36
|
+
format_activity(a, tracks)
|
|
34
37
|
end
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
File.write(@output, ::JSON.pretty_generate(formatted))
|
|
41
|
+
File.write(@tracks_output, ::JSON.generate(tracks)) if @tracks_output
|
|
38
42
|
formatted.size
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
private
|
|
42
46
|
|
|
43
|
-
def
|
|
47
|
+
def extract_polyline(a)
|
|
48
|
+
return nil if a[:source] == 'keep' && a[:summary_polyline] == PLACEHOLDER_POLYLINE
|
|
49
|
+
|
|
50
|
+
a[:summary_polyline]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def pick_best_polyline(group_activities)
|
|
54
|
+
candidates = group_activities.filter_map { |a| extract_polyline(a) }
|
|
55
|
+
candidates.max_by { |p| p.to_s.length }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def format_activity(a, tracks = nil)
|
|
59
|
+
polyline = extract_polyline(a)
|
|
60
|
+
tracks[a[:run_id]] = polyline if tracks && polyline
|
|
61
|
+
|
|
44
62
|
base = {
|
|
45
63
|
run_id: a[:run_id],
|
|
46
64
|
name: a[:name],
|
|
@@ -51,8 +69,7 @@ module GitFit
|
|
|
51
69
|
start_date: a[:start_date],
|
|
52
70
|
start_date_local: a[:start_date_local],
|
|
53
71
|
location_country: a[:location_country],
|
|
54
|
-
summary_polyline:
|
|
55
|
-
nil : a[:summary_polyline],
|
|
72
|
+
summary_polyline: tracks ? nil : polyline,
|
|
56
73
|
average_heartrate: a[:average_heartrate] && a[:average_heartrate] != 0 ? a[:average_heartrate] : nil,
|
|
57
74
|
max_heartrate: a[:max_heartrate],
|
|
58
75
|
average_cadence: a[:average_cadence],
|
|
@@ -71,7 +88,7 @@ module GitFit
|
|
|
71
88
|
base
|
|
72
89
|
end
|
|
73
90
|
|
|
74
|
-
def composite_activity(activity, group_id, members)
|
|
91
|
+
def composite_activity(activity, group_id, members, tracks = nil)
|
|
75
92
|
run_ids = members.map { |m| m[:run_id] }
|
|
76
93
|
group_activities = @db[:activities].where(run_id: run_ids).all
|
|
77
94
|
|
|
@@ -85,11 +102,17 @@ module GitFit
|
|
|
85
102
|
end
|
|
86
103
|
result
|
|
87
104
|
else
|
|
88
|
-
members.first&.slice(:run_id, :distance, :moving_time, :elapsed_time,
|
|
105
|
+
composite = members.first&.slice(:run_id, :distance, :moving_time, :elapsed_time,
|
|
89
106
|
:average_heartrate, :max_heartrate, :elevation_gain,
|
|
90
107
|
:average_speed, :average_cadence, :max_cadence) || {}
|
|
108
|
+
composite[:summary_polyline] = pick_best_polyline(group_activities)
|
|
109
|
+
composite
|
|
91
110
|
end
|
|
92
111
|
|
|
112
|
+
polyline = composite[:summary_polyline]
|
|
113
|
+
tracks[activity[:run_id]] = polyline if tracks && polyline
|
|
114
|
+
composite[:summary_polyline] = tracks ? nil : polyline
|
|
115
|
+
|
|
93
116
|
composite[:run_id] = activity[:run_id]
|
|
94
117
|
composite[:source] = activity[:source]
|
|
95
118
|
composite[:sport_type] = activity[:sport_type]
|
data/lib/git_fit/export.rb
CHANGED
data/lib/git_fit/fit.rb
CHANGED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
require 'zlib'
|
|
5
|
+
require 'openssl'
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'time'
|
|
8
|
+
require_relative '../geo/coord_transform'
|
|
9
|
+
|
|
10
|
+
module GitFit
|
|
11
|
+
module Source
|
|
12
|
+
module Keep
|
|
13
|
+
PLACEHOLDER_POLYLINE = 'gqqrFurkeU??'
|
|
14
|
+
|
|
15
|
+
AES_KEY = Base64.decode64('NTZmZTU5OzgyZzpkODczYw==').freeze
|
|
16
|
+
AES_IV = Base64.decode64('MjM0Njg5MjQzMjkyMDMwMA==').freeze
|
|
17
|
+
|
|
18
|
+
TIMESTAMP_THRESHOLD = 3_600_000
|
|
19
|
+
HR_THRESHOLD = 100
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
def decode_geo_points(geo_data)
|
|
24
|
+
return nil unless geo_data
|
|
25
|
+
raw = Base64.decode64(geo_data)
|
|
26
|
+
decrypted = aes_decrypt(raw)
|
|
27
|
+
decompressed = zlib_inflate(decrypted)
|
|
28
|
+
JSON.parse(decompressed)
|
|
29
|
+
rescue StandardError
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def decode_hr_data(hr_data)
|
|
34
|
+
return nil unless hr_data
|
|
35
|
+
raw = Base64.decode64(hr_data)
|
|
36
|
+
decompressed = zlib_inflate(raw)
|
|
37
|
+
JSON.parse(decompressed)
|
|
38
|
+
rescue StandardError
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def aes_decrypt(data)
|
|
43
|
+
cipher = OpenSSL::Cipher.new('AES-128-CBC')
|
|
44
|
+
cipher.decrypt
|
|
45
|
+
cipher.key = AES_KEY
|
|
46
|
+
cipher.iv = AES_IV
|
|
47
|
+
cipher.update(data) + cipher.final
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def zlib_inflate(data)
|
|
51
|
+
[-Zlib::MAX_WBITS, Zlib::MAX_WBITS, Zlib::MAX_WBITS + 16].each do |wbits|
|
|
52
|
+
inflater = Zlib::Inflate.new(wbits)
|
|
53
|
+
result = inflater.inflate(data)
|
|
54
|
+
begin
|
|
55
|
+
inflater.finish
|
|
56
|
+
rescue StandardError
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
inflater.close
|
|
60
|
+
return result
|
|
61
|
+
rescue Zlib::DataError
|
|
62
|
+
next
|
|
63
|
+
end
|
|
64
|
+
raise Zlib::DataError, 'cannot inflate data'
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def compute_wgs_points(payload, run_data)
|
|
68
|
+
geo_points = payload[:geo_points]
|
|
69
|
+
hr_data = payload[:hr_data]
|
|
70
|
+
return [] unless geo_points&.any?
|
|
71
|
+
|
|
72
|
+
start_time = run_data['startTime']
|
|
73
|
+
use_absolute_ts = geo_points.first['timestamp'].to_i > TIMESTAMP_THRESHOLD
|
|
74
|
+
effective_start = use_absolute_ts ? 0 : start_time
|
|
75
|
+
|
|
76
|
+
geo_points.map do |p|
|
|
77
|
+
lat, lng = Geo::CoordTransform.gcj02_to_wgs84_exact(p['latitude'], p['longitude'])
|
|
78
|
+
ts = p['timestamp'] || p['unixTimestamp']
|
|
79
|
+
pt_time = ts ? Time.at(effective_start / 1000.0 + ts.to_i / 10.0).utc : nil
|
|
80
|
+
hr = find_nearest_hr(hr_data, ts, start_time)
|
|
81
|
+
[lat, lng, p['altitude'], pt_time, hr]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def find_nearest_hr(hr_data, geo_ts, start_time, threshold: HR_THRESHOLD)
|
|
86
|
+
return nil unless hr_data&.any? && geo_ts
|
|
87
|
+
|
|
88
|
+
normalize = lambda { |ts|
|
|
89
|
+
if ts > TIMESTAMP_THRESHOLD
|
|
90
|
+
ts - start_time / 100
|
|
91
|
+
else
|
|
92
|
+
ts
|
|
93
|
+
end
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
target = normalize.call(geo_ts)
|
|
97
|
+
|
|
98
|
+
nearest = nil
|
|
99
|
+
min_diff = Float::INFINITY
|
|
100
|
+
|
|
101
|
+
hr_data.each do |item|
|
|
102
|
+
ts = item['timestamp']
|
|
103
|
+
next unless ts
|
|
104
|
+
|
|
105
|
+
diff = (normalize.call(ts) - target).abs
|
|
106
|
+
if diff <= threshold && diff < min_diff
|
|
107
|
+
nearest = item
|
|
108
|
+
min_diff = diff
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
hr = nearest&.dig('beatsPerMinute')
|
|
113
|
+
hr&.positive? ? hr : nil
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
data/lib/git_fit/source.rb
CHANGED
data/lib/git_fit/sync/base.rb
CHANGED
|
@@ -235,6 +235,17 @@ module GitFit
|
|
|
235
235
|
.select_map(:run_id)
|
|
236
236
|
.map { |rid| rid.sub(/\A#{Regexp.escape(prefix)}/, '') }
|
|
237
237
|
end
|
|
238
|
+
|
|
239
|
+
def normalize_fit_timestamps(points)
|
|
240
|
+
points.map do |pt|
|
|
241
|
+
ts = pt['timestamp']
|
|
242
|
+
next pt unless ts.is_a?(Numeric)
|
|
243
|
+
|
|
244
|
+
pt = pt.dup
|
|
245
|
+
pt['timestamp'] = Time.at(FIT::FIT_EPOCH + ts).utc.iso8601
|
|
246
|
+
pt
|
|
247
|
+
end
|
|
248
|
+
end
|
|
238
249
|
end
|
|
239
250
|
end
|
|
240
251
|
end
|
|
@@ -140,7 +140,7 @@ module GitFit
|
|
|
140
140
|
FileUtils.mkdir_p(dir)
|
|
141
141
|
tmp = File.join(dir, ".#{ride_id}.json.tmp")
|
|
142
142
|
final = File.join(dir, "#{ride_id}.json")
|
|
143
|
-
File.write(tmp, JSON.generate(wgs_points))
|
|
143
|
+
File.write(tmp, JSON.generate(normalize_fit_timestamps(wgs_points)))
|
|
144
144
|
File.rename(tmp, final)
|
|
145
145
|
end
|
|
146
146
|
|
data/lib/git_fit/sync/keep.rb
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base'
|
|
4
|
-
require 'base64'
|
|
5
|
-
require 'zlib'
|
|
6
|
-
require 'openssl'
|
|
7
4
|
require 'time'
|
|
8
5
|
require 'tzinfo'
|
|
9
6
|
|
|
@@ -14,11 +11,6 @@ module GitFit
|
|
|
14
11
|
STATS_URL = 'https://api.gotokeep.com/pd/v3/stats/detail?dateUnit=all&type=%{sport}&lastDate=%{last_date}'
|
|
15
12
|
LOG_URL = 'https://api.gotokeep.com/pd/v3/%{sport}log/%{run_id}'
|
|
16
13
|
|
|
17
|
-
AES_KEY = Base64.decode64('NTZmZTU5OzgyZzpkODczYw==')
|
|
18
|
-
AES_IV = Base64.decode64('MjM0Njg5MjQzMjkyMDMwMA==')
|
|
19
|
-
|
|
20
|
-
PLACEHOLDER_POLYLINE = 'gqqrFurkeU??'
|
|
21
|
-
|
|
22
14
|
KEEP_TYPES = %w[running hiking cycling].freeze
|
|
23
15
|
KEEP_RAW_EXT = 'json'
|
|
24
16
|
|
|
@@ -38,9 +30,6 @@ module GitFit
|
|
|
38
30
|
'mountaineering' => 'Hiking',
|
|
39
31
|
}.freeze
|
|
40
32
|
|
|
41
|
-
TIMESTAMP_THRESHOLD = 3_600_000
|
|
42
|
-
HR_THRESHOLD = 100
|
|
43
|
-
|
|
44
33
|
register_adapter
|
|
45
34
|
register_config :phone, :password
|
|
46
35
|
|
|
@@ -231,7 +220,7 @@ module GitFit
|
|
|
231
220
|
heartRates: payload[:hr_data],
|
|
232
221
|
}, keep_id)
|
|
233
222
|
|
|
234
|
-
wgs_points = compute_wgs_points(payload, run_data)
|
|
223
|
+
wgs_points = Source::Keep.compute_wgs_points(payload, run_data)
|
|
235
224
|
write_standardized_json(wgs_points, payload, run_data, keep_id)
|
|
236
225
|
|
|
237
226
|
attrs = build_activity_from_wgs(run_data, wgs_points)
|
|
@@ -273,35 +262,17 @@ module GitFit
|
|
|
273
262
|
else
|
|
274
263
|
payload = { geo_points: raw['geoPoints'], hr_data: raw['heartRates'] }
|
|
275
264
|
run_data = raw['meta']
|
|
276
|
-
compute_wgs_points(payload, run_data)
|
|
265
|
+
Source::Keep.compute_wgs_points(payload, run_data)
|
|
277
266
|
end
|
|
278
267
|
end
|
|
279
268
|
|
|
280
269
|
def decrypt_raw_payload(run_data)
|
|
281
270
|
{
|
|
282
|
-
geo_points: decode_geo_points(run_data['geoPoints']),
|
|
283
|
-
hr_data: decode_hr_data(run_data.dig('heartRate', 'heartRates')),
|
|
271
|
+
geo_points: Source::Keep.decode_geo_points(run_data['geoPoints']),
|
|
272
|
+
hr_data: Source::Keep.decode_hr_data(run_data.dig('heartRate', 'heartRates')),
|
|
284
273
|
}
|
|
285
274
|
end
|
|
286
275
|
|
|
287
|
-
def compute_wgs_points(payload, run_data)
|
|
288
|
-
geo_points = payload[:geo_points]
|
|
289
|
-
hr_data = payload[:hr_data]
|
|
290
|
-
return [] unless geo_points&.any?
|
|
291
|
-
|
|
292
|
-
start_time = run_data['startTime']
|
|
293
|
-
use_absolute_ts = geo_points.first['timestamp'].to_i > TIMESTAMP_THRESHOLD
|
|
294
|
-
effective_start = use_absolute_ts ? 0 : start_time
|
|
295
|
-
|
|
296
|
-
geo_points.map do |p|
|
|
297
|
-
lat, lng = Geo::CoordTransform.gcj02_to_wgs84_exact(p['latitude'], p['longitude'])
|
|
298
|
-
ts = p['timestamp'] || p['unixTimestamp']
|
|
299
|
-
pt_time = ts ? Time.at(effective_start / 1000.0 + ts.to_i / 10.0).utc : nil
|
|
300
|
-
hr = find_nearest_hr(hr_data, ts, start_time)
|
|
301
|
-
[lat, lng, p['altitude'], pt_time, hr]
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
|
|
305
276
|
def tz_resolver
|
|
306
277
|
@tz_resolver ||= GitFit::Timezone::Resolver.new({})
|
|
307
278
|
end
|
|
@@ -414,79 +385,6 @@ module GitFit
|
|
|
414
385
|
id_str.split('_')[1]
|
|
415
386
|
end
|
|
416
387
|
|
|
417
|
-
def decode_geo_points(geo_data)
|
|
418
|
-
return nil unless geo_data
|
|
419
|
-
raw = Base64.decode64(geo_data)
|
|
420
|
-
decrypted = aes_decrypt(raw)
|
|
421
|
-
decompressed = zlib_inflate(decrypted)
|
|
422
|
-
JSON.parse(decompressed)
|
|
423
|
-
rescue StandardError
|
|
424
|
-
nil
|
|
425
|
-
end
|
|
426
|
-
|
|
427
|
-
def decode_hr_data(hr_data)
|
|
428
|
-
return nil unless hr_data
|
|
429
|
-
raw = Base64.decode64(hr_data)
|
|
430
|
-
decompressed = zlib_inflate(raw)
|
|
431
|
-
JSON.parse(decompressed)
|
|
432
|
-
rescue StandardError
|
|
433
|
-
nil
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
def aes_decrypt(data)
|
|
437
|
-
cipher = OpenSSL::Cipher.new('AES-128-CBC')
|
|
438
|
-
cipher.decrypt
|
|
439
|
-
cipher.key = AES_KEY
|
|
440
|
-
cipher.iv = AES_IV
|
|
441
|
-
cipher.update(data) + cipher.final
|
|
442
|
-
end
|
|
443
|
-
|
|
444
|
-
def zlib_inflate(data)
|
|
445
|
-
[-Zlib::MAX_WBITS, Zlib::MAX_WBITS, Zlib::MAX_WBITS + 16].each do |wbits|
|
|
446
|
-
begin
|
|
447
|
-
inflater = Zlib::Inflate.new(wbits)
|
|
448
|
-
result = inflater.inflate(data)
|
|
449
|
-
inflater.finish rescue nil
|
|
450
|
-
inflater.close
|
|
451
|
-
return result
|
|
452
|
-
rescue Zlib::DataError
|
|
453
|
-
next
|
|
454
|
-
end
|
|
455
|
-
end
|
|
456
|
-
raise Zlib::DataError, 'cannot inflate data'
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
def find_nearest_hr(hr_data, geo_ts, start_time, threshold: HR_THRESHOLD)
|
|
460
|
-
return nil unless hr_data&.any? && geo_ts
|
|
461
|
-
|
|
462
|
-
normalize = ->(ts) {
|
|
463
|
-
if ts > TIMESTAMP_THRESHOLD
|
|
464
|
-
ts - start_time / 100
|
|
465
|
-
else
|
|
466
|
-
ts
|
|
467
|
-
end
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
target = normalize.call(geo_ts)
|
|
471
|
-
|
|
472
|
-
nearest = nil
|
|
473
|
-
min_diff = Float::INFINITY
|
|
474
|
-
|
|
475
|
-
hr_data.each do |item|
|
|
476
|
-
ts = item['timestamp']
|
|
477
|
-
next unless ts
|
|
478
|
-
|
|
479
|
-
diff = (normalize.call(ts) - target).abs
|
|
480
|
-
if diff <= threshold && diff < min_diff
|
|
481
|
-
nearest = item
|
|
482
|
-
min_diff = diff
|
|
483
|
-
end
|
|
484
|
-
end
|
|
485
|
-
|
|
486
|
-
hr = nearest&.dig('beatsPerMinute')
|
|
487
|
-
hr&.positive? ? hr : nil
|
|
488
|
-
end
|
|
489
|
-
|
|
490
388
|
def compute_avg_hr(points)
|
|
491
389
|
hrs = points.map { |p| p[4] }.compact
|
|
492
390
|
return nil if hrs.empty?
|
data/lib/git_fit/sync/strava.rb
CHANGED
|
@@ -134,27 +134,40 @@ module GitFit
|
|
|
134
134
|
coords = Geo::Polyline.decode(polyline)
|
|
135
135
|
return nil if coords.empty?
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
137
|
+
streams_data = %w[altitude time heartrate cadence watts temp velocity_smooth distance].to_h do |k|
|
|
138
|
+
[k, streams[k]&.dig('data')]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
start_time = begin
|
|
142
|
+
Time.parse(detail['start_date'])
|
|
143
|
+
rescue StandardError
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
145
146
|
|
|
146
147
|
coords.each_with_index.map do |latlng, i|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
pt
|
|
148
|
+
build_l2_point(latlng, i, streams_data, start_time)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def build_l2_point(latlng, i, streams_data, start_time)
|
|
153
|
+
pt = { positionLat: latlng[0], positionLong: latlng[1] }
|
|
154
|
+
pt[:altitude] = stream_at(streams_data, 'altitude', i)
|
|
155
|
+
ts = stream_at(streams_data, 'time', i)
|
|
156
|
+
if ts
|
|
157
|
+
pt[:timestamp] = start_time ? (start_time + ts).utc.iso8601 : ts
|
|
157
158
|
end
|
|
159
|
+
pt[:heartRate] = stream_at(streams_data, 'heartrate', i)
|
|
160
|
+
pt[:cadence] = stream_at(streams_data, 'cadence', i)
|
|
161
|
+
pt[:power] = stream_at(streams_data, 'watts', i)
|
|
162
|
+
pt[:temperature] = stream_at(streams_data, 'temp', i)
|
|
163
|
+
pt[:speed] = stream_at(streams_data, 'velocity_smooth', i)
|
|
164
|
+
pt[:distance] = stream_at(streams_data, 'distance', i)
|
|
165
|
+
pt
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def stream_at(streams_data, key, i)
|
|
169
|
+
data = streams_data[key]
|
|
170
|
+
data && i < data.size ? data[i] : nil
|
|
158
171
|
end
|
|
159
172
|
|
|
160
173
|
def build_attrs(act, category, polyline = nil, external_id: nil)
|
data/lib/git_fit/sync/xoss.rb
CHANGED
|
@@ -348,7 +348,7 @@ module GitFit
|
|
|
348
348
|
FileUtils.mkdir_p(dir)
|
|
349
349
|
tmp = File.join(dir, ".#{act_id}.json.tmp")
|
|
350
350
|
final = File.join(dir, "#{act_id}.json")
|
|
351
|
-
File.write(tmp, JSON.generate(points))
|
|
351
|
+
File.write(tmp, JSON.generate(normalize_fit_timestamps(points)))
|
|
352
352
|
File.rename(tmp, final)
|
|
353
353
|
end
|
|
354
354
|
|
data/lib/git_fit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git-fit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.10.
|
|
4
|
+
version: 0.10.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lax
|
|
@@ -177,6 +177,20 @@ dependencies:
|
|
|
177
177
|
- - "~>"
|
|
178
178
|
- !ruby/object:Gem::Version
|
|
179
179
|
version: '1.0'
|
|
180
|
+
- !ruby/object:Gem::Dependency
|
|
181
|
+
name: webrick
|
|
182
|
+
requirement: !ruby/object:Gem::Requirement
|
|
183
|
+
requirements:
|
|
184
|
+
- - "~>"
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: '1.9'
|
|
187
|
+
type: :runtime
|
|
188
|
+
prerelease: false
|
|
189
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
190
|
+
requirements:
|
|
191
|
+
- - "~>"
|
|
192
|
+
- !ruby/object:Gem::Version
|
|
193
|
+
version: '1.9'
|
|
180
194
|
- !ruby/object:Gem::Dependency
|
|
181
195
|
name: ostruct
|
|
182
196
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -218,6 +232,8 @@ files:
|
|
|
218
232
|
- db/migrations/002_iso8601_time_format.rb
|
|
219
233
|
- exe/git-fit
|
|
220
234
|
- lib/git-fit.rb
|
|
235
|
+
- lib/git_fit/auth/garmin_token.rb
|
|
236
|
+
- lib/git_fit/auth/strava.rb
|
|
221
237
|
- lib/git_fit/cli.rb
|
|
222
238
|
- lib/git_fit/cli/export.rb
|
|
223
239
|
- lib/git_fit/cli/geo_cli.rb
|
|
@@ -232,10 +248,17 @@ files:
|
|
|
232
248
|
- lib/git_fit/db/cli.rb
|
|
233
249
|
- lib/git_fit/db/connection.rb
|
|
234
250
|
- lib/git_fit/db/rebuild.rb
|
|
251
|
+
- lib/git_fit/dedup/field_elector.rb
|
|
252
|
+
- lib/git_fit/dedup/group_consolidator.rb
|
|
253
|
+
- lib/git_fit/dedup/log_writer.rb
|
|
254
|
+
- lib/git_fit/dedup/matcher.rb
|
|
255
|
+
- lib/git_fit/dedup/service.rb
|
|
256
|
+
- lib/git_fit/dedup/trajectory.rb
|
|
235
257
|
- lib/git_fit/export.rb
|
|
236
258
|
- lib/git_fit/export/calculator.rb
|
|
237
259
|
- lib/git_fit/export/csv.rb
|
|
238
260
|
- lib/git_fit/export/defaults.rb
|
|
261
|
+
- lib/git_fit/export/gpx.rb
|
|
239
262
|
- lib/git_fit/export/json.rb
|
|
240
263
|
- lib/git_fit/export/stats.rb
|
|
241
264
|
- lib/git_fit/fit.rb
|
|
@@ -262,6 +285,7 @@ files:
|
|
|
262
285
|
- lib/git_fit/privacy/polyline_filter.rb
|
|
263
286
|
- lib/git_fit/source.rb
|
|
264
287
|
- lib/git_fit/source/base.rb
|
|
288
|
+
- lib/git_fit/source/keep.rb
|
|
265
289
|
- lib/git_fit/source/tally.rb
|
|
266
290
|
- lib/git_fit/sport_mapper.rb
|
|
267
291
|
- lib/git_fit/strava_web/file_check.rb
|