git-fit 0.10.4 → 0.10.6
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 +9 -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 +243 -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/import/local_file.rb +3 -2
- data/lib/git_fit/std/resolver.rb +32 -0
- data/lib/git_fit/sync/base.rb +11 -0
- data/lib/git_fit/sync/igpsport.rb +1 -1
- 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
|
@@ -124,8 +124,9 @@ module GitFit
|
|
|
124
124
|
h[:heart_rate] = pt[4] if pt[4]
|
|
125
125
|
h
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
natural_id = activity[:run_id].sub("#{source}_", '')
|
|
128
|
+
std_tmp = File.join(std_source_dir, ".#{natural_id}.json.tmp")
|
|
129
|
+
std_final = File.join(std_source_dir, "#{natural_id}.json")
|
|
129
130
|
File.write(std_tmp, JSON.generate(std_data))
|
|
130
131
|
File.rename(std_tmp, std_final)
|
|
131
132
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GitFit
|
|
4
|
+
module Std
|
|
5
|
+
module Resolver
|
|
6
|
+
# garmin_cn 必须排在 garmin 前面:"garmin_cn_7".start_with?("garmin_") == true
|
|
7
|
+
SOURCE_PREFIXES = %w[
|
|
8
|
+
garmin_cn garmin strava keep igpsport xoss xingzhe
|
|
9
|
+
apple_health gpx fit tcx
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
# 从 run_id 解构出 [source, natural_id]
|
|
13
|
+
# natural_id = platform_id(sync)或内容哈希(local)
|
|
14
|
+
def self.decompose(run_id)
|
|
15
|
+
SOURCE_PREFIXES.each do |s|
|
|
16
|
+
prefix = "#{s}_"
|
|
17
|
+
return [s, run_id.sub(prefix, '')] if run_id.start_with?(prefix)
|
|
18
|
+
end
|
|
19
|
+
[nil, nil]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [String, nil] std 文件路径(相对 cwd),找不到返回 nil
|
|
23
|
+
def self.find(run_id)
|
|
24
|
+
source, natural_id = decompose(run_id)
|
|
25
|
+
return nil unless source
|
|
26
|
+
|
|
27
|
+
path = File.join('data', 'std', source, "#{natural_id}.json")
|
|
28
|
+
path if File.exist?(path)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
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/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.6
|
|
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
|
|
@@ -265,6 +288,7 @@ files:
|
|
|
265
288
|
- lib/git_fit/source/keep.rb
|
|
266
289
|
- lib/git_fit/source/tally.rb
|
|
267
290
|
- lib/git_fit/sport_mapper.rb
|
|
291
|
+
- lib/git_fit/std/resolver.rb
|
|
268
292
|
- lib/git_fit/strava_web/file_check.rb
|
|
269
293
|
- lib/git_fit/sync/base.rb
|
|
270
294
|
- lib/git_fit/sync/garmin.rb
|