git-fit 0.17.0 → 0.17.2

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
  SHA256:
3
- metadata.gz: fb01a16a1c3ffd8bc69b1c7941dcbd5d08e472d4e726ffac27f4147ed268a5de
4
- data.tar.gz: e32f30371d9e2c9e9879e78e749fb649372c8a0cd644dbdb70042139e60dabd6
3
+ metadata.gz: 472d41138e7c8607cbc63dbff14db13079b6b824c342ec03661696c77f08f8f9
4
+ data.tar.gz: 291394e38307334eaedd49106b5c6c057331d972ccf4fa2619ce68904ca89a86
5
5
  SHA512:
6
- metadata.gz: 854594f865e691b539e5d487278afab1bd0e26d8b25462b19c458950e59acdae38b8bd9b4d27f48dcb2ac666e55e7666e8b22ee3ad4a6d97f3f19caee72d0387
7
- data.tar.gz: 0af21c0ba3536d6f48bd7b35099aaff5599afc1a692245b2587ef74e03b2380b082bf177b9efecaaa6ba23492a695a8332abb7377825fda812953be1f47fb697
6
+ metadata.gz: 2f7e4971cc16db4f139ae122a65fe19842bfd1e999362bfc57294e4fa2b41a3adbd228824e9126b13a13fc2499643dec07ad195e1d6050618137a54f12b34a48
7
+ data.tar.gz: 713ff0444661494cb22469f5f3535de7b20f89229ec82b5db07398654b6cd2622267db18a0d1841517f796a351bbc1d29800d79b53469009454532dba855b61e
@@ -20,18 +20,18 @@ module GitFit
20
20
  desc 'activity RUN_ID', 'Delete activity by run_id (e.g. keep_9223370467120855807)'
21
21
  option :dry_run, type: :boolean, default: false, desc: 'Preview without deleting'
22
22
  option :checkpoint, type: :boolean, default: true, desc: 'Run WAL checkpoint after delete'
23
+ option :delete_raw, type: :boolean, default: false, desc: 'Also delete raw source file(s)'
24
+ option :delete_std, type: :boolean, default: false, desc: 'Also delete standardized JSON file'
23
25
 
24
26
  def activity(run_id)
25
27
  db = connect_db
26
28
 
27
29
  record = db[:activities].where(run_id: run_id).first
28
30
 
29
- unless record
30
- raise RecordNotFound, "Not found: #{run_id}"
31
- end
31
+ raise RecordNotFound, "Not found: #{run_id}" unless record
32
32
 
33
33
  if options[:dry_run]
34
- say_status :dry_run, "Would delete activity:", :yellow
34
+ say_status :dry_run, 'Would delete activity:', :yellow
35
35
  say " run_id: #{record[:run_id]}"
36
36
  say " name: #{record[:name]}"
37
37
  say " source: #{record[:source]}"
@@ -39,6 +39,8 @@ module GitFit
39
39
  say " start_date: #{record[:start_date]}"
40
40
  say " distance: #{record[:distance]}m" if record[:distance]
41
41
  say " moving_time: #{record[:moving_time]}s" if record[:moving_time]
42
+ list_raw_files(run_id) if options[:delete_raw]
43
+ list_std_file(run_id) if options[:delete_std]
42
44
  return
43
45
  end
44
46
 
@@ -47,6 +49,9 @@ module GitFit
47
49
 
48
50
  say_status :deleted, "Removed #{deleted} activity: #{run_id}", :green
49
51
 
52
+ delete_raw_files(run_id) if options[:delete_raw]
53
+ delete_std_file(run_id) if options[:delete_std]
54
+
50
55
  checkpoint_db(db) if options[:checkpoint]
51
56
  rescue RecordNotFound
52
57
  raise
@@ -61,5 +66,53 @@ module GitFit
61
66
  conn.migrate!
62
67
  conn.db
63
68
  end
