antw-dyno 0.1.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.
- data/LICENSE +19 -0
- data/README.markdown +68 -0
- data/Rakefile +45 -0
- data/VERSION.yml +4 -0
- data/lib/dyno.rb +22 -0
- data/lib/dyno/competitor.rb +55 -0
- data/lib/dyno/event.rb +16 -0
- data/lib/dyno/parsers/gtr2_parser.rb +13 -0
- data/lib/dyno/parsers/race07_parser.rb +130 -0
- data/lib/dyno/parsers/rfactor_parser.rb +138 -0
- data/spec/competitor_spec.rb +129 -0
- data/spec/event_spec.rb +59 -0
- data/spec/fixtures/gtr2/full.ini +0 -0
- data/spec/fixtures/gtr2/header_no_track.ini +0 -0
- data/spec/fixtures/gtr2/header_only.ini +0 -0
- data/spec/fixtures/gtr2/no_header_section.ini +0 -0
- data/spec/fixtures/gtr2/single_driver.ini +0 -0
- data/spec/fixtures/race07/full.ini +131 -0
- data/spec/fixtures/race07/header_no_track.ini +11 -0
- data/spec/fixtures/race07/header_only.ini +12 -0
- data/spec/fixtures/race07/no_header_section.ini +6 -0
- data/spec/fixtures/race07/no_steam_id.ini +37 -0
- data/spec/fixtures/race07/single_driver.ini +37 -0
- data/spec/fixtures/race07/single_driver_dnf.ini +34 -0
- data/spec/fixtures/race07/single_driver_dsq.ini +34 -0
- data/spec/fixtures/readme.markdown +5 -0
- data/spec/fixtures/rfactor/arca.xml +187 -0
- data/spec/fixtures/rfactor/event_only.xml +43 -0
- data/spec/fixtures/rfactor/full.xml +1023 -0
- data/spec/fixtures/rfactor/missing_root.xml +6 -0
- data/spec/fixtures/rfactor/single_driver.xml +85 -0
- data/spec/fixtures/rfactor/single_driver_dnf.xml +80 -0
- data/spec/fixtures/rfactor/single_driver_dsq.xml +79 -0
- data/spec/parsers/gtr2_parser_spec.rb +199 -0
- data/spec/parsers/race07_parser_spec.rb +155 -0
- data/spec/parsers/rfactor_parser_spec.rb +158 -0
- data/spec/spec_helper.rb +3 -0
- metadata +107 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2008 Anthony Williams
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
dyno
|
2
|
+
====
|
3
|
+
|
4
|
+
> _A dynamometer or "dyno" for short, is a machine used to measure torque and
|
5
|
+
> rotational speed (rpm) from which power produced by an engine, motor or
|
6
|
+
> other rotating prime mover can be calculated. ([Wikipedia][dyno-wp])_
|
7
|
+
|
8
|
+
In this context, however, Dyno is a pure Ruby library for parsing sim-racing
|
9
|
+
result files, soon to be used on the upcoming Torque sim-racing app.
|
10
|
+
|
11
|
+
Dyno presently supports files spat out by a number of games:
|
12
|
+
|
13
|
+
* **Race07Parser** parses results from RACE 07, GTR: Evolution, and
|
14
|
+
STCC: The Game
|
15
|
+
* **GTR2Parser** parses results from GTR2.
|
16
|
+
* **RFactorParser** parses results from rFactor (and mods), and ARCA.
|
17
|
+
|
18
|
+
Dyno requires the iniparse gem (available via `gem install iniparse` or on
|
19
|
+
[GitHub][iniparse]) for the Race 07 and GTR2 parsers, and libxml-ruby for
|
20
|
+
the rFactor parser.
|
21
|
+
|
22
|
+
Usage
|
23
|
+
-----
|
24
|
+
|
25
|
+
Dyno::Parsers::Race07Parser.parse_file( '/path/to/result/file' )
|
26
|
+
# => Dyno::Event
|
27
|
+
|
28
|
+
Dyno::Parsers::GTR2Parser.parse_file( '/path/to/result/file' )
|
29
|
+
# => Dyno::Event
|
30
|
+
|
31
|
+
Dyno::Parsers::RFactorParser.parse_file( '/path/to/result/file' )
|
32
|
+
# => Dyno::Event
|
33
|
+
|
34
|
+
Each of these operations will return a Dyno::Event instance. Dyno::Event
|
35
|
+
defines `#competitors` containing a collection of all of those who took part
|
36
|
+
in the event. See the API docs for Dyno::Event and Dyno::Competitor for more
|
37
|
+
details.
|
38
|
+
|
39
|
+
If your results file couldn't be parsed, a Dyno::MalformedInputError exception
|
40
|
+
will be raised.
|
41
|
+
|
42
|
+
License
|
43
|
+
-------
|
44
|
+
|
45
|
+
Dyno is distributed under the MIT/X11 License.
|
46
|
+
|
47
|
+
> Copyright (c) 2008-2009 Anthony Williams
|
48
|
+
>
|
49
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
50
|
+
> of this software and associated documentation files (the "Software"), to
|
51
|
+
> deal in the Software without restriction, including without limitation the
|
52
|
+
> rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
53
|
+
> sell copies of the Software, and to permit persons to whom the Software is
|
54
|
+
> furnished to do so, subject to the following conditions:
|
55
|
+
>
|
56
|
+
> The above copyright notice and this permission notice shall be included in
|
57
|
+
> all copies or substantial portions of the Software.
|
58
|
+
>
|
59
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
60
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
61
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
62
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
63
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
64
|
+
> FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
65
|
+
> IN THE SOFTWARE.
|
66
|
+
|
67
|
+
[dyno-wp]: http://en.wikipedia.org/wiki/Dynamometer "Wikipedia"
|
68
|
+
[iniparse]: http://github.com/antw/iniparse "IniParse on GitHub"
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |s|
|
7
|
+
s.name = 'dyno'
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.has_rdoc = true
|
10
|
+
s.summary = 'A rubygem for parsing sim-racing results files.'
|
11
|
+
s.description = s.summary
|
12
|
+
s.author = 'Anthony Williams'
|
13
|
+
s.email = 'anthony@ninecraft.com'
|
14
|
+
s.homepage = 'http://github.com/anthonyw/dyno'
|
15
|
+
|
16
|
+
s.extra_rdoc_files = ['README.markdown', 'LICENSE']
|
17
|
+
|
18
|
+
# Dependencies.
|
19
|
+
s.add_dependency "iniparse", ">= 0.2.0"
|
20
|
+
|
21
|
+
s.files = %w(LICENSE README.markdown Rakefile VERSION.yml) +
|
22
|
+
Dir.glob("{lib,spec}/**/*")
|
23
|
+
end
|
24
|
+
rescue LoadError
|
25
|
+
puts 'Jeweler not available. Install it with: sudo gem install ' +
|
26
|
+
'technicalpickles-jeweler -s http://gems.github.com'
|
27
|
+
end
|
28
|
+
|
29
|
+
##############################################################################
|
30
|
+
# rSpec & rcov
|
31
|
+
##############################################################################
|
32
|
+
|
33
|
+
desc "Run all examples"
|
34
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
35
|
+
t.spec_files = FileList['spec/**/*.rb']
|
36
|
+
t.spec_opts = ['-c -f s']
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Run all examples with RCov"
|
40
|
+
Spec::Rake::SpecTask.new('spec:rcov') do |t|
|
41
|
+
t.spec_files = FileList['spec/**/*.rb']
|
42
|
+
t.spec_opts = ['-c -f s']
|
43
|
+
t.rcov = true
|
44
|
+
t.rcov_opts = ['--exclude', 'spec']
|
45
|
+
end
|
data/VERSION.yml
ADDED
data/lib/dyno.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'time'
|
3
|
+
require 'iniparse'
|
4
|
+
require 'libxml'
|
5
|
+
|
6
|
+
module Dyno
|
7
|
+
# Base exception class.
|
8
|
+
class DynoError < StandardError; end
|
9
|
+
|
10
|
+
# Raised if an input source couldn't be parsed, or was missing something.
|
11
|
+
class MalformedInputError < DynoError; end
|
12
|
+
end
|
13
|
+
|
14
|
+
dir = File.join( File.dirname(__FILE__), "dyno" )
|
15
|
+
|
16
|
+
require File.join( dir, "competitor" )
|
17
|
+
require File.join( dir, "event" )
|
18
|
+
|
19
|
+
# Parsers
|
20
|
+
require File.join( dir, "parsers", "race07_parser" )
|
21
|
+
require File.join( dir, "parsers", "gtr2_parser" )
|
22
|
+
require File.join( dir, "parsers", "rfactor_parser" )
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Dyno
|
2
|
+
class Competitor
|
3
|
+
attr_accessor :name, :uid, :position, :vehicle, :laps, :race_time,
|
4
|
+
:best_lap, :lap_times
|
5
|
+
|
6
|
+
##
|
7
|
+
# @param [String] name The competitor's name.
|
8
|
+
# @param [Hash] properties Extra information about the competitor.
|
9
|
+
#
|
10
|
+
def initialize(name, properties = {})
|
11
|
+
@name = name
|
12
|
+
@lap_times = properties.fetch(:laps, [])
|
13
|
+
@dnf = false
|
14
|
+
|
15
|
+
[:uid, :position, :vehicle, :laps, :race_time, :best_lap].each do |prop|
|
16
|
+
instance_variable_set "@#{prop}", properties[prop]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Returns true fi the competitor finished the event.
|
22
|
+
#
|
23
|
+
def finished?
|
24
|
+
not dnf? and not dsq?
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Flags this competitor as having not completed the event.
|
29
|
+
#
|
30
|
+
def dnf!
|
31
|
+
@dnf = :dnf
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Returns true if the competitor failed to finish.
|
36
|
+
#
|
37
|
+
def dnf?
|
38
|
+
@dnf == :dnf
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Flags this competitor has having been disqualified from the event.
|
43
|
+
#
|
44
|
+
def dsq!
|
45
|
+
@dnf = :dsq
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Returns true if the competitor was disqualified.
|
50
|
+
#
|
51
|
+
def dsq?
|
52
|
+
@dnf == :dsq
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/dyno/event.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Dyno
|
2
|
+
class Event
|
3
|
+
attr_accessor :time, :game, :game_version, :track, :competitors
|
4
|
+
|
5
|
+
##
|
6
|
+
# @param [Hash] properties Event information.
|
7
|
+
#
|
8
|
+
def initialize(properties = {})
|
9
|
+
@time = properties.fetch( :time, Time.now )
|
10
|
+
@track = properties[:track]
|
11
|
+
@game = properties[:game]
|
12
|
+
@game_version = properties[:game_version]
|
13
|
+
@competitors = properties.fetch( :competitors, [] )
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Dyno::Parsers
|
2
|
+
##
|
3
|
+
# Parses a Race 07 results file which are almost identical to Race 07 files.
|
4
|
+
#
|
5
|
+
class GTR2Parser < Race07Parser
|
6
|
+
def self.parse_file( filename )
|
7
|
+
# GTR2 files start with a line which isn't valid INI; remove it.
|
8
|
+
parse( IniParse.parse(
|
9
|
+
File.read( filename ).sub!(/^.*\n/, '')
|
10
|
+
) )
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Dyno::Parsers
|
2
|
+
##
|
3
|
+
# Parses a Race 07 results file (which appears to be some variation of the
|
4
|
+
# ini format).
|
5
|
+
#
|
6
|
+
class Race07Parser
|
7
|
+
##
|
8
|
+
# Takes a file path and parses it.
|
9
|
+
#
|
10
|
+
# @param [String] filename The path to the results file.
|
11
|
+
#
|
12
|
+
def self.parse_file( filename )
|
13
|
+
parse( IniParse.open( filename ) )
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Takes an IniParse::Document instance, parses the contents, and returns a
|
18
|
+
# Dyno::Event containing your results.
|
19
|
+
#
|
20
|
+
# @param [IniParse::Document] results The results.
|
21
|
+
# @return [Dyno::Event]
|
22
|
+
#
|
23
|
+
def self.parse( results )
|
24
|
+
new( results ).parse
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Returns your parsed event and competitor information.
|
29
|
+
#
|
30
|
+
def parse
|
31
|
+
parse_event!
|
32
|
+
parse_competitors!
|
33
|
+
@event
|
34
|
+
end
|
35
|
+
|
36
|
+
#######
|
37
|
+
private
|
38
|
+
#######
|
39
|
+
|
40
|
+
##
|
41
|
+
# Takes an IniParse::Document instance and parses the contents.
|
42
|
+
#
|
43
|
+
# @param [IniParse::Document] results The results.
|
44
|
+
#
|
45
|
+
def initialize( results )
|
46
|
+
@raw = results
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Extracts the event information from the results.
|
51
|
+
#
|
52
|
+
def parse_event!
|
53
|
+
raise Dyno::MalformedInputError unless @raw.has_section?('Header')
|
54
|
+
|
55
|
+
@event = Dyno::Event.new( :game => @raw['Header']['Game'] )
|
56
|
+
@event.time = Time.parse( @raw['Header']['TimeString'] )
|
57
|
+
@event.game_version = @raw['Header']['Version']
|
58
|
+
|
59
|
+
# Extract the track name from Race/Scene
|
60
|
+
if @raw.has_section?('Race') && @raw['Race']['Scene']
|
61
|
+
@event.track = @raw['Race']['Scene'].split( '\\' )[-2].gsub( /[_-]+/, ' ' )
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Extracts information about each of the competitors.
|
67
|
+
#
|
68
|
+
def parse_competitors!
|
69
|
+
finished_competitors = []
|
70
|
+
dnf_competitors = []
|
71
|
+
|
72
|
+
@raw.each do |section|
|
73
|
+
# Competitor sections are named SlotNNN.
|
74
|
+
next unless section.key =~ /Slot\d\d\d/
|
75
|
+
|
76
|
+
competitor = Dyno::Competitor.new(section['Driver'],
|
77
|
+
:vehicle => section['Vehicle'],
|
78
|
+
:laps => section['Laps'].to_i
|
79
|
+
)
|
80
|
+
|
81
|
+
# Some results files have a blank ID.
|
82
|
+
if section['SteamId'] && section['SteamId'].kind_of?(Numeric)
|
83
|
+
competitor.uid = section['SteamId']
|
84
|
+
end
|
85
|
+
|
86
|
+
# Sort out the competitors lap times.
|
87
|
+
competitor.best_lap = lap_time_to_float(section['BestLap'])
|
88
|
+
|
89
|
+
competitor.lap_times = section['Lap'].map do |lap|
|
90
|
+
lap = lap.gsub(/\((.*)\)/, '\1')
|
91
|
+
lap_time_to_float(lap.split(',').last.strip)
|
92
|
+
end
|
93
|
+
|
94
|
+
if section['RaceTime'] =~ /D(NF|S?Q)/
|
95
|
+
$1 == 'NF' ? competitor.dnf! : competitor.dsq!
|
96
|
+
competitor.race_time = 0
|
97
|
+
dnf_competitors << competitor
|
98
|
+
else
|
99
|
+
time = section['RaceTime'].split( /:|\./ )
|
100
|
+
|
101
|
+
competitor.race_time = time[2].to_f + ( time[1].to_i * 60 ) +
|
102
|
+
( time[0].to_i * 60 * 60 ) + "0.#{time[3]}".to_f
|
103
|
+
|
104
|
+
finished_competitors << competitor
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Sort finished competitors by their race time, lowest (P1) first.
|
109
|
+
finished_competitors = finished_competitors.sort_by do |c|
|
110
|
+
[ - c.laps, c.race_time ]
|
111
|
+
end
|
112
|
+
|
113
|
+
# ... and DNF'ed competitors by how many laps they've done.
|
114
|
+
dnf_competitors = dnf_competitors.sort_by { |c| c.laps }.reverse!
|
115
|
+
|
116
|
+
# Finally let's assign their finishing positions.
|
117
|
+
competitors = finished_competitors + dnf_competitors
|
118
|
+
competitors.each_with_index { |c, i| c.position = i + 1 }
|
119
|
+
|
120
|
+
# All done!
|
121
|
+
@event.competitors = competitors
|
122
|
+
end
|
123
|
+
|
124
|
+
# Converts a lap time (in the format of M:SS:SSS) to a float.
|
125
|
+
def lap_time_to_float(time)
|
126
|
+
time = time.split( /:|\./ )
|
127
|
+
time[1].to_f + ( time[0].to_i * 60 ) + "0.#{time[2]}".to_f
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Dyno::Parsers
|
2
|
+
##
|
3
|
+
# Parses a rFactor results files.
|
4
|
+
#
|
5
|
+
class RFactorParser
|
6
|
+
##
|
7
|
+
# Takes a file path and parses it.
|
8
|
+
#
|
9
|
+
# @param [String] filename The path to the results file.
|
10
|
+
#
|
11
|
+
def self.parse_file( filename )
|
12
|
+
xmlparser = LibXML::XML::Parser.new
|
13
|
+
xmlparser.file = filename
|
14
|
+
parse( xmlparser.parse )
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Takes a LibXML::XML::Document instance, parses the contents, and returns
|
19
|
+
# a Dyno::Event containing your results.
|
20
|
+
#
|
21
|
+
# @param [LibXML::XML::Document] results The results.
|
22
|
+
# @return [Dyno::Event]
|
23
|
+
#
|
24
|
+
def self.parse( results )
|
25
|
+
new( results ).parse
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Returns your parsed event and competitor information.
|
30
|
+
#
|
31
|
+
def parse
|
32
|
+
parse_event!
|
33
|
+
parse_competitors!
|
34
|
+
@event
|
35
|
+
end
|
36
|
+
|
37
|
+
#######
|
38
|
+
private
|
39
|
+
#######
|
40
|
+
|
41
|
+
##
|
42
|
+
# Takes a LibXML::XML::Document instance and parses the contents.
|
43
|
+
#
|
44
|
+
# @param [LibXML::XML::Document] results The results.
|
45
|
+
#
|
46
|
+
def initialize( results )
|
47
|
+
@doc = results
|
48
|
+
@stack = []
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Extracts the event information from the results.
|
53
|
+
#
|
54
|
+
def parse_event!
|
55
|
+
unless results = @doc.find_first('//RaceResults')
|
56
|
+
raise Dyno::MalformedInputError, 'No //RaceResults node.'
|
57
|
+
end
|
58
|
+
|
59
|
+
with_node(results) do
|
60
|
+
@event = Dyno::Event.new(
|
61
|
+
:game => clean(value('Mod').gsub(/\.rfm$/, '')),
|
62
|
+
:track => clean(value('TrackCourse')),
|
63
|
+
:game_version => value('GameVersion')
|
64
|
+
)
|
65
|
+
|
66
|
+
# Sort out the event time - the rFactor results format has more than
|
67
|
+
# one TimeString node. The first appears to be when the server was
|
68
|
+
# started - we need the time of the event itself, which is held under
|
69
|
+
# the Race, Qualify or Warmup node.
|
70
|
+
if event_node = \
|
71
|
+
(@doc.find_first('//RaceResults/Race') ||
|
72
|
+
@doc.find_first('//RaceResults/Qualify') ||
|
73
|
+
@doc.find_first('//RaceResults/Warmup'))
|
74
|
+
with_node(event_node) do
|
75
|
+
@event.time = Time.parse( value('TimeString') )
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Extracts information about each of the competitors.
|
83
|
+
#
|
84
|
+
def parse_competitors!
|
85
|
+
@doc.find('//Driver').each do |section|
|
86
|
+
with_node(section) do
|
87
|
+
competitor = Dyno::Competitor.new(value('Name'),
|
88
|
+
:vehicle => clean(value('CarType')),
|
89
|
+
:laps => value('Laps').to_i
|
90
|
+
)
|
91
|
+
|
92
|
+
competitor.position = value('Position').to_i
|
93
|
+
competitor.best_lap = lap_time_to_float( value('BestLapTime') )
|
94
|
+
competitor.race_time = lap_time_to_float( value('FinishTime') )
|
95
|
+
|
96
|
+
competitor.lap_times = section.find('Lap').map do |lap|
|
97
|
+
lap_time_to_float( lap.content )
|
98
|
+
end
|
99
|
+
|
100
|
+
if value('FinishStatus') =~ /D(NF|S?Q)/
|
101
|
+
$1 == 'NF' ? competitor.dnf! : competitor.dsq!
|
102
|
+
end
|
103
|
+
|
104
|
+
@event.competitors << competitor
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
@event.competitors = @event.competitors.sort_by { |c| c.position }
|
109
|
+
end
|
110
|
+
|
111
|
+
# --
|
112
|
+
# Utility stuff
|
113
|
+
# ++
|
114
|
+
|
115
|
+
def value( node ) # :nodoc:
|
116
|
+
if found = @stack.last.find_first( node )
|
117
|
+
found.content
|
118
|
+
else
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def clean( value ) # :nodoc:
|
124
|
+
value.gsub(/[-_]/, ' ').squeeze(' ') unless value.nil?
|
125
|
+
end
|
126
|
+
|
127
|
+
def with_node( node ) # :nodoc:
|
128
|
+
@stack.push( node )
|
129
|
+
yield
|
130
|
+
@stack.pop
|
131
|
+
end
|
132
|
+
|
133
|
+
# Converts a lap time (in the format of M:SS:SSS) to a float.
|
134
|
+
def lap_time_to_float(time) # :nodoc:
|
135
|
+
(time.nil? || time == '--.----') ? 0.0 : Float(time)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|