Skip to content

Commit d64c625

Browse files
committed
Major update, support properties, add basic example, modify README and tests
1 parent 804ba50 commit d64c625

File tree

4 files changed

+107
-22
lines changed

4 files changed

+107
-22
lines changed

README.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,44 @@ tcxread is a Ruby package designed to simplify the process of reading and proces
99

1010
## Installation 📦
1111

12-
```
13-
12+
```sh
13+
$ gem install ast-tdl
1414
```
1515

1616
## Basic run example 🚀
1717

1818
```ruby
1919
require 'tcxread'
2020

21-
parser = TCXRead.new('2.tcx')
22-
data = parser.parse
23-
puts data.inspect
21+
data = TCXRead.new('2.tcx')
22+
23+
puts "Distance meters: #{data.total_distance_meters}, " \
24+
"Time seconds: #{data.total_time_seconds}, " \
25+
"Calories: #{data.total_calories}, " \
26+
"Total ascent: #{data.total_ascent}, " \
27+
"Total descent: #{data.total_descent}, " \
28+
"Max altitude: #{data.max_altitude}, " \
29+
"Average heart rate: #{data.average_heart_rate}"
2430
```
2531

2632
## Datasets
2733

2834
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).
2935

36+
## Further read
37+
38+
[1] [Awesome Computational Intelligence in Sports](https://github.com/firefly-cpp/awesome-computational-intelligence-in-sports)
39+
3040
## Related packages/frameworks
3141

3242
[1] [tcxreader: Python reader/parser for Garmin's TCX file format.](https://github.com/alenrajsp/tcxreader)
3343

3444
[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)
3545

46+
[3] [TCXReader.jl: Julia package designed for parsing TCX files](https://github.com/firefly-cpp/TCXReader.jl)
47+
48+
[4] [TCXWriter: A Tiny Library for writing/creating TCX files on Arduino](https://github.com/firefly-cpp/tcxwriter)
49+
3650
## License
3751

3852
This package is distributed under the MIT License. This license can be found online at <http://www.opensource.org/licenses/MIT>.

examples/basic_run.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
require 'tcxread'
2+
3+
data = TCXRead.new('2.tcx')
4+
5+
puts "Distance meters: #{data.total_distance_meters}, " \
6+
"Time seconds: #{data.total_time_seconds}, " \
7+
"Calories: #{data.total_calories}, " \
8+
"Total ascent: #{data.total_ascent}, " \
9+
"Total descent: #{data.total_descent}, " \
10+
"Max altitude: #{data.max_altitude}, " \
11+
"Average heart rate: #{data.average_heart_rate}"
12+

lib/tcxread.rb

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,44 @@
11
require "nokogiri"
22

33
# 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.
4+
# workout data such as activities, laps, tracks, trackpoints, and integral metrics.
55
class TCXRead
6+
attr_reader :total_distance_meters, :total_time_seconds, :total_calories,
7+
:total_ascent, :total_descent, :max_altitude, :average_heart_rate
8+
69
# Initializes the TCXRead with the path to the TCX file.
710
#
811
# @param file_path [String] the path to the TCX file.
912
def initialize(file_path)
1013
@file_path = file_path
1114
@doc = Nokogiri::XML(File.open(file_path))
15+
16+
# Init the properties
17+
@total_distance_meters = 0
18+
@total_time_seconds = 0
19+
@total_calories = 0
20+
@total_ascent = 0
21+
@total_descent = 0
22+
@max_altitude = 0
23+
@average_heart_rate = 0
24+
25+
parse
1226
end
1327

1428
# Parses the TCX file and extracts data.
1529
#
1630
# @return [Hash] a hash containing the parsed activities.
1731
def parse
18-
{
19-
activities: parse_activities
20-
}
32+
activities = parse_activities
33+
if activities.any?
34+
@total_time_seconds = activities.sum { |activity| activity[:total_time_seconds] }
35+
@total_distance_meters = activities.sum { |activity| activity[:total_distance_meters] }
36+
@total_calories = activities.sum { |activity| activity[:total_calories] }
37+
@total_ascent, @total_descent, @max_altitude = calculate_ascent_descent_and_max_altitude_from_activities(activities)
38+
@average_heart_rate = calculate_average_heart_rate_from_activities(activities)
39+
end
40+
41+
{ activities: activities }
2142
end
2243

2344
private
@@ -128,7 +149,6 @@ def calculate_ascent_descent_and_max_altitude(laps)
128149
altitude = trackpoint[:altitude_meters]
129150
max_altitude = altitude if altitude > max_altitude
130151

131-
# can be improved
132152
if previous_altitude
133153
altitude_change = altitude - previous_altitude
134154
if altitude_change > 0
@@ -145,6 +165,24 @@ def calculate_ascent_descent_and_max_altitude(laps)
145165
[total_ascent, total_descent, max_altitude]
146166
end
147167

168+
# Calculates the total ascent, total descent, and maximum altitude from the activities.
169+
#
170+
# @param activities [Array<Hash>] an array of activity hashes.
171+
# @return [Array<Float>] an array containing total ascent, total descent, and maximum altitude.
172+
def calculate_ascent_descent_and_max_altitude_from_activities(activities)
173+
total_ascent = 0.0
174+
total_descent = 0.0
175+
max_altitude = -Float::INFINITY
176+
177+
activities.each do |activity|
178+
total_ascent += activity[:total_ascent]
179+
total_descent += activity[:total_descent]
180+
max_altitude = activity[:max_altitude] if activity[:max_altitude] > max_altitude
181+
end
182+
183+
[total_ascent, total_descent, max_altitude]
184+
end
185+
148186
# Calculates the average heart rate from the laps.
149187
#
150188
# @param laps [Array<Hash>] an array of lap hashes.
@@ -165,4 +203,27 @@ def calculate_average_heart_rate(laps)
165203

166204
heart_rate_count > 0 ? total_heart_rate.to_f / heart_rate_count : 0.0
167205
end
206+
207+
# Calculates the average heart rate from the activities.
208+
#
209+
# @param activities [Array<Hash>] an array of activity hashes.
210+
# @return [Float] the average heart rate.
211+
def calculate_average_heart_rate_from_activities(activities)
212+
total_heart_rate = 0
213+
heart_rate_count = 0
214+
215+
activities.each do |activity|
216+
activity[:laps].each do |lap|
217+
lap[:tracks].flatten.each do |trackpoint|
218+
heart_rate = trackpoint[:heart_rate]
219+
if heart_rate > 0
220+
total_heart_rate += heart_rate
221+
heart_rate_count += 1
222+
end
223+
end
224+
end
225+
end
226+
227+
heart_rate_count > 0 ? total_heart_rate.to_f / heart_rate_count : 0.0
228+
end
168229
end

test/test_tcxread.rb

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,30 @@
55
class TCXReadTest < Minitest::Test
66
def setup
77
# tests for file 2.tcx
8-
@parser1 = TCXRead.new('test/2.tcx')
9-
@data1 = @parser1.parse
8+
@data1 = TCXRead.new('test/2.tcx')
109

1110
# tests for file 15.tcx
12-
@parser2 = TCXRead.new('test/15.tcx')
13-
@data2 = @parser2.parse
11+
@data2 = TCXRead.new('test/15.tcx')
1412
end
1513

1614
def test_total_calories
17-
assert_equal @data1[:activities][0][:total_calories], 924
18-
assert_equal @data2[:activities][0][:total_calories], 2010
15+
assert_equal @data1.total_calories, 924
16+
assert_equal @data2.total_calories, 2010
1917
end
2018

2119
def test_total_distance
22-
assert_equal @data1[:activities][0][:total_distance_meters], 24732.34
23-
assert_equal @data2[:activities][0][:total_distance_meters], 116366.98
20+
assert_equal @data1.total_distance_meters, 24732.34
21+
assert_equal @data2.total_distance_meters, 116366.98
2422

2523
end
2624

2725
def test_total_duration
28-
assert_equal @data1[:activities][0][:total_time_seconds], 3876.0
29-
assert_equal @data2[:activities][0][:total_time_seconds], 17250.0
26+
assert_equal @data1.total_time_seconds, 3876.0
27+
assert_equal @data2.total_time_seconds, 17250.0
3028
end
3129

3230
def test_total_ascent
33-
assert_equal @data1[:activities][0][:total_ascent], 452.5999946594238
34-
assert_equal @data2[:activities][0][:total_ascent], 1404.400026500225
31+
assert_equal @data1.total_ascent, 452.5999946594238
32+
assert_equal @data2.total_ascent, 1404.400026500225
3533
end
3634
end

0 commit comments

Comments
 (0)