69
+
70
+ def delete_raw_files(run_id)
71
+ source, natural_id = GitFit::Std::Resolver.decompose(run_id)
72
+ return unless source && natural_id
73
+
74
+ raw_base = File.join('data', 'raw', source, natural_id)
75
+ files = Dir.glob("#{raw_base}.*")
76
+ if files.any?
77
+ files.each { |f| File.delete(f) }
78
+ elsif File.directory?(raw_base)
79
+ FileUtils.rm_rf(raw_base)
80
+ end
81
+ end
82
+
83
+ def list_raw_files(run_id)
84
+ source, natural_id = GitFit::Std::Resolver.decompose(run_id)
85
+ return unless source && natural_id
86
+
87
+ raw_base = File.join('data', 'raw', source, natural_id)
88
+ files = Dir.glob("#{raw_base}.*")
89
+ if files.any?
90
+ files.each { |f| say "Would delete: #{f}" }
91
+ elsif File.directory?(raw_base)
92
+ say "Would delete: #{raw_base}/ (directory)"
93
+ else
94
+ say "No raw files found for #{run_id}"
95
+ end
96
+ end
97
+
98
+ def delete_std_file(run_id)
99
+ source, natural_id = GitFit::Std::Resolver.decompose(run_id)
100
+ return unless source && natural_id
101
+
102
+ std_path = File.join('data', 'std', source, "#{natural_id}.json")
103
+ File.delete(std_path) if File.exist?(std_path)
104
+ end
105
+
106
+ def list_std_file(run_id)
107
+ source, natural_id = GitFit::Std::Resolver.decompose(run_id)
108
+ return unless source && natural_id
109
+
110
+ std_path = File.join('data', 'std', source, "#{natural_id}.json")
111
+ if File.exist?(std_path)
112
+ say "Would delete: #{std_path}"
113
+ else
114
+ say "No std file found for #{run_id}"
115
+ end
116
+ end
64
117
  end
65
118
  end
@@ -100,46 +100,41 @@ module GitFit
100
100
  max_power_peak: 0.0, total_max_power: 0.0, max_power_count: 0 }
101
101
  end
102
102
 
103
- rows.each do |r|
104
- next unless r[:start_date_local] || r[:start_date]
103
+ rows.each { |row| accumulate_monthly_row(monthly, row) }
105
104
 
106
- ym = (r[:start_date_local] || r[:start_date])[0..6]
107
- d = r[:distance] || 0
108
- t = r[:moving_time] || 0
109
- hr = r[:average_heartrate]
110
- e = r[:elevation_gain] || 0
111
- l = r[:elevation_loss] || 0
112
- s = r[:steps] || 0
105
+ format_monthly(monthly.sort)
106
+ end
113
107
 
114
- monthly[ym][:count] += 1
115
- monthly[ym][:distance_km] += d / 1000.0
116
- monthly[ym][:moving_time_h] += t / 3600.0
117
- monthly[ym][:total_elev] += e
118
- monthly[ym][:total_loss] += l
119
- monthly[ym][:total_steps] += s
120
- if hr && hr > 0
121
- monthly[ym][:total_hr] += hr
122
- monthly[ym][:hr_count] += 1
123
- end
124
- cad = r[:average_cadence]
125
- pow = r[:average_power]
126
- if cad && cad > 0
127
- monthly[ym][:total_cadence] += cad
128
- monthly[ym][:cadence_count] += 1
129
- end
130
- if pow && pow > 0
131
- monthly[ym][:total_power] += pow
132
- monthly[ym][:power_count] += 1
133
- end
134
- max_pow = r[:max_power]
135
- if max_pow && max_pow > 0
136
- monthly[ym][:max_power_peak] = max_pow if max_pow > monthly[ym][:max_power_peak]
137
- monthly[ym][:total_max_power] += max_pow
138
- monthly[ym][:max_power_count] += 1
139
- end
140
- end
108
+ def accumulate_monthly_row(monthly, row)
109
+ return unless row[:start_date_local] || row[:start_date]
110
+
111
+ ym = (row[:start_date_local] || row[:start_date])[0..6]
112
+ acc = monthly[ym]
113
+ acc[:count] += 1
114
+ acc[:distance_km] += (row[:distance] || 0) / 1000.0
115
+ acc[:moving_time_h] += (row[:moving_time] || 0) / 3600.0
116
+ acc[:total_elev] += row[:elevation_gain] || 0
117
+ acc[:total_loss] += row[:elevation_loss] || 0
118
+ acc[:total_steps] += row[:steps] || 0
119
+ add_if_positive(acc, :total_hr, :hr_count, row[:average_heartrate])
120
+ add_if_positive(acc, :total_cadence, :cadence_count, row[:average_cadence])
121
+ add_if_positive(acc, :total_power, :power_count, row[:average_power])
122
+ max_pow = row[:max_power]
123
+ return unless max_pow && max_pow > 0
124
+ acc[:max_power_peak] = max_pow if max_pow > acc[:max_power_peak]
125
+ acc[:total_max_power] += max_pow
126
+ acc[:max_power_count] += 1
127
+ end
128
+
129
+ def add_if_positive(acc, sum_key, count_key, value)
130
+ return unless value && value > 0
131
+
132
+ acc[sum_key] += value
133
+ acc[count_key] += 1
134
+ end
141
135
 
