tcxread 0.1.3 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -1
- data/lib/tcxread.rb +67 -54
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0f94a5a07acd01e1c6b0419324bbef97912e0d7e67b3f2b1dc18d50336dd7e2
|
4
|
+
data.tar.gz: 776953d7610ffa6c6903449332eb1bf36ff2a7fb54c5fa10dde8e591ecefd3df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc1240984ccb04288e3790d69529eea52269f16888e1b6d8906017b42252ce98eeef44240b729ed1358444e2f0d3f7ce377f3a49123ea5c85c21493a2438cc77
|
7
|
+
data.tar.gz: 99863f90228c50f2590709105fab6d55395c26462bec7b2ba2b11c429f2e85453867583fdcb964d83599f669bbc1dc991fa6ca8a0c5eff6aba8a3681007d1b48
|
data/README.md
CHANGED
@@ -49,7 +49,11 @@ puts "Distance meters: #{data.total_distance_meters}, " \
|
|
49
49
|
"Average heart rate: #{data.average_heart_rate}, " \
|
50
50
|
"Average watts: #{data.average_watts}, " \
|
51
51
|
"Max watts: #{data.max_watts}, " \
|
52
|
-
"Average
|
52
|
+
"Average speed: #{data.average_speed_all}, " \
|
53
|
+
"Average speed (moving): #{data.average_speed_moving}, " \
|
54
|
+
"Average cadence (moving): #{data.average_cadence_biking}, " \
|
55
|
+
"Average cadence: #{data.average_cadence_all}"
|
56
|
+
|
53
57
|
```
|
54
58
|
|
55
59
|
## 💾 Datasets
|
data/lib/tcxread.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
require "nokogiri"
|
2
2
|
|
3
|
-
# TCXRead is a class that parses TCX (Training Center XML) files to extract
|
4
|
-
# workout data such as activities, laps, tracks, trackpoints, and integral metrics.
|
5
3
|
class TCXRead
|
6
4
|
attr_reader :total_distance_meters, :total_time_seconds, :total_calories,
|
7
5
|
:total_ascent, :total_descent, :max_altitude, :average_heart_rate,
|
8
|
-
:max_watts, :average_watts, :
|
6
|
+
:max_watts, :average_watts, :average_cadence_all, :average_cadence_biking,
|
7
|
+
:average_speed_all, :average_speed_moving
|
9
8
|
|
10
9
|
def initialize(file_path)
|
11
10
|
@file_path = file_path
|
@@ -19,17 +18,16 @@ class TCXRead
|
|
19
18
|
@total_descent = 0
|
20
19
|
@max_altitude = 0
|
21
20
|
@average_heart_rate = 0
|
22
|
-
# use NA if no watts exist in the TCX file
|
23
21
|
@max_watts = 'NA'
|
24
22
|
@average_watts = 'NA'
|
25
|
-
@
|
23
|
+
@average_cadence_all = 0
|
24
|
+
@average_cadence_biking = 0
|
25
|
+
@average_speed_all = 0
|
26
|
+
@average_speed_moving = 0
|
26
27
|
|
27
28
|
parse
|
28
29
|
end
|
29
30
|
|
30
|
-
# Parses the TCX file and extracts data.
|
31
|
-
#
|
32
|
-
# @return [Hash] a hash containing the parsed activities.
|
33
31
|
def parse
|
34
32
|
activities = parse_activities
|
35
33
|
if activities.any?
|
@@ -39,7 +37,12 @@ class TCXRead
|
|
39
37
|
@total_ascent, @total_descent, @max_altitude = calculate_ascent_descent_and_max_altitude_from_activities(activities)
|
40
38
|
@average_heart_rate = calculate_average_heart_rate_from_activities(activities)
|
41
39
|
@max_watts, @average_watts = calculate_watts_from_activities(activities)
|
42
|
-
|
40
|
+
cadence_results = calculate_average_cadence_from_activities(activities)
|
41
|
+
@average_cadence_all = cadence_results[:average_cadence_all]
|
42
|
+
@average_cadence_biking = cadence_results[:average_cadence_biking]
|
43
|
+
speed_results = calculate_average_speed_from_activities(activities)
|
44
|
+
@average_speed_all = speed_results[:average_speed_all]
|
45
|
+
@average_speed_moving = speed_results[:average_speed_moving]
|
43
46
|
end
|
44
47
|
|
45
48
|
{ activities: activities }
|
@@ -47,9 +50,6 @@ class TCXRead
|
|
47
50
|
|
48
51
|
private
|
49
52
|
|
50
|
-
# Parses the activities from the TCX file.
|
51
|
-
#
|
52
|
-
# @return [Array<Hash>] an array of hashes, each representing an activity.
|
53
53
|
def parse_activities
|
54
54
|
activities = []
|
55
55
|
@doc.xpath('//xmlns:Activities/xmlns:Activity').each do |activity|
|
@@ -76,10 +76,6 @@ class TCXRead
|
|
76
76
|
activities
|
77
77
|
end
|
78
78
|
|
79
|
-
# Parses the laps for a given activity.
|
80
|
-
#
|
81
|
-
# @param activity [Nokogiri::XML::Element] the activity element from the TCX file.
|
82
|
-
# @return [Array<Hash>] an array of hashes, each representing a lap.
|
83
79
|
def parse_laps(activity)
|
84
80
|
laps = []
|
85
81
|
activity.xpath('xmlns:Lap').each do |lap|
|
@@ -100,10 +96,6 @@ class TCXRead
|
|
100
96
|
laps
|
101
97
|
end
|
102
98
|
|
103
|
-
# Parses the tracks for a given lap.
|
104
|
-
#
|
105
|
-
# @param lap [Nokogiri::XML::Element] the lap element from the TCX file.
|
106
|
-
# @return [Array<Array<Hash>>] an array of arrays, each representing a track containing trackpoints.
|
107
99
|
def parse_tracks(lap)
|
108
100
|
tracks = []
|
109
101
|
lap.xpath('xmlns:Track').each do |track|
|
@@ -117,7 +109,8 @@ class TCXRead
|
|
117
109
|
heart_rate: trackpoint.xpath('xmlns:HeartRateBpm/xmlns:Value').text.to_i,
|
118
110
|
cadence: trackpoint.xpath('xmlns:Cadence').text.to_i,
|
119
111
|
sensor_state: trackpoint.xpath('xmlns:SensorState').text,
|
120
|
-
watts: trackpoint.xpath('xmlns:Extensions/ns3:TPX/ns3:Watts').text.to_f
|
112
|
+
watts: trackpoint.xpath('xmlns:Extensions/ns3:TPX/ns3:Watts').text.to_f,
|
113
|
+
speed: trackpoint.xpath('xmlns:Extensions/ns3:TPX/ns3:Speed').text.to_f
|
121
114
|
}
|
122
115
|
end
|
123
116
|
tracks << trackpoints
|
@@ -125,10 +118,6 @@ class TCXRead
|
|
125
118
|
tracks
|
126
119
|
end
|
127
120
|
|
128
|
-
# Parses the position for a given trackpoint.
|
129
|
-
#
|
130
|
-
# @param trackpoint [Nokogiri::XML::Element] the trackpoint element from the TCX file.
|
131
|
-
# @return [Hash, nil] a hash representing the position (latitude and longitude) or nil if no position is available.
|
132
121
|
def parse_position(trackpoint)
|
133
122
|
position = trackpoint.at_xpath('xmlns:Position')
|
134
123
|
return nil unless position
|
@@ -139,10 +128,6 @@ class TCXRead
|
|
139
128
|
}
|
140
129
|
end
|
141
130
|
|
142
|
-
# Calculates the total ascent, total descent, and maximum altitude from the laps.
|
143
|
-
#
|
144
|
-
# @param laps [Array<Hash>] an array of lap hashes.
|
145
|
-
# @return [Array<Float>] an array containing total ascent, total descent, and maximum altitude.
|
146
131
|
def calculate_ascent_descent_and_max_altitude(laps)
|
147
132
|
total_ascent = 0.0
|
148
133
|
total_descent = 0.0
|
@@ -170,10 +155,6 @@ class TCXRead
|
|
170
155
|
[total_ascent, total_descent, max_altitude]
|
171
156
|
end
|
172
157
|
|
173
|
-
# Calculates the total ascent, total descent, and maximum altitude from the activities.
|
174
|
-
#
|
175
|
-
# @param activities [Array<Hash>] an array of activity hashes.
|
176
|
-
# @return [Array<Float>] an array containing total ascent, total descent, and maximum altitude.
|
177
158
|
def calculate_ascent_descent_and_max_altitude_from_activities(activities)
|
178
159
|
total_ascent = 0.0
|
179
160
|
total_descent = 0.0
|
@@ -188,10 +169,6 @@ class TCXRead
|
|
188
169
|
[total_ascent, total_descent, max_altitude]
|
189
170
|
end
|
190
171
|
|
191
|
-
# Calculates the average heart rate from the laps.
|
192
|
-
#
|
193
|
-
# @param laps [Array<Hash>] an array of lap hashes.
|
194
|
-
# @return [Float] the average heart rate.
|
195
172
|
def calculate_average_heart_rate(laps)
|
196
173
|
total_heart_rate = 0
|
197
174
|
heart_rate_count = 0
|
@@ -209,10 +186,6 @@ class TCXRead
|
|
209
186
|
heart_rate_count > 0 ? total_heart_rate.to_f / heart_rate_count : 0.0
|
210
187
|
end
|
211
188
|
|
212
|
-
# Calculates the average heart rate from the activities.
|
213
|
-
#
|
214
|
-
# @param activities [Array<Hash>] an array of activity hashes.
|
215
|
-
# @return [Float] the average heart rate.
|
216
189
|
def calculate_average_heart_rate_from_activities(activities)
|
217
190
|
total_heart_rate = 0
|
218
191
|
heart_rate_count = 0
|
@@ -232,10 +205,6 @@ class TCXRead
|
|
232
205
|
heart_rate_count > 0 ? total_heart_rate.to_f / heart_rate_count : 0.0
|
233
206
|
end
|
234
207
|
|
235
|
-
# Calculates the maximum and average watts from the activities.
|
236
|
-
#
|
237
|
-
# @param activities [Array<Hash>] an array of activity hashes.
|
238
|
-
# @return [Array<Float, Float>] an array containing maximum watts and average watts. Returns 'NA' for both if no watts data is available.
|
239
208
|
def calculate_watts_from_activities(activities)
|
240
209
|
max_watts = 0
|
241
210
|
total_watts = 0
|
@@ -265,26 +234,70 @@ class TCXRead
|
|
265
234
|
[max_watts, average_watts]
|
266
235
|
end
|
267
236
|
|
268
|
-
# Calculates the average cadence from the activities.
|
269
|
-
#
|
270
|
-
# @param activities [Array<Hash>] an array of activity hashes.
|
271
|
-
# @return [Float] the average cadence.
|
272
237
|
def calculate_average_cadence_from_activities(activities)
|
273
|
-
|
274
|
-
|
238
|
+
total_cadence_all = 0
|
239
|
+
total_cadence_biking = 0
|
240
|
+
cadence_count_all = 0
|
241
|
+
cadence_count_biking = 0
|
275
242
|
|
276
243
|
activities.each do |activity|
|
277
244
|
activity[:laps].each do |lap|
|
278
245
|
lap[:tracks].flatten.each do |trackpoint|
|
279
246
|
cadence = trackpoint[:cadence]
|
247
|
+
total_cadence_all += cadence
|
248
|
+
cadence_count_all += 1
|
249
|
+
|
280
250
|
if cadence > 0
|
281
|
-
|
282
|
-
|
251
|
+
total_cadence_biking += cadence
|
252
|
+
cadence_count_biking += 1
|
283
253
|
end
|
284
254
|
end
|
285
255
|
end
|
286
256
|
end
|
287
257
|
|
288
|
-
|
258
|
+
average_cadence_all = cadence_count_all > 0 ? total_cadence_all.to_f / cadence_count_all : 0.0
|
259
|
+
average_cadence_biking = cadence_count_biking > 0 ? total_cadence_biking.to_f / cadence_count_biking : 0.0
|
260
|
+
|
261
|
+
{
|
262
|
+
average_cadence_all: average_cadence_all,
|
263
|
+
average_cadence_biking: average_cadence_biking
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
# Calculates the average speed from the activities.
|
268
|
+
#
|
269
|
+
# @param activities [Array<Hash>] an array of activity hashes.
|
270
|
+
# @return [Hash] a hash containing average speed including zeros and average speed while moving.
|
271
|
+
def calculate_average_speed_from_activities(activities)
|
272
|
+
total_speed_all = 0
|
273
|
+
total_speed_moving = 0
|
274
|
+
speed_count_all = 0
|
275
|
+
speed_count_moving = 0
|
276
|
+
|
277
|
+
activities.each do |activity|
|
278
|
+
activity[:laps].each do |lap|
|
279
|
+
lap[:tracks].flatten.each do |trackpoint|
|
280
|
+
speed = trackpoint[:speed]
|
281
|
+
|
282
|
+
if speed
|
283
|
+
total_speed_all += speed
|
284
|
+
speed_count_all += 1
|
285
|
+
|
286
|
+
if speed > 0
|
287
|
+
total_speed_moving += speed
|
288
|
+
speed_count_moving += 1
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
average_speed_all = speed_count_all > 0 ? total_speed_all.to_f / speed_count_all : 0.0
|
296
|
+
average_speed_moving = speed_count_moving > 0 ? total_speed_moving.to_f / speed_count_moving : 0.0
|
297
|
+
|
298
|
+
{
|
299
|
+
average_speed_all: average_speed_all,
|
300
|
+
average_speed_moving: average_speed_moving
|
301
|
+
}
|
289
302
|
end
|
290
303
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tcxread
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- firefly-cpp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|