concept2-data-parser 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +26 -0
- data/Rakefile +9 -0
- data/bin/concept2-parser +15 -0
- data/concept2-data-parser.gemspec +24 -0
- data/lib/concept2/data/parser/version.rb +7 -0
- data/lib/concept2/data/parser.rb +106 -0
- data/test/concept2_data_parser_test.rb +55 -0
- data/test/fixtures/6k-stroke-data.txt +18596 -0
- data/test/test_helper.rb +5 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1879f7982a7337c27edd455ba39c1353fb66e51b
|
4
|
+
data.tar.gz: 128deb1c4762a504c9835ad509510327cd11421e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bfbf3711c132ff290986a2d7fb969288b67de0905dcd0b2168f39160a96b3e70d5dcf822398ab7d7fa10881c6b55e12e80e15152cb7c1496adacbf15ef2c69d3
|
7
|
+
data.tar.gz: 412d8a438df5ee7ae04f6dc8a76d17bbb9f85ed80b64f661018fc7b709d5535a0f9779e98ceba26282c726296dcf40c1a4e1f7228435f8030c99218a6619ff0f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Henry Poydar
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Concept2 Data Parser
|
2
|
+
|
3
|
+
Compiles a 6k erg test 500m splits spreadsheet from Concept2 stroke data files
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install it yourself as:
|
8
|
+
|
9
|
+
$ gem install concept2-data-parser
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
TODO: Write usage instructions here
|
14
|
+
|
15
|
+
## Development
|
16
|
+
|
17
|
+
- Use with any distance erg test
|
18
|
+
- Error handling with bad files
|
19
|
+
|
20
|
+
## Contributing
|
21
|
+
|
22
|
+
1. Fork it ( https://github.com/hpoydar/concept2-data-parser/fork )
|
23
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
24
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
25
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
26
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/concept2-parser
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
|
7
|
+
require 'concept2/data/parser'
|
8
|
+
|
9
|
+
if ARGV.empty?
|
10
|
+
puts "Please pass in a data file"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
parser = Concept2::Data::Parser.new(ARGV.first)
|
15
|
+
parser.write_file!
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'concept2/data/parser/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "concept2-data-parser"
|
8
|
+
spec.version = Concept2::Data::Parser::VERSION
|
9
|
+
spec.authors = ["Henry Poydar"]
|
10
|
+
spec.email = ["hpoydar@gmail.com"]
|
11
|
+
spec.summary = %q{Compiles a 6k erg test 500m splits spreadsheet from Concept2 stroke data files}
|
12
|
+
spec.homepage = "https://github.com/hpoydar/concept2-data-parser"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_runtime_dependency 'chronic_duration', '~> 0.10.6'
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency 'minitest', '~> 5.5.0'
|
24
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require "concept2/data/parser/version"
|
2
|
+
require "csv"
|
3
|
+
require 'chronic_duration'
|
4
|
+
|
5
|
+
module Concept2
|
6
|
+
module Data
|
7
|
+
class Parser
|
8
|
+
|
9
|
+
attr_accessor :rowers
|
10
|
+
|
11
|
+
def initialize(file)
|
12
|
+
@file = file
|
13
|
+
@raw = nil
|
14
|
+
@rowers = [] # Array of arrays where we'll store everything
|
15
|
+
|
16
|
+
read_file
|
17
|
+
compile_totals!
|
18
|
+
toss_empties!
|
19
|
+
compile_splits!
|
20
|
+
end
|
21
|
+
|
22
|
+
def write_file!(file="#{File.basename(@file)}-formatted.csv")
|
23
|
+
headers = ["Name", "Time", "Avg. Split", "Avg. SPM"]
|
24
|
+
12.times do |i|
|
25
|
+
headers << "#{(i+1)*500}m Split"
|
26
|
+
headers << "#{(i+1)*500}m SPM"
|
27
|
+
end
|
28
|
+
CSV.open(file, "wb") do |csv|
|
29
|
+
csv << headers
|
30
|
+
rowers.each do |rower|
|
31
|
+
res = []
|
32
|
+
res << rower[:name]
|
33
|
+
res << rower[:overall_time]
|
34
|
+
res << rower[:overall_split]
|
35
|
+
res << rower[:overall_stroke_rate]
|
36
|
+
12.times do |i|
|
37
|
+
res << rower[:splits][i]
|
38
|
+
res << rower[:stroke_rates][i]
|
39
|
+
end
|
40
|
+
csv << res
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def read_file
|
48
|
+
CSV.foreach(@file, headers: true) do |csv|
|
49
|
+
if !csv['PM3'].nil?
|
50
|
+
@rowers << {name: csv['PM3'], data: []}
|
51
|
+
end
|
52
|
+
@rowers[@rowers.size-1][:data] << [
|
53
|
+
csv['Time'].to_f,
|
54
|
+
csv['Meters'].to_f,
|
55
|
+
csv['Stroke_Rate'].to_i]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def compile_totals!
|
60
|
+
@rowers.each do |rower|
|
61
|
+
rower[:overall_time] = ChronicDuration.output(
|
62
|
+
rower[:data].last[0].round(1), format: :chrono)
|
63
|
+
rower[:overall_stroke_rate] = average_stroke_rate(
|
64
|
+
rower[:data].map {|n| n[2] })
|
65
|
+
rower[:overall_split] = split_time(rower[:data].last[0], 6000.0)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def compile_splits!
|
70
|
+
@rowers.each do |rower|
|
71
|
+
idxs = []
|
72
|
+
12.times do |i|
|
73
|
+
idx_val = rower[:data].map {|r| r[1] }.
|
74
|
+
min_by { |v| (v-(500*(i+1))).abs }
|
75
|
+
idxs << rower[:data].index {|r| r[1] == idx_val }
|
76
|
+
end
|
77
|
+
rower[:stroke_rates] = []
|
78
|
+
rower[:splits] = []
|
79
|
+
prev_idx = 0
|
80
|
+
idxs.each do |idx|
|
81
|
+
rower[:stroke_rates] << average_stroke_rate(
|
82
|
+
rower[:data][prev_idx..idx].map {|n| n[2] })
|
83
|
+
rower[:splits] << split_time(
|
84
|
+
rower[:data][idx][0] - rower[:data][prev_idx][0])
|
85
|
+
prev_idx = idx
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def toss_empties!
|
91
|
+
@rowers = @rowers.select {|r| r[:overall_stroke_rate] != 0 }
|
92
|
+
end
|
93
|
+
|
94
|
+
def average_stroke_rate(stroke_array)
|
95
|
+
stroke_array.inject(:+) / stroke_array.size
|
96
|
+
end
|
97
|
+
|
98
|
+
def split_time(secs, distance=500.0)
|
99
|
+
ChronicDuration.output(
|
100
|
+
((secs * 500.0) / distance).round(1), format: :chrono)
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class Concept2DataParserTest < Minitest::Test
|
4
|
+
|
5
|
+
describe "parsing data" do
|
6
|
+
def setup
|
7
|
+
@fixture = File.join(File.dirname(__FILE__),
|
8
|
+
'fixtures/6k-stroke-data.txt')
|
9
|
+
@parser = Concept2::Data::Parser.new(@fixture)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "creates raw data for each rower" do
|
13
|
+
assert_equal 4, @parser.rowers.size
|
14
|
+
end
|
15
|
+
|
16
|
+
it "records the name of each rower" do
|
17
|
+
assert_equal %w(Snell Cheuse McCormick Keefe),
|
18
|
+
@parser.rowers.map {|r| r[:name] }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "compiling data" do
|
23
|
+
def setup
|
24
|
+
@fixture = File.join(File.dirname(__FILE__),
|
25
|
+
'fixtures/6k-stroke-data.txt')
|
26
|
+
@parser = Concept2::Data::Parser.new(@fixture)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "compiles the overall time" do
|
30
|
+
assert_equal ["26:17.5", "26:15", "27:00.9", "25:52"],
|
31
|
+
@parser.rowers.map {|r| r[:overall_time] }
|
32
|
+
end
|
33
|
+
|
34
|
+
it "compiles the overall stroke rate" do
|
35
|
+
assert_equal [26, 24, 26, 23],
|
36
|
+
@parser.rowers.map {|r| r[:overall_stroke_rate] }
|
37
|
+
end
|
38
|
+
|
39
|
+
it "compiles the overall 500m split" do
|
40
|
+
assert_equal ["2:11.5", "2:11.3", "2:15.1", "2:09.3"],
|
41
|
+
@parser.rowers.map {|r| r[:overall_split] }
|
42
|
+
end
|
43
|
+
|
44
|
+
it "compiles 500m split average stroke rate" do
|
45
|
+
assert_equal [28, 25, 25, 24, 25, 25, 26, 25, 26, 26, 27, 29],
|
46
|
+
@parser.rowers[0][:stroke_rates]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "compiles 500m splits" do
|
50
|
+
assert_equal ["2:03.5", "2:12.5", "2:14", "2:15", "2:15", "2:14.5", "2:13", "2:14", "2:10", "2:10.4", "2:10.5", "2:04.6"],
|
51
|
+
@parser.rowers[0][:splits]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|