142
- monthly.sort.map do |ym, v|
136
+ def format_monthly(sorted)
137
+ sorted.map do |ym, v|
143
138
  pace = if v[:moving_time_h] > 0 && v[:distance_km] > 0
144
139
  format_pace(v[:moving_time_h] * 3600 / v[:distance_km])
145
140
  end
@@ -39,6 +39,15 @@ module GitFit
39
39
  nil
40
40
  end
41
41
 
42
+ def decode_step_data(step_data)
43
+ return nil unless step_data
44
+ raw = Base64.decode64(step_data)
45
+ decompressed = zlib_inflate(raw)
46
+ JSON.parse(decompressed)
47
+ rescue StandardError
48
+ nil
49
+ end
50
+
42
51
  def aes_decrypt(data)
43
52
  cipher = OpenSSL::Cipher.new('AES-128-CBC')
44
53
  cipher.decrypt
@@ -14,6 +14,8 @@ module GitFit
14
14
  KEEP_TYPES = %w[running hiking cycling].freeze
15
15
  KEEP_RAW_EXT = 'json'
16
16
 
17
+ STEP_ENCODED_FIELDS = %w[stepPoints stepFrequencies fullStepPoints].freeze
18
+
17
19
  TYPE_MAP = {
18
20
  'outdoorWalking' => 'walk',
19
21
  'outdoorRunning' => 'run',
@@ -208,9 +210,16 @@ module GitFit
208
210
  raw = run_data.dup
209
211
  if raw['heartRate'].is_a?(Hash)
210
212
  raw['heartRate'] = raw['heartRate'].dup
211
- raw['heartRate'].delete('heartRates')
213
+ raw['heartRate']['heartRates'] = nil if raw['heartRate'].key?('heartRates')
214
+ end
215
+ raw['geoPoints'] = nil if raw.key?('geoPoints')
216
+
217
+ decoded_steps = {}
218
+ STEP_ENCODED_FIELDS.each do |field|
219
+ next unless raw.key?(field)
220
+ decoded_steps[field] = Source::Keep.decode_step_data(raw[field])
221
+ raw[field] = nil
212
222
  end
213
- raw.delete('geoPoints')
214
223
 
215
224
  write_source_archive({
216
225
  archiveVersion: 0,
@@ -219,7 +228,7 @@ module GitFit
219
228
  raw: raw,
220
229
  geoPoints: payload[:geo_points],
221
230
  heartRates: payload[:hr_data],
222
- }, keep_id)
231
+ }.merge(decoded_steps), keep_id)
223
232
 
224
233
  wgs_points = Source::Keep.compute_wgs_points(payload, run_data)
225
234
  write_standardized_json(wgs_points, payload, run_data, keep_id)
@@ -73,7 +73,9 @@ module GitFit
73
73
  next if skip_type?(type)
74
74
  seen[act_id] = seen.fetch(act_id, 0) + 1
75
75
  if seen[act_id] > 3
76
- raise SyncError, "XOSS: activity list returned #{seen[act_id]} duplicate records for id #{act_id} — server-side degradation. Run: git fit sync --source xoss"
76
+ raise SyncError,
77
+ "XOSS: activity list returned #{seen[act_id]} duplicate records for id #{act_id} " \
78
+ '— server-side degradation. Run: git fit sync --source xoss'
77
79
  end
78
80
  next if seen[act_id] > 1
79
81
  ids << { id: act_id, type: type, summary: act }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GitFit
4
- VERSION = '0.17.0'
4
+ VERSION = '0.17.2'
5
5
  end
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.17.0
4
+ version: 0.17.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lax