git-fit 0.9.5 → 0.9.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/config_template.rb +1 -1
- data/lib/git_fit/sync/base.rb +8 -0
- data/lib/git_fit/sync/keep.rb +40 -3
- data/lib/git_fit/sync/runner.rb +103 -51
- data/lib/git_fit/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca129e8a18a66ca1cf48deaaffac3c5a7b84b752737bb52e96906a207be7eb19
|
|
4
|
+
data.tar.gz: aba8f6fd23d5d6cc89b174b0de160a590dd9a74c20836e4986303e9d2ab5be2b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 848a12e3fe926fe889c3672d75b0929756ee036622d6c09db8306f6b7590eb63298243d3d90733dcff18b253a285120cf26896546f000f05d7dbe57bc8b28c65
|
|
7
|
+
data.tar.gz: bbc19cdda1ceee49217d287cc07073cdbee17ecdf6a41774963670dbf377e2933250ef23a21fdfc9beb75624b86212289b6424408f40409d202c0a0873dc62c7
|
|
@@ -10,7 +10,7 @@ module GitFit
|
|
|
10
10
|
|
|
11
11
|
sync:
|
|
12
12
|
# workers: 3 # env: GIT_FIT_SYNC_WORKERS
|
|
13
|
-
# time_budget:
|
|
13
|
+
# time_budget: 90 # env: GIT_FIT_SYNC_TIME_BUDGET (seconds)
|
|
14
14
|
# total_timeout: 600 # env: GIT_FIT_SYNC_TOTAL_TIMEOUT (seconds)
|
|
15
15
|
|
|
16
16
|
# strava: # env: GIT_FIT_STRAVA_CLIENT_ID
|
data/lib/git_fit/sync/base.rb
CHANGED
data/lib/git_fit/sync/keep.rb
CHANGED
|
@@ -53,9 +53,14 @@ module GitFit
|
|
|
53
53
|
'Content-Type' => 'application/x-www-form-urlencoded;charset=utf-8',
|
|
54
54
|
}
|
|
55
55
|
@session = nil
|
|
56
|
+
@cached_pending_ids = nil
|
|
57
|
+
@last_http_code = nil
|
|
58
|
+
@token_issued_at = nil
|
|
59
|
+
@requests_since_login = 0
|
|
56
60
|
end
|
|
57
61
|
|
|
58
62
|
def before_call
|
|
63
|
+
@cached_pending_ids = nil
|
|
59
64
|
return false if @phone.nil? || @password.nil?
|
|
60
65
|
super
|
|
61
66
|
end
|
|
@@ -67,6 +72,7 @@ module GitFit
|
|
|
67
72
|
end
|
|
68
73
|
|
|
69
74
|
def pending_ids
|
|
75
|
+
return @cached_pending_ids if @cached_pending_ids
|
|
70
76
|
existing = existing_run_ids
|
|
71
77
|
ids = []
|
|
72
78
|
KEEP_TYPES.each do |sport|
|
|
@@ -80,6 +86,7 @@ module GitFit
|
|
|
80
86
|
ids << { id: cid, keep_id: keep_id, sport: sport, has_raw: has_raw }
|
|
81
87
|
end
|
|
82
88
|
end
|
|
89
|
+
@cached_pending_ids = ids
|
|
83
90
|
ids
|
|
84
91
|
end
|
|
85
92
|
|
|
@@ -91,6 +98,14 @@ module GitFit
|
|
|
91
98
|
end
|
|
92
99
|
end
|
|
93
100
|
|
|
101
|
+
def auth_error?
|
|
102
|
+
[401, 403].include?(@last_http_code)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def last_auth_code
|
|
106
|
+
@last_http_code
|
|
107
|
+
end
|
|
108
|
+
|
|
94
109
|
private
|
|
95
110
|
|
|
96
111
|
def http_post(url, body, headers)
|
|
@@ -102,7 +117,9 @@ module GitFit
|
|
|
102
117
|
http.use_ssl = uri.scheme == 'https'
|
|
103
118
|
http.open_timeout = 30
|
|
104
119
|
http.read_timeout = 60
|
|
105
|
-
http.start { |h| h.request(req) }
|
|
120
|
+
resp = http.start { |h| h.request(req) }
|
|
121
|
+
@last_http_code = resp.code.to_i
|
|
122
|
+
resp
|
|
106
123
|
end
|
|
107
124
|
|
|
108
125
|
def http_get(url, headers)
|
|
@@ -113,7 +130,10 @@ module GitFit
|
|
|
113
130
|
http.use_ssl = uri.scheme == 'https'
|
|
114
131
|
http.open_timeout = 30
|
|
115
132
|
http.read_timeout = 60
|
|
116
|
-
http.start { |h| h.request(req) }
|
|
133
|
+
resp = http.start { |h| h.request(req) }
|
|
134
|
+
@last_http_code = resp.code.to_i
|
|
135
|
+
@requests_since_login += 1
|
|
136
|
+
resp
|
|
117
137
|
end
|
|
118
138
|
|
|
119
139
|
def login
|
|
@@ -126,6 +146,8 @@ module GitFit
|
|
|
126
146
|
|
|
127
147
|
@headers['Authorization'] = "Bearer #{token}"
|
|
128
148
|
@session = true
|
|
149
|
+
@token_issued_at = Time.now
|
|
150
|
+
@requests_since_login = 0
|
|
129
151
|
rescue JSON::ParserError
|
|
130
152
|
nil
|
|
131
153
|
end
|
|
@@ -147,7 +169,12 @@ module GitFit
|
|
|
147
169
|
loop do
|
|
148
170
|
url = STATS_URL % { sport: sport, last_date: last_date }
|
|
149
171
|
resp = @session ? http_get(url, @headers) : nil
|
|
150
|
-
break unless resp
|
|
172
|
+
break unless resp
|
|
173
|
+
if [401, 403].include?(resp.code.to_i)
|
|
174
|
+
report_token_expiry
|
|
175
|
+
break
|
|
176
|
+
end
|
|
177
|
+
break unless resp.code.to_i == 200
|
|
151
178
|
|
|
152
179
|
data = JSON.parse(resp.body)['data']
|
|
153
180
|
break unless data
|
|
@@ -175,6 +202,10 @@ module GitFit
|
|
|
175
202
|
def sync_single_run_with_raw(compound_id, sport)
|
|
176
203
|
url = LOG_URL % { sport: sport, run_id: compound_id }
|
|
177
204
|
resp = http_get(url, @headers)
|
|
205
|
+
if [401, 403].include?(resp.code.to_i)
|
|
206
|
+
report_token_expiry
|
|
207
|
+
return false
|
|
208
|
+
end
|
|
178
209
|
return false unless resp.code.to_i == 200
|
|
179
210
|
|
|
180
211
|
data = JSON.parse(resp.body)
|
|
@@ -213,6 +244,7 @@ module GitFit
|
|
|
213
244
|
end
|
|
214
245
|
|
|
215
246
|
def upsert_from_raw(compound_id)
|
|
247
|
+
@last_http_code = nil
|
|
216
248
|
keep_id = extract_id(compound_id) || compound_id
|
|
217
249
|
raw = JSON.parse(File.read(raw_path(keep_id)))
|
|
218
250
|
run_data = raw['meta'].merge('id' => compound_id)
|
|
@@ -497,6 +529,11 @@ module GitFit
|
|
|
497
529
|
def spider_sleep
|
|
498
530
|
sleep 0.1
|
|
499
531
|
end
|
|
532
|
+
|
|
533
|
+
def report_token_expiry
|
|
534
|
+
lifetime = @token_issued_at ? (Time.now - @token_issued_at).round(1) : nil
|
|
535
|
+
warn "Keep: token expired after #{@requests_since_login} requests#{lifetime ? " (#{lifetime}s)" : ''}"
|
|
536
|
+
end
|
|
500
537
|
end
|
|
501
538
|
end
|
|
502
539
|
end
|
data/lib/git_fit/sync/runner.rb
CHANGED
|
@@ -6,7 +6,7 @@ module GitFit
|
|
|
6
6
|
module Sync
|
|
7
7
|
class Runner
|
|
8
8
|
POISON = Object.new.freeze
|
|
9
|
-
BATCH_SIZE =
|
|
9
|
+
BATCH_SIZE = 10
|
|
10
10
|
|
|
11
11
|
def initialize(sources:, db:, config:, activity_filter: nil,
|
|
12
12
|
privacy: nil, num_workers: 3, time_budget: nil, total_timeout: nil)
|
|
@@ -15,7 +15,7 @@ module GitFit
|
|
|
15
15
|
@config = config
|
|
16
16
|
@activity_filter = activity_filter
|
|
17
17
|
@privacy = privacy
|
|
18
|
-
@num_workers = [
|
|
18
|
+
@num_workers = [num_workers.to_i, 1].max
|
|
19
19
|
@time_budget = time_budget.is_a?(Numeric) ? time_budget.to_f : nil
|
|
20
20
|
@total_timeout = total_timeout.is_a?(Numeric) ? total_timeout.to_f : nil
|
|
21
21
|
@sync_start = Time.now
|
|
@@ -83,36 +83,43 @@ module GitFit
|
|
|
83
83
|
return false
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
86
|
+
unless @adapters[source]
|
|
87
|
+
output_queue << { type: :banner, source:, text: 'authenticating' }
|
|
88
|
+
adapter = klass.new(config: cfg, db: @db, activity_filter: @activity_filter, privacy: @privacy,
|
|
89
|
+
time_budget: @time_budget)
|
|
90
|
+
unless adapter.before_call
|
|
91
|
+
output_queue << { type: :banner, source:, text: 'before_call failed' }
|
|
92
|
+
clear_source(source)
|
|
93
|
+
return false
|
|
94
|
+
end
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
unless adapter.authenticate
|
|
97
|
+
output_queue << { type: :banner, source:, text: 'auth failed' }
|
|
98
|
+
clear_source(source)
|
|
99
|
+
return false
|
|
100
|
+
end
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
method = adapter.auth_method || 'unknown'
|
|
103
|
+
output_queue << { type: :banner, source:, text: "authenticated (#{method})" }
|
|
104
|
+
@adapters[source] = adapter
|
|
105
|
+
|
|
106
|
+
output_queue << { type: :banner, source:, text: 'fetching activity list' }
|
|
107
|
+
ids = adapter.pending_ids
|
|
108
|
+
if adapter.auth_error?
|
|
109
|
+
output_queue << { type: :banner, source:, text: "auth failed (#{adapter.last_auth_code})" }
|
|
110
|
+
clear_source(source)
|
|
111
|
+
return false
|
|
112
|
+
end
|
|
113
|
+
if ids.nil? || ids.empty?
|
|
114
|
+
output_queue << { type: :banner, source:, text: 'no new activities' }
|
|
115
|
+
clear_source(source)
|
|
116
|
+
return false
|
|
117
|
+
end
|
|
118
|
+
@pending_ids[source] = ids
|
|
119
|
+
output_queue << { type: :banner, source:, text: "#{ids.size} pending" }
|
|
109
120
|
end
|
|
110
121
|
|
|
111
|
-
@adapters[source] = adapter
|
|
112
|
-
@pending_ids[source] = ids
|
|
113
|
-
@source_start_times[source] = Time.now
|
|
114
122
|
@source_activated[source] = true
|
|
115
|
-
output_queue << { type: :banner, source:, text: "#{ids.size} pending" }
|
|
116
123
|
true
|
|
117
124
|
end
|
|
118
125
|
|
|
@@ -149,6 +156,7 @@ module GitFit
|
|
|
149
156
|
end
|
|
150
157
|
|
|
151
158
|
def process_source(source)
|
|
159
|
+
@stats_lock.synchronize { @source_start_times[source] ||= Time.now }
|
|
152
160
|
unless init_source(source)
|
|
153
161
|
finish_activity(source)
|
|
154
162
|
return
|
|
@@ -157,6 +165,7 @@ module GitFit
|
|
|
157
165
|
total = @pending_ids[source].size
|
|
158
166
|
processed = 0
|
|
159
167
|
timed_out_local = false
|
|
168
|
+
auth_failed_local = false
|
|
160
169
|
|
|
161
170
|
loop do
|
|
162
171
|
activity = nil
|
|
@@ -187,33 +196,73 @@ module GitFit
|
|
|
187
196
|
begin
|
|
188
197
|
result = @adapters[source].process_one(activity)
|
|
189
198
|
processed += 1
|
|
190
|
-
|
|
191
|
-
info = @adapters[source].build_progress_info(result)
|
|
192
|
-
@output_queue << {
|
|
193
|
-
type: :progress, source:, id: activity[:id],
|
|
194
|
-
info: info, done: processed, total: total
|
|
195
|
-
}
|
|
196
|
-
else
|
|
197
|
-
@output_queue << {
|
|
198
|
-
type: :progress, source:, id: activity[:id],
|
|
199
|
-
info: nil, done: processed, total: total
|
|
200
|
-
}
|
|
201
|
-
end
|
|
199
|
+
outcome = handle_result(source, activity, result, processed, total)
|
|
202
200
|
rescue StandardError => e
|
|
203
201
|
@output_queue << { type: :error, source:, message: e.message }
|
|
202
|
+
outcome = :ok
|
|
204
203
|
end
|
|
205
204
|
|
|
205
|
+
if outcome == :auth_failed
|
|
206
|
+
auth_failed_local = true
|
|
207
|
+
break
|
|
208
|
+
end
|
|
206
209
|
break if processed >= BATCH_SIZE
|
|
207
210
|
end
|
|
208
211
|
|
|
209
|
-
|
|
212
|
+
if auth_failed_local
|
|
213
|
+
finish_activity(source, true, 're-auth failed')
|
|
214
|
+
else
|
|
215
|
+
finish_activity(source, timed_out_local)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def handle_result(source, activity, result, processed, total)
|
|
220
|
+
if result
|
|
221
|
+
report_activity_progress(source, activity, result, processed, total)
|
|
222
|
+
:ok
|
|
223
|
+
elsif @adapters[source].auth_error?
|
|
224
|
+
code = @adapters[source].last_auth_code
|
|
225
|
+
@output_queue << { type: :banner, source:, text: "auth error (#{code}), re-authenticating" }
|
|
226
|
+
if @adapters[source].authenticate
|
|
227
|
+
result = @adapters[source].process_one(activity)
|
|
228
|
+
if result
|
|
229
|
+
report_activity_progress(source, activity, result, processed, total)
|
|
230
|
+
elsif @adapters[source].auth_error?
|
|
231
|
+
still_code = @adapters[source].last_auth_code
|
|
232
|
+
msg = "#{activity[:id]} still #{still_code} after re-auth"
|
|
233
|
+
@output_queue << { type: :error, source:, message: msg }
|
|
234
|
+
report_activity_progress(source, activity, nil, processed, total)
|
|
235
|
+
else
|
|
236
|
+
report_activity_progress(source, activity, nil, processed, total)
|
|
237
|
+
end
|
|
238
|
+
:ok
|
|
239
|
+
else
|
|
240
|
+
:auth_failed
|
|
241
|
+
end
|
|
242
|
+
else
|
|
243
|
+
report_activity_progress(source, activity, nil, processed, total)
|
|
244
|
+
:ok
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def report_activity_progress(source, activity, result, processed, total)
|
|
249
|
+
info = result ? @adapters[source].build_progress_info(result) : nil
|
|
250
|
+
@output_queue << {
|
|
251
|
+
type: :progress, source:, id: activity[:id],
|
|
252
|
+
info:, done: processed, total:
|
|
253
|
+
}
|
|
210
254
|
end
|
|
211
255
|
|
|
212
|
-
def finish_activity(source, timed_out = false)
|
|
256
|
+
def finish_activity(source, timed_out = false, timeout_note = nil)
|
|
213
257
|
@stats_lock.synchronize do
|
|
214
258
|
if timed_out
|
|
215
259
|
remaining = @pending_ids[source]&.size.to_i
|
|
216
|
-
|
|
260
|
+
msg = if timeout_note
|
|
261
|
+
{ type: :banner, source:, text: "#{timeout_note} — #{remaining} remaining, ending source" }
|
|
262
|
+
else
|
|
263
|
+
{ type: :timeout, source:, remaining: }
|
|
264
|
+
end
|
|
265
|
+
@output_queue << msg
|
|
217
266
|
@pending_ids[source]&.clear
|
|
218
267
|
end
|
|
219
268
|
|
|
@@ -277,11 +326,12 @@ module GitFit
|
|
|
277
326
|
ps[:done] = msg[:done].nil? ? ps.fetch(:done, 0) + 1 : msg[:done]
|
|
278
327
|
total = msg[:total] || ps[:total]
|
|
279
328
|
next unless show_progress?(msg[:done], total)
|
|
280
|
-
line =
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
329
|
+
line = " [#{msg[:done]}/#{total}] #{msg[:id]}"
|
|
330
|
+
line += if msg[:info]
|
|
331
|
+
" | #{msg[:info]}"
|
|
332
|
+
else
|
|
333
|
+
' | (skipped)'
|
|
334
|
+
end
|
|
285
335
|
$stdout.puts " #{s.rjust(10)} |#{line}"
|
|
286
336
|
when :error
|
|
287
337
|
ps[:errors] = (ps[:errors] || 0) + 1
|
|
@@ -297,13 +347,15 @@ module GitFit
|
|
|
297
347
|
@sources.each do |s|
|
|
298
348
|
info = per_source[s] || {}
|
|
299
349
|
err = info[:errors] || 0
|
|
300
|
-
if info[:
|
|
301
|
-
$stdout.puts " done: #{s}: 0 synced (auth failed)"
|
|
302
|
-
elsif info[:up_to_date]
|
|
350
|
+
if info[:up_to_date]
|
|
303
351
|
$stdout.puts " done: #{s}: up to date"
|
|
304
352
|
elsif (cnt = info[:done]) && cnt > 0
|
|
305
|
-
label =
|
|
353
|
+
label = "#{cnt} synced"
|
|
354
|
+
label += ", #{err} errors" if err > 0
|
|
355
|
+
label += " (auth failed)" if info[:auth_failed]
|
|
306
356
|
$stdout.puts " done: #{s}: #{label}"
|
|
357
|
+
elsif info[:auth_failed]
|
|
358
|
+
$stdout.puts " done: #{s}: 0 synced (auth failed)"
|
|
307
359
|
elsif info.key?(:done)
|
|
308
360
|
$stdout.puts " done: #{s}: 0 synced"
|
|
309
361
|
else
|
data/lib/git_fit/version.rb
CHANGED