tcxread 0.1.2 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -0
  3. data/README.md +83 -0
  4. data/lib/tcxread.rb +67 -53
  5. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e42a89981c1614dec069270cb1315ee64d0c6a172de5c44a5370b5f5c8bd584f
4
- data.tar.gz: dcee18bb770a1aa020d5ad8f67370f775a6f8d25139c39eb8576e76c463c1efd
3
+ metadata.gz: 7b17178126aaf26d70869aa047a6054ca4128ac454c75b3fbd0903ce935222dc
4
+ data.tar.gz: f27e41c68c87672a7acd2791b4a147c8a0bf09a2da1707a8722091a965508b89
5
5
  SHA512:
6
- metadata.gz: 72c0b8ba80f7cd524c91417123353e6d9523551050c513d55447ae1c6d73ff8add6af60cc6ec319d26af8aa237e7bcb40771f587653ffe92766f57be7d538194
7
- data.tar.gz: 42a2ea64072c68e786e002a5dd0fa32c4547ac0503a4eb207537c3d9442148ca958fe9038b44b826033fa94ca4ddf80feaff92f8904745bfdad7aaba96c591ae
6
+ metadata.gz: 9281010f7900f5bf0a579c648690329e0f5680ecb9cc91e56733a2718ce15ac75cdfa15eabb4fc4f97b99d6cd26dfbbb53c627124775261fa1083797f9c1d4c1
7
+ data.tar.gz: c61eee523a2b34730e40cddce7cd2eb58193bcb9585b84c0d08252f66cd8f4e90f49b6bf77dcbebdf13d8e96336fd5365787a3e670fda68b26cab214a540cd26
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022-2024 Iztok Fister Jr.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ <h1 align="center">
2
+ tcxread -- A parser for TCX files written in Ruby
3
+ </h1>
4
+
5
+ <p align="center">
6
+ <a href="https://badge.fury.io/rb/tcxread">
7
+ <img alt="Gem Version" src="https://badge.fury.io/rb/tcxread.svg">
8
+ </a>
9
+ <a href="https://github.com/firefly-cpp/tcxread/blob/master/LICENSE">
10
+ <img alt="License" src="https://img.shields.io/github/license/firefly-cpp/tcxread.svg">
11
+ </a>
12
+ <a href=https://repology.org/project/ruby:tcxread/versions>
13
+ <img alt="Packaging status" src="https://repology.org/badge/tiny-repos/ruby:tcxread.svg">
14
+ </a>
15
+ <img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/w/firefly-cpp/tcxread.svg">
16
+ <img alt="GitHub repo size" src="https://img.shields.io/github/repo-size/firefly-cpp/tcxread">
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="#-installation">📦 Installation</a> •
21
+ <a href="#-basic-run-example">🚀 Basic run example</a> •
22
+ <a href="#-datasets">💾 Datasets</a> •
23
+ <a href="#-further-read">📖 Further read</a> •
24
+ <a href="#-related-packagesframeworks">🔗 Related packages/frameworks</a> •
25
+ <a href="#-license">🔑 License</a>
26
+ </p>
27
+
28
+ tcxread is a Ruby package designed to simplify the process of reading and processing .tcx files, commonly used by Garmin devices and other GPS-enabled fitness devices to store workout data.
29
+
30
+ ## 📦 Installation
31
+
32
+ ```sh
33
+ $ gem install tcxread
34
+ ```
35
+
36
+ ## 🚀 Basic run example
37
+
38
+ ```ruby
39
+ require 'tcxread'
40
+
41
+ data = TCXRead.new('23.tcx')
42
+
43
+ puts "Distance meters: #{data.total_distance_meters}, " \
44
+ "Time seconds: #{data.total_time_seconds}, " \
45
+ "Calories: #{data.total_calories}, " \
46
+ "Total ascent: #{data.total_ascent}, " \
47
+ "Total descent: #{data.total_descent}, " \
48
+ "Max altitude: #{data.max_altitude}, " \
49
+ "Average heart rate: #{data.average_heart_rate}, " \
50
+ "Average watts: #{data.average_watts}, " \
51
+ "Max watts: #{data.max_watts}, " \
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
+
57
+ ```
58
+
59
+ ## 💾 Datasets
60
+
61
+ Datasets available and used in the examples on the following links: [DATASET1](http://iztok-jr-fister.eu/static/publications/Sport5.zip), [DATASET2](http://iztok-jr-fister.eu/static/css/datasets/Sport.zip), [DATASET3](https://github.com/firefly-cpp/tcx-test-files).
62
+
63
+ ## 📖 Further read
64
+
65
+ [1] [Awesome Computational Intelligence in Sports](https://github.com/firefly-cpp/awesome-computational-intelligence-in-sports)
66
+
67
+ ## 🔗 Related packages/frameworks
68
+
69
+ [1] [tcxreader: Python reader/parser for Garmin's TCX file format.](https://github.com/alenrajsp/tcxreader)
70
+
71
+ [2] [sport-activities-features: A minimalistic toolbox for extracting features from sports activity files written in Python](https://github.com/firefly-cpp/sport-activities-features)
72
+
73
+ [3] [TCXReader.jl: Julia package designed for parsing TCX files](https://github.com/firefly-cpp/TCXReader.jl)
74
+
75
+ [4] [TCXWriter: A Tiny Library for writing/creating TCX files on Arduino](https://github.com/firefly-cpp/tcxwriter)
76
+
77
+ ## 🔑 License
78
+
79
+ This package is distributed under the MIT License. This license can be found online at <http://www.opensource.org/licenses/MIT>.
80
+
81
+ ## Disclaimer
82
+
83
+ This framework is provided as-is, and there are no guarantees that it fits your purposes or that it is bug-free. Use it at your own risk!
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, :average_cadence
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
- @average_cadence = 0
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
- @average_cadence = calculate_average_cadence_from_activities(activities)
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|
@@ -125,10 +117,6 @@ class TCXRead
125
117
  tracks
126
118
  end
127
119
 
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
120
  def parse_position(trackpoint)
133
121
  position = trackpoint.at_xpath('xmlns:Position')
134
122
  return nil unless position
@@ -139,10 +127,6 @@ class TCXRead
139
127
  }
140
128
  end
141
129
 
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
130
  def calculate_ascent_descent_and_max_altitude(laps)
147
131
  total_ascent = 0.0
148
132
  total_descent = 0.0
@@ -170,10 +154,6 @@ class TCXRead
170
154
  [total_ascent, total_descent, max_altitude]
171
155
  end
172
156
 
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
157
  def calculate_ascent_descent_and_max_altitude_from_activities(activities)
178
158
  total_ascent = 0.0
179
159
  total_descent = 0.0
@@ -188,10 +168,6 @@ class TCXRead
188
168
  [total_ascent, total_descent, max_altitude]
189
169
  end
190
170
 
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
171
  def calculate_average_heart_rate(laps)
196
172
  total_heart_rate = 0
197
173
  heart_rate_count = 0
@@ -209,10 +185,6 @@ class TCXRead
209
185
  heart_rate_count > 0 ? total_heart_rate.to_f / heart_rate_count : 0.0
210
186
  end
211
187
 
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
188
  def calculate_average_heart_rate_from_activities(activities)
217
189
  total_heart_rate = 0
218
190
  heart_rate_count = 0
@@ -232,10 +204,6 @@ class TCXRead
232
204
  heart_rate_count > 0 ? total_heart_rate.to_f / heart_rate_count : 0.0
233
205
  end
234
206
 
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
207
  def calculate_watts_from_activities(activities)
240
208
  max_watts = 0
241
209
  total_watts = 0
@@ -265,26 +233,72 @@ class TCXRead
265
233
  [max_watts, average_watts]
266
234
  end
267
235
 
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
236
  def calculate_average_cadence_from_activities(activities)
273
- total_cadence = 0
274
- cadence_count = 0
237
+ total_cadence_all = 0
238
+ total_cadence_biking = 0
239
+ cadence_count_all = 0
240
+ cadence_count_biking = 0
275
241
 
276
242
  activities.each do |activity|
277
243
  activity[:laps].each do |lap|
278
244
  lap[:tracks].flatten.each do |trackpoint|
279
245
  cadence = trackpoint[:cadence]
246
+ total_cadence_all += cadence
247
+ cadence_count_all += 1
248
+
280
249
  if cadence > 0
281
- total_cadence += cadence
282
- cadence_count += 1
250
+ total_cadence_biking += cadence
251
+ cadence_count_biking += 1
283
252
  end
284
253
  end
285
254
  end
286
255
  end
287
256
 
288
- cadence_count > 0 ? total_cadence.to_f / cadence_count : 0.0
257
+ average_cadence_all = cadence_count_all > 0 ? total_cadence_all.to_f / cadence_count_all : 0.0
258
+ average_cadence_biking = cadence_count_biking > 0 ? total_cadence_biking.to_f / cadence_count_biking : 0.0
259
+
260
+ {
261
+ average_cadence_all: average_cadence_all,
262
+ average_cadence_biking: average_cadence_biking
263
+ }
264
+ end
265
+
266
+ # Calculates the average speed from the activities.
267
+ #
268
+ # @param activities [Array<Hash>] an array of activity hashes.
269
+ # @return [Hash] a hash containing average speed including zeros and average speed while moving.
270
+ def calculate_average_speed_from_activities(activities)
271
+ total_speed_all = 0
272
+ total_speed_moving = 0
273
+ speed_count_all = 0
274
+ speed_count_moving = 0
275
+
276
+ activities.each do |activity|
277
+ activity[:laps].each do |lap|
278
+ lap[:tracks].flatten.each do |trackpoint|
279
+ distance = trackpoint[:distance_meters]
280
+ time_seconds = lap[:total_time_seconds]
281
+ speed = distance / time_seconds if time_seconds > 0
282
+
283
+ if speed
284
+ total_speed_all += speed
285
+ speed_count_all += 1
286
+
287
+ if speed > 0
288
+ total_speed_moving += speed
289
+ speed_count_moving += 1
290
+ end
291
+ end
292
+ end
293
+ end
294
+ end
295
+
296
+ average_speed_all = speed_count_all > 0 ? total_speed_all.to_f / speed_count_all : 0.0
297
+ average_speed_moving = speed_count_moving > 0 ? total_speed_moving.to_f / speed_count_moving : 0.0
298
+
299
+ {
300
+ average_speed_all: average_speed_all,
301
+ average_speed_moving: average_speed_moving
302
+ }
289
303
  end
290
304
  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.2
4
+ version: 0.1.4
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-07-29 00:00:00.000000000 Z
11
+ date: 2024-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -31,6 +31,8 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
+ - LICENSE
35
+ - README.md
34
36
  - lib/tcxread.rb
35
37
  homepage: https://github.com/firefly-cpp/tcxread
36
38
  licenses: