f1results 2.0 → 3.0
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.
- checksums.yaml +5 -5
- data/Rakefile +53 -5
- data/bin/f1results +8 -9
- data/lib/f1results/agent.rb +83 -40
- data/lib/f1results/event.rb +22 -20
- data/lib/f1results/parser.rb +34 -26
- data/lib/f1results/version.rb +1 -1
- data/lib/f1results.rb +9 -9
- data/test/agent_test.rb +25 -24
- data/test/event_test.rb +15 -21
- data/test/parser_test.rb +40 -19
- data/test/result_test.rb +3 -3
- metadata +13 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: aa2c935bee2392688a8b77b2c56668778bb6b84525c3fb6ea4331d5520675d59
|
|
4
|
+
data.tar.gz: 0a035e5ce0d87d87dad070597b860cdb33e2a32cb77f0c48e5b5f9bf4968f8b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7664c1698d0b058b1e7a2da0889e322869eac9fbc59c3589297690f843e5c4fcb38867b78448aa50aace0f9d73b32ec1415017f78f443d451497d98522c31235
|
|
7
|
+
data.tar.gz: d5deaedc384e08a0f02434cca1bd1e902c8b276b2bbf4d324ebc4b74ff44c1f51ab6709ea9e6df5ee3e5b1580052da5069b3eefd77ab4a6eea3f21683096a327
|
data/Rakefile
CHANGED
|
@@ -1,10 +1,58 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
1
|
+
require "bundler/setup"
|
|
2
|
+
require "rake"
|
|
3
|
+
require "rake/testtask"
|
|
4
|
+
require "./test/lib/fixtures"
|
|
5
|
+
require "open-uri"
|
|
6
|
+
require "fileutils"
|
|
7
|
+
require "tempfile"
|
|
4
8
|
|
|
5
9
|
task default: :test
|
|
6
10
|
|
|
7
11
|
Rake::TestTask.new do |t|
|
|
8
|
-
t.libs <<
|
|
9
|
-
t.pattern =
|
|
12
|
+
t.libs << "test/lib"
|
|
13
|
+
t.pattern = "test/*_test.rb"
|
|
14
|
+
t.warning = false
|
|
10
15
|
end
|
|
16
|
+
|
|
17
|
+
def fixture_downloads
|
|
18
|
+
Fixtures::STUBS.flat_map do |_name, mappings|
|
|
19
|
+
mappings.filter_map do |path, fixture|
|
|
20
|
+
next if fixture.is_a?(Integer)
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
url: URI.join(F1Results::BASE_URL, path).to_s,
|
|
24
|
+
destination: File.join(Fixtures::FIXTURES_DIR, fixture)
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
namespace :fixtures do
|
|
31
|
+
desc "List fixture URLs and destinations without downloading them"
|
|
32
|
+
task :list do
|
|
33
|
+
fixture_downloads.each do |download|
|
|
34
|
+
puts "#{download[:url]} => #{download[:destination]}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
desc "Download fixtures from formula1.com"
|
|
39
|
+
task :download do
|
|
40
|
+
WebMock.disable!
|
|
41
|
+
FileUtils.mkdir_p(Fixtures::FIXTURES_DIR)
|
|
42
|
+
|
|
43
|
+
fixture_downloads.each do |download|
|
|
44
|
+
puts "Downloading #{download[:url]} => #{download[:destination]}"
|
|
45
|
+
|
|
46
|
+
Tempfile.create(["f1results-fixture-", ".html"], Fixtures::FIXTURES_DIR) do |temporary|
|
|
47
|
+
URI.open(download[:url]) do |remote_file|
|
|
48
|
+
IO.copy_stream(remote_file, temporary)
|
|
49
|
+
end
|
|
50
|
+
temporary.close
|
|
51
|
+
FileUtils.mv(temporary.path, download[:destination])
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
desc "Download fixtures from formula1.com (alias for fixtures:download)"
|
|
58
|
+
task download_fixtures: "fixtures:download"
|
data/bin/f1results
CHANGED
|
@@ -3,24 +3,24 @@
|
|
|
3
3
|
require 'rubygems'
|
|
4
4
|
require 'optparse'
|
|
5
5
|
require 'terminal-table'
|
|
6
|
-
|
|
6
|
+
require_relative '../lib/f1results'
|
|
7
7
|
|
|
8
8
|
year = Time.now.year
|
|
9
|
-
|
|
9
|
+
grand_prix = ''
|
|
10
10
|
event_type = :race
|
|
11
11
|
url = nil
|
|
12
12
|
|
|
13
13
|
optparse = OptionParser.new do |opts|
|
|
14
|
-
opts.on('-y', '--year=YEAR', Integer)
|
|
15
|
-
opts.on('-
|
|
16
|
-
opts.on('-e', '--event=EVENT', String)
|
|
17
|
-
opts.on('-u', '--url=URL', String)
|
|
14
|
+
opts.on('-y', '--year=YEAR', Integer) { |val| year = val }
|
|
15
|
+
opts.on('-g', '--grandprix=GRANDPRIX', String) { |val| grand_prix = val.capitalize }
|
|
16
|
+
opts.on('-e', '--event=EVENT', String) { |val| event_type = val.downcase.to_sym }
|
|
17
|
+
opts.on('-u', '--url=URL', String) { |val| url = val }
|
|
18
18
|
opts.parse!
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
begin
|
|
22
22
|
event = if url.nil?
|
|
23
|
-
F1Results.fetch(year,
|
|
23
|
+
F1Results.fetch(year, grand_prix, event_type)
|
|
24
24
|
else
|
|
25
25
|
F1Results.fetch_with_url(url)
|
|
26
26
|
end
|
|
@@ -29,7 +29,7 @@ rescue RuntimeError => e
|
|
|
29
29
|
exit
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
title = url.nil? ? "#{event.year} #{event.
|
|
32
|
+
title = url.nil? ? "#{event.year} #{event.grand_prix} #{event.type_name}\n#{event.name}" : event.name
|
|
33
33
|
table = Terminal::Table.new title: title, rows: event.to_a
|
|
34
34
|
|
|
35
35
|
if event.practice?
|
|
@@ -58,5 +58,4 @@ elsif event.race?
|
|
|
58
58
|
table.align_column 6, :right
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
puts event.results[0].inspect
|
|
62
61
|
puts table
|
data/lib/f1results/agent.rb
CHANGED
|
@@ -2,6 +2,10 @@ require 'mechanize'
|
|
|
2
2
|
|
|
3
3
|
module F1Results
|
|
4
4
|
class Agent < ::Mechanize
|
|
5
|
+
def initialize
|
|
6
|
+
super
|
|
7
|
+
self.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
8
|
+
end
|
|
5
9
|
|
|
6
10
|
def get_url(event)
|
|
7
11
|
@event = event
|
|
@@ -11,13 +15,13 @@ module F1Results
|
|
|
11
15
|
def get_results(new_event)
|
|
12
16
|
@event = new_event
|
|
13
17
|
event.url ||= url
|
|
14
|
-
|
|
18
|
+
update_event
|
|
15
19
|
return event
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
def get_results_with_url(new_url)
|
|
19
23
|
event.url = new_url
|
|
20
|
-
|
|
24
|
+
update_event
|
|
21
25
|
return event
|
|
22
26
|
end
|
|
23
27
|
|
|
@@ -28,64 +32,103 @@ module F1Results
|
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def url
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
base_uri = URI.join F1Results::BASE_URL, "en/results/#{event.year}/"
|
|
36
|
+
year_uri = URI.join base_uri, 'races'
|
|
33
37
|
begin
|
|
34
|
-
get(
|
|
38
|
+
get(year_uri)
|
|
35
39
|
rescue Mechanize::ResponseCodeError, Net::HTTPNotFound
|
|
36
40
|
raise "No results found for #{event.year}"
|
|
37
41
|
end
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
grand_prix_nodes = page.parser.xpath("//table//tbody/tr/td[1]//a[contains(@href, '/races/')]").map do |a|
|
|
44
|
+
# Remove SVG icons from the links
|
|
45
|
+
a2 = a.dup
|
|
46
|
+
a2.search('.//svg').remove
|
|
47
|
+
a2
|
|
48
|
+
end
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
grand_prix_node = grand_prix_nodes.find { |node| node.text.strip.downcase == event.grand_prix.downcase }
|
|
51
|
+
raise "No grand prix '#{event.grand_prix}' found for #{event.year}" if grand_prix_node.nil?
|
|
52
|
+
|
|
53
|
+
grand_prix_uri = URI.join base_uri, grand_prix_node.attr('href')
|
|
45
54
|
|
|
46
|
-
def update_results
|
|
47
55
|
begin
|
|
48
|
-
get(
|
|
49
|
-
new_event = Parser.new(page).parse
|
|
50
|
-
event.results = new_event.results
|
|
51
|
-
event.type ||= new_event.type
|
|
52
|
-
event.country ||= new_event.country
|
|
53
|
-
event.name ||= new_event.name
|
|
56
|
+
get(grand_prix_uri)
|
|
54
57
|
rescue Mechanize::ResponseCodeError, Net::HTTPNotFound
|
|
55
|
-
raise "
|
|
58
|
+
raise "Error fetching #{grand_prix_uri}"
|
|
56
59
|
end
|
|
57
|
-
end
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
event_names = {
|
|
62
|
+
"race result" => :race_result_node,
|
|
63
|
+
"fastest laps" => :fastest_laps_node,
|
|
64
|
+
"pit stop summary" => :pit_stop_summary_node,
|
|
65
|
+
"starting grid" => :starting_grid_node,
|
|
66
|
+
"overall qualifying" => :overall_qualifying_node,
|
|
67
|
+
"qualifying" => :qualifying_node,
|
|
68
|
+
"practice 3" => :practice3_node,
|
|
69
|
+
"practice 2" => :practice2_node,
|
|
70
|
+
"practice 1" => :practice1_node,
|
|
71
|
+
"sprint" => :sprint_node,
|
|
72
|
+
"sprint grid" => :sprint_grid_node,
|
|
73
|
+
"sprint qualifying" => :sprint_qualifying_node
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
event_nodes = page.parser.xpath("//menu[@aria-labelledby='sidebar-dropdown']//li/a").map do |a|
|
|
77
|
+
# Remove SVG icons from the links
|
|
78
|
+
a2 = a.dup
|
|
79
|
+
a2.search('.//svg').remove
|
|
80
|
+
a2
|
|
64
81
|
end
|
|
65
|
-
raise "No country '#{event.country}' found for #{event.year}" if meeting_key_node.nil?
|
|
66
|
-
return meeting_key_node.attr('value')
|
|
67
|
-
end
|
|
68
82
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
key = node.text.
|
|
73
|
-
value = node.attr('data-value')
|
|
74
|
-
result_types[key] = value
|
|
83
|
+
# Create a hash to store the nodes
|
|
84
|
+
nodes = {}
|
|
85
|
+
event_names.each do |name, key|
|
|
86
|
+
nodes[key] = event_nodes.find { |node| node.text.strip.downcase == name }
|
|
75
87
|
end
|
|
76
88
|
|
|
77
89
|
# TODO: add support for Starting Grid, Warm Up, Pit Stop Summary, Fastest Laps
|
|
78
|
-
|
|
90
|
+
node = case
|
|
79
91
|
when event.race?
|
|
80
|
-
|
|
92
|
+
nodes[:race_result_node]
|
|
81
93
|
when event.qualifying?
|
|
82
|
-
|
|
83
|
-
when event.
|
|
84
|
-
|
|
94
|
+
nodes[:qualifying_node] || nodes[:overall_qualifying_node]
|
|
95
|
+
when event.practice3?
|
|
96
|
+
nodes[:practice3_node]
|
|
97
|
+
when event.practice2?
|
|
98
|
+
nodes[:practice2_node]
|
|
99
|
+
when event.practice1?
|
|
100
|
+
nodes[:practice1_node]
|
|
85
101
|
else
|
|
86
|
-
raise "
|
|
102
|
+
raise "Unsupported event type '#{event.type}'"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
raise "No results for event type '#{event.type}' at #{event.year} #{event.grand_prix}" if node.nil?
|
|
106
|
+
|
|
107
|
+
uri = URI.join base_uri, node.attr('href')
|
|
108
|
+
return uri.to_s
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def update_event
|
|
112
|
+
begin
|
|
113
|
+
get(event.url)
|
|
114
|
+
rescue Mechanize::ResponseCodeError, Net::HTTPNotFound
|
|
115
|
+
raise "No results found at '#{url}'"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
new_event = Parser.new(page).parse
|
|
119
|
+
event.type ||= new_event.type
|
|
120
|
+
validate_event(new_event)
|
|
121
|
+
|
|
122
|
+
event.grand_prix ||= new_event.grand_prix
|
|
123
|
+
event.circuit ||= new_event.circuit
|
|
124
|
+
event.name ||= new_event.name
|
|
125
|
+
event.results = new_event.results
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def validate_event(new_event)
|
|
129
|
+
if event.type != new_event.type
|
|
130
|
+
raise "Results at '#{url}' are returning from '#{new_event.type_name}', not the requested '#{event.type_name}'"
|
|
87
131
|
end
|
|
88
|
-
return file_name + '.html'
|
|
89
132
|
end
|
|
90
133
|
|
|
91
134
|
end
|
data/lib/f1results/event.rb
CHANGED
|
@@ -1,40 +1,42 @@
|
|
|
1
|
-
require 'active_support/core_ext/string'
|
|
2
|
-
|
|
3
1
|
module F1Results
|
|
4
2
|
class Event
|
|
5
|
-
attr_accessor :year, :
|
|
6
|
-
|
|
7
|
-
TYPE_ALIASES = {
|
|
8
|
-
p1: :practice1,
|
|
9
|
-
p2: :practice2,
|
|
10
|
-
p3: :practice3,
|
|
11
|
-
q: :qualifying,
|
|
12
|
-
r: :race
|
|
13
|
-
}
|
|
3
|
+
attr_accessor :year, :grand_prix, :circuit, :type, :name, :results, :url
|
|
14
4
|
|
|
15
5
|
def initialize(args = {})
|
|
16
6
|
@results = []
|
|
17
7
|
args.each { |k,v| send("#{k}=", v) }
|
|
18
8
|
end
|
|
19
9
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
@type = TYPE_ALIASES[type] || type
|
|
10
|
+
def grand_prix_slug
|
|
11
|
+
grand_prix.strip.downcase.gsub(/[:space:]+/, '-')
|
|
23
12
|
end
|
|
24
13
|
|
|
25
|
-
# Human readable type, e.g. "Practice 1", "Qualifying"
|
|
14
|
+
# Human readable type, e.g. "Practice 1", "Qualifying", "Starting Grid"
|
|
26
15
|
def type_name
|
|
27
|
-
@type.to_s
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
@type.to_s
|
|
17
|
+
.gsub(/\d/, ' \0')
|
|
18
|
+
.gsub('_', ' ')
|
|
19
|
+
.split(' ')
|
|
20
|
+
.map(&:capitalize)
|
|
21
|
+
.join(' ')
|
|
32
22
|
end
|
|
33
23
|
|
|
34
24
|
def practice?
|
|
35
25
|
/^practice(1|2|3)$/ =~ @type.to_s
|
|
36
26
|
end
|
|
37
27
|
|
|
28
|
+
def practice1?
|
|
29
|
+
@type == :practice1
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def practice2?
|
|
33
|
+
@type == :practice2
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def practice3?
|
|
37
|
+
@type == :practice3
|
|
38
|
+
end
|
|
39
|
+
|
|
38
40
|
def qualifying?
|
|
39
41
|
@type == :qualifying
|
|
40
42
|
end
|
data/lib/f1results/parser.rb
CHANGED
|
@@ -8,7 +8,8 @@ module F1Results
|
|
|
8
8
|
def parse
|
|
9
9
|
@event.name = event_name
|
|
10
10
|
@event.type = event_type
|
|
11
|
-
@event.
|
|
11
|
+
@event.grand_prix = event_grand_prix
|
|
12
|
+
@event.circuit = event_circuit
|
|
12
13
|
@event.results = event_results
|
|
13
14
|
# TODO: date
|
|
14
15
|
return @event
|
|
@@ -17,49 +18,56 @@ module F1Results
|
|
|
17
18
|
private
|
|
18
19
|
|
|
19
20
|
def event_name
|
|
20
|
-
|
|
21
|
-
return @page.parser.xpath('//title').text
|
|
22
|
-
.gsub(/[[:space:]]+/, ' ').strip
|
|
21
|
+
return @page.parser.at_xpath('//h1').text.gsub(/[[:space:]]+/, ' ').strip
|
|
23
22
|
end
|
|
24
23
|
|
|
25
24
|
def event_type
|
|
26
|
-
if
|
|
27
|
-
|
|
28
|
-
.gsub(/(?<=RACE )RESULT
|
|
29
|
-
.gsub(/OVERALL(?= QUALIFYING)
|
|
30
|
-
.gsub(/(?<=PRACTICE)( )(?=[1
|
|
31
|
-
.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
if m = @event.name.match(/(?:.+) - (.+)/)
|
|
26
|
+
result = m[1]
|
|
27
|
+
.gsub(/(?<=RACE )RESULT/i, '')
|
|
28
|
+
.gsub(/OVERALL(?= QUALIFYING)/i, '')
|
|
29
|
+
.gsub(/(?<=PRACTICE)( )(?=[1-3])/i, '')
|
|
30
|
+
.sub(/(?<=QUALIFYING)\s*\d+\z/i, '') # strip numbers for Qualifying
|
|
31
|
+
.strip
|
|
32
|
+
.downcase
|
|
33
|
+
result.to_sym
|
|
35
34
|
end
|
|
36
35
|
end
|
|
37
36
|
|
|
38
|
-
def
|
|
39
|
-
node = @page.parser.at_xpath('//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return text
|
|
37
|
+
def event_grand_prix
|
|
38
|
+
node = @page.parser.at_xpath('(//dialog)[3]//a[.//title[text()="Active"]]')
|
|
39
|
+
node.xpath('.//svg').remove
|
|
40
|
+
|
|
41
|
+
return node.text.strip
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def event_circuit
|
|
45
|
+
node = @page.parser.at_xpath('//h1/../../../../../div/following-sibling::div[1]//p[2]')
|
|
46
|
+
return node.text
|
|
43
47
|
end
|
|
44
48
|
|
|
45
49
|
def event_results
|
|
46
50
|
results = []
|
|
47
|
-
|
|
51
|
+
tbody = @page.parser.at_xpath('//tbody')
|
|
48
52
|
|
|
49
53
|
# Remove driver abbreviation from driver cell
|
|
50
|
-
|
|
54
|
+
tbody.xpath('.//span[@class="md:hidden"]').each(&:remove)
|
|
51
55
|
|
|
52
|
-
# Turn HTML
|
|
53
|
-
data =
|
|
56
|
+
# Turn HTML tbody into an array of arrays
|
|
57
|
+
data = tbody.xpath('.//tr').map do |row|
|
|
54
58
|
row.xpath('./td|./th').map do |cell|
|
|
55
59
|
cell = cell.text.gsub(/[[:space:]]+/, ' ').strip
|
|
56
|
-
cell.
|
|
60
|
+
cell.empty? ? nil : cell
|
|
57
61
|
end
|
|
58
62
|
end
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
header = @page.parser.xpath('//thead//th').map do |cell|
|
|
65
|
+
cell.text.to_s
|
|
66
|
+
.gsub(/\./, '') # Pos. -> Pos
|
|
67
|
+
.gsub(/[[:space:]]+/, '')
|
|
68
|
+
.gsub(/\/.*/, '') # Time/Retired -> Time
|
|
69
|
+
.downcase
|
|
70
|
+
.to_sym
|
|
63
71
|
end
|
|
64
72
|
|
|
65
73
|
# Set result class type
|
data/lib/f1results/version.rb
CHANGED
data/lib/f1results.rb
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
require_relative './f1results/version'
|
|
2
|
+
require_relative './f1results/agent'
|
|
3
|
+
require_relative './f1results/parser'
|
|
4
|
+
require_relative './f1results/event'
|
|
5
|
+
require_relative './f1results/result'
|
|
6
6
|
|
|
7
7
|
module F1Results
|
|
8
|
-
|
|
8
|
+
BASE_URL = 'https://www.formula1.com/'
|
|
9
9
|
|
|
10
|
-
# Get results from formula1.com for a given year,
|
|
10
|
+
# Get results from formula1.com for a given year, grand prix, and event type
|
|
11
11
|
# (race or qualifying)
|
|
12
12
|
#
|
|
13
13
|
# F1Results.f1results(2010, 'australia', :qualifying)
|
|
@@ -15,8 +15,8 @@ module F1Results
|
|
|
15
15
|
# Returns an `F1Results::Event` object which has a method `results`, which
|
|
16
16
|
# returns multiple objects of type `F1Results::Result`
|
|
17
17
|
|
|
18
|
-
def self.fetch(year,
|
|
19
|
-
event = Event.new(year: year,
|
|
18
|
+
def self.fetch(year, grand_prix, type = :race)
|
|
19
|
+
event = Event.new(year: year, grand_prix: grand_prix, type: type)
|
|
20
20
|
event.get_results
|
|
21
21
|
return event
|
|
22
22
|
end
|
data/test/agent_test.rb
CHANGED
|
@@ -1,82 +1,83 @@
|
|
|
1
1
|
require 'test_helper'
|
|
2
2
|
|
|
3
|
-
class AgentTest <
|
|
3
|
+
class AgentTest < Minitest::Test
|
|
4
4
|
include Fixtures
|
|
5
5
|
|
|
6
6
|
def setup
|
|
7
|
+
stub_all
|
|
7
8
|
@agent = F1Results::Agent.new
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def test_get_url_2016_australia_practice1
|
|
11
|
-
event = F1Results::Event.new(year: 2016,
|
|
12
|
-
url = '
|
|
12
|
+
event = F1Results::Event.new(year: 2016, grand_prix: 'Australia', type: :practice1)
|
|
13
|
+
url = 'https://www.formula1.com/en/results/2016/races/938/australia/practice/1'
|
|
13
14
|
assert_equal url, @agent.get_url(event)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def test_get_url_2016_australia_qualifying
|
|
17
|
-
event = F1Results::Event.new(year: 2016,
|
|
18
|
-
url = '
|
|
18
|
+
event = F1Results::Event.new(year: 2016, grand_prix: 'Australia', type: :qualifying)
|
|
19
|
+
url = 'https://www.formula1.com/en/results/2016/races/938/australia/qualifying'
|
|
19
20
|
assert_equal url, @agent.get_url(event)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
def test_get_url_2016_australia_race
|
|
23
|
-
event = F1Results::Event.new(year: 2016,
|
|
24
|
-
url = '
|
|
24
|
+
event = F1Results::Event.new(year: 2016, grand_prix: 'Australia', type: :race)
|
|
25
|
+
url = 'https://www.formula1.com/en/results/2016/races/938/australia/race-result'
|
|
25
26
|
assert_equal url, @agent.get_url(event)
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def test_get_url_1984_brazil_qualifying
|
|
29
|
-
event = F1Results::Event.new(year: 1984,
|
|
30
|
-
url = '
|
|
30
|
+
event = F1Results::Event.new(year: 1984, grand_prix: 'Brazil', type: :qualifying)
|
|
31
|
+
url = 'https://www.formula1.com/en/results/1984/races/466/brazil/qualifying/0'
|
|
31
32
|
assert_equal url, @agent.get_url(event)
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
def test_get_url_1984_brazil_race
|
|
35
|
-
event = F1Results::Event.new(year: 1984,
|
|
36
|
-
url = '
|
|
36
|
+
event = F1Results::Event.new(year: 1984, grand_prix: 'Brazil', type: :race)
|
|
37
|
+
url = 'https://www.formula1.com/en/results/1984/races/466/brazil/race-result'
|
|
37
38
|
assert_equal url, @agent.get_url(event)
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def test_get_url_1950_indianapolis_500_qualifying
|
|
41
|
-
event = F1Results::Event.new(year: 1950,
|
|
42
|
-
url = '
|
|
42
|
+
event = F1Results::Event.new(year: 1950, grand_prix: 'Indianapolis', type: :qualifying)
|
|
43
|
+
url = 'https://www.formula1.com/en/results/1950/races/96/indianapolis/qualifying/0'
|
|
43
44
|
assert_equal url, @agent.get_url(event)
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
def test_get_url_1950_indianapolis_500_race
|
|
47
|
-
event = F1Results::Event.new(year: 1950,
|
|
48
|
-
url = '
|
|
48
|
+
event = F1Results::Event.new(year: 1950, grand_prix: 'Indianapolis', type: :race)
|
|
49
|
+
url = 'https://www.formula1.com/en/results/1950/races/96/indianapolis/race-result'
|
|
49
50
|
assert_equal url, @agent.get_url(event)
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def test_get_results_1950_indianapolis_500_race
|
|
53
|
-
event = F1Results::Event.new(year: 1950,
|
|
54
|
+
event = F1Results::Event.new(year: 1950, grand_prix: 'Indianapolis', type: :race)
|
|
54
55
|
event = @agent.get_results(event)
|
|
55
56
|
assert_equal 36, event.results.length
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
def test_api
|
|
59
|
-
event = F1Results.fetch(1950, '
|
|
60
|
+
event = F1Results.fetch(1950, 'Indianapolis', :race)
|
|
60
61
|
assert_equal 36, event.results.length
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
def test_event_with_bad_year
|
|
64
|
-
assert_raise_with_message RuntimeError,
|
|
65
|
-
event = F1Results::Event.new(year: 1900,
|
|
65
|
+
assert_raise_with_message RuntimeError, "No grand prix 'Australia' found for 1900" do
|
|
66
|
+
event = F1Results::Event.new(year: 1900, grand_prix: 'Australia', type: :race)
|
|
66
67
|
@agent.get_url(event)
|
|
67
68
|
end
|
|
68
69
|
end
|
|
69
70
|
|
|
70
|
-
def
|
|
71
|
-
assert_raise_with_message RuntimeError, "No
|
|
72
|
-
event = F1Results::Event.new(year: 2016,
|
|
71
|
+
def test_event_with_bad_country
|
|
72
|
+
assert_raise_with_message RuntimeError, "No grand prix 'New Zealand' found for 2016" do
|
|
73
|
+
event = F1Results::Event.new(year: 2016, grand_prix: 'New Zealand', type: :race)
|
|
73
74
|
@agent.get_url(event)
|
|
74
75
|
end
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
def test_event_with_bad_event_name
|
|
78
|
-
assert_raise_with_message RuntimeError, "
|
|
79
|
-
event = F1Results::Event.new(year: 2016,
|
|
79
|
+
assert_raise_with_message RuntimeError, "Unsupported event type 'practice4'" do
|
|
80
|
+
event = F1Results::Event.new(year: 2016, grand_prix: 'Australia', type: :practice4)
|
|
80
81
|
@agent.get_url(event)
|
|
81
82
|
end
|
|
82
83
|
end
|
data/test/event_test.rb
CHANGED
|
@@ -1,37 +1,31 @@
|
|
|
1
1
|
require 'test_helper'
|
|
2
2
|
|
|
3
|
-
class EventTest <
|
|
4
|
-
include Fixtures
|
|
5
|
-
|
|
3
|
+
class EventTest < Minitest::Test
|
|
6
4
|
def setup
|
|
7
5
|
@event = F1Results::Event.new
|
|
8
6
|
end
|
|
9
7
|
|
|
10
|
-
def test_type
|
|
11
|
-
@event.type = :qualifying
|
|
12
|
-
assert_equal 'qualifying', @event.type_slug
|
|
13
|
-
@event.type = :practice3
|
|
14
|
-
assert_equal 'practice-3', @event.type_slug
|
|
15
|
-
@event.type = :p3
|
|
16
|
-
assert_equal :practice3, @event.type
|
|
17
|
-
end
|
|
18
|
-
|
|
19
8
|
def test_type_name
|
|
9
|
+
@event.type = :practice1
|
|
10
|
+
assert_equal 'Practice 1', @event.type_name
|
|
20
11
|
@event.type = :practice3
|
|
21
12
|
assert_equal 'Practice 3', @event.type_name
|
|
22
13
|
@event.type = :qualifying
|
|
23
14
|
assert_equal 'Qualifying', @event.type_name
|
|
24
15
|
@event.type = :race
|
|
25
16
|
assert_equal 'Race', @event.type_name
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@event.
|
|
32
|
-
|
|
33
|
-
@event.
|
|
34
|
-
|
|
17
|
+
@event.type = :starting_grid
|
|
18
|
+
assert_equal 'Starting Grid', @event.type_name
|
|
19
|
+
@event.type = :warm_up
|
|
20
|
+
assert_equal 'Warm Up', @event.type_name
|
|
21
|
+
@event.type = :pit_stop_summary
|
|
22
|
+
assert_equal 'Pit Stop Summary', @event.type_name
|
|
23
|
+
@event.type = :fastest_laps
|
|
24
|
+
assert_equal 'Fastest Laps', @event.type_name
|
|
25
|
+
@event.type = :sprint
|
|
26
|
+
assert_equal 'Sprint', @event.type_name
|
|
27
|
+
@event.type = :sprint_grid
|
|
28
|
+
assert_equal 'Sprint Grid', @event.type_name
|
|
35
29
|
end
|
|
36
30
|
|
|
37
31
|
def test_type_boolean_methods
|
data/test/parser_test.rb
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
require 'test_helper'
|
|
2
2
|
|
|
3
|
-
class ParserTest <
|
|
3
|
+
class ParserTest < Minitest::Test
|
|
4
4
|
include Fixtures
|
|
5
5
|
|
|
6
6
|
def test_2016_australia_practice1
|
|
7
|
-
event = events(:
|
|
7
|
+
event = events(:event_2016_australia_practice_1)
|
|
8
8
|
assert_equal '2016 FORMULA 1 ROLEX AUSTRALIAN GRAND PRIX - PRACTICE 1', event.name
|
|
9
|
-
assert_equal 'Australia', event.
|
|
9
|
+
assert_equal 'Australia', event.grand_prix
|
|
10
|
+
assert_equal 'Albert Park Grand Prix Circuit, Melbourne', event.circuit
|
|
10
11
|
assert_equal :practice1, event.type
|
|
11
12
|
assert_equal 22, event.results.length
|
|
12
13
|
assert_equal 'Lewis Hamilton', event.winning.driver
|
|
@@ -14,19 +15,22 @@ class ParserTest < MiniTest::Test
|
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def test_2016_australia_qualifying
|
|
17
|
-
event = events(:
|
|
18
|
+
event = events(:event_2016_australia_qualifying)
|
|
18
19
|
assert_equal '2016 FORMULA 1 ROLEX AUSTRALIAN GRAND PRIX - QUALIFYING', event.name
|
|
19
|
-
assert_equal 'Australia', event.
|
|
20
|
+
assert_equal 'Australia', event.grand_prix
|
|
21
|
+
assert_equal 'Albert Park Grand Prix Circuit, Melbourne', event.circuit
|
|
20
22
|
assert_equal :qualifying, event.type
|
|
21
23
|
assert_equal 22, event.results.length
|
|
24
|
+
|
|
22
25
|
assert_equal 'Lewis Hamilton', event.winning.driver
|
|
23
26
|
assert_equal 'Pascal Wehrlein', event.losing.driver
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
def test_2016_australia_race
|
|
27
|
-
event = events(:
|
|
30
|
+
event = events(:event_2016_australia_race_result)
|
|
28
31
|
assert_equal '2016 FORMULA 1 ROLEX AUSTRALIAN GRAND PRIX - RACE RESULT', event.name
|
|
29
|
-
assert_equal 'Australia', event.
|
|
32
|
+
assert_equal 'Australia', event.grand_prix
|
|
33
|
+
assert_equal 'Albert Park Grand Prix Circuit, Melbourne', event.circuit
|
|
30
34
|
assert_equal :race, event.type
|
|
31
35
|
assert_equal 22, event.results.length
|
|
32
36
|
assert_equal 'Nico Rosberg', event.winning.driver
|
|
@@ -34,9 +38,10 @@ class ParserTest < MiniTest::Test
|
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def test_1984_brazil_qualifying
|
|
37
|
-
event = events(:
|
|
38
|
-
assert_equal '
|
|
39
|
-
assert_equal 'Brazil', event.
|
|
41
|
+
event = events(:event_1984_brazil_qualifying_0)
|
|
42
|
+
assert_equal 'BRAZILIAN GRAND PRIX 1984 - OVERALL QUALIFYING', event.name
|
|
43
|
+
assert_equal 'Brazil', event.grand_prix
|
|
44
|
+
assert_equal 'Autódromo Internacional do Rio de Janeiro, Brazil', event.circuit
|
|
40
45
|
assert_equal :qualifying, event.type
|
|
41
46
|
assert_equal 24, event.results.length
|
|
42
47
|
assert_equal 'Elio de Angelis', event.winning.driver
|
|
@@ -44,9 +49,10 @@ class ParserTest < MiniTest::Test
|
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
def test_1984_brazil_race
|
|
47
|
-
event = events(:
|
|
48
|
-
assert_equal '
|
|
49
|
-
assert_equal 'Brazil', event.
|
|
52
|
+
event = events(:event_1984_brazil_race_result)
|
|
53
|
+
assert_equal 'BRAZILIAN GRAND PRIX 1984 - RACE RESULT', event.name
|
|
54
|
+
assert_equal 'Brazil', event.grand_prix
|
|
55
|
+
assert_equal 'Autódromo Internacional do Rio de Janeiro, Brazil', event.circuit
|
|
50
56
|
assert_equal :race, event.type
|
|
51
57
|
assert_equal 24, event.results.length
|
|
52
58
|
assert_equal 'Alain Prost', event.winning.driver
|
|
@@ -54,9 +60,10 @@ class ParserTest < MiniTest::Test
|
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
def test_1950_indianapolis_500_qualifying
|
|
57
|
-
event = events(:
|
|
58
|
-
assert_equal '1950
|
|
59
|
-
assert_equal '
|
|
63
|
+
event = events(:event_1950_indianapolis_qualifying_0)
|
|
64
|
+
assert_equal '1950 INDIANAPOLIS 500 - QUALIFYING', event.name
|
|
65
|
+
assert_equal 'Indianapolis', event.grand_prix
|
|
66
|
+
assert_equal 'Indianapolis Motor Speedway, United States', event.circuit
|
|
60
67
|
assert_equal :qualifying, event.type
|
|
61
68
|
assert_equal 1, event.results.length
|
|
62
69
|
assert_equal 'Walt Faulkner', event.winning.driver
|
|
@@ -64,13 +71,27 @@ class ParserTest < MiniTest::Test
|
|
|
64
71
|
end
|
|
65
72
|
|
|
66
73
|
def test_1950_indianapolis_500_race
|
|
67
|
-
event = events(:
|
|
68
|
-
assert_equal '1950
|
|
69
|
-
assert_equal '
|
|
74
|
+
event = events(:event_1950_indianapolis_race_result)
|
|
75
|
+
assert_equal '1950 INDIANAPOLIS 500 - RACE RESULT', event.name
|
|
76
|
+
assert_equal 'Indianapolis', event.grand_prix
|
|
77
|
+
assert_equal 'Indianapolis Motor Speedway, United States', event.circuit
|
|
70
78
|
assert_equal :race, event.type
|
|
71
79
|
# Note: 3 ties, so 36 finishers, 33 positions in this race
|
|
72
80
|
assert_equal 36, event.results.length
|
|
73
81
|
assert_equal 'Johnnie Parsons', event.winning.driver
|
|
74
82
|
assert_equal 'Duke Dinsmore', event.losing.driver
|
|
75
83
|
end
|
|
84
|
+
|
|
85
|
+
def test_2024_belgium_race
|
|
86
|
+
event = events(:event_2024_belgium_race_result)
|
|
87
|
+
assert_equal 'FORMULA 1 ROLEX BELGIAN GRAND PRIX 2024 - RACE RESULT', event.name
|
|
88
|
+
assert_equal 'Belgium', event.grand_prix
|
|
89
|
+
assert_equal 'Circuit de Spa-Francorchamps, Spa-Francorchamps', event.circuit
|
|
90
|
+
assert event.race?
|
|
91
|
+
assert_equal 20, event.results.length
|
|
92
|
+
# Note: George Russell won but was DQ'd, but still shows as first result
|
|
93
|
+
assert_equal 'George Russell', event.winning.driver
|
|
94
|
+
assert_equal 'DQ', event.results[0].position_name
|
|
95
|
+
assert_equal 'Zhou Guanyu', event.losing.driver
|
|
96
|
+
end
|
|
76
97
|
end
|
data/test/result_test.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require 'test_helper'
|
|
2
2
|
|
|
3
|
-
class ResultTest <
|
|
3
|
+
class ResultTest < Minitest::Test
|
|
4
4
|
def test_time
|
|
5
5
|
result = F1Results::RaceResult.new
|
|
6
6
|
result.time_or_retired = '1:25:11.288'
|
|
@@ -64,8 +64,8 @@ class ResultTest < MiniTest::Test
|
|
|
64
64
|
def test_parse_qualifying_with_blank_values
|
|
65
65
|
result = F1Results::QualifyingResult.new(position: '16', driver: 'Marcus Ericsson', team: 'Sauber', q1: '1:31.376', q2: nil, q3: nil, laps: 10)
|
|
66
66
|
assert_equal '1:31.376', result.q1
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
assert_nil result.q2
|
|
68
|
+
assert_nil result.q3
|
|
69
69
|
assert_equal 10, result.laps
|
|
70
70
|
end
|
|
71
71
|
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: f1results
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '
|
|
4
|
+
version: '3.0'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Darren Jeacocke
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: mechanize
|
|
@@ -16,84 +15,70 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '2.
|
|
18
|
+
version: '2.14'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - "~>"
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '2.
|
|
25
|
+
version: '2.14'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: terminal-table
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
30
29
|
requirements:
|
|
31
30
|
- - "~>"
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
32
|
+
version: '4.0'
|
|
34
33
|
type: :runtime
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
37
|
- - "~>"
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: activesupport
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '4'
|
|
48
|
-
type: :runtime
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '4'
|
|
39
|
+
version: '4.0'
|
|
55
40
|
- !ruby/object:Gem::Dependency
|
|
56
41
|
name: rake
|
|
57
42
|
requirement: !ruby/object:Gem::Requirement
|
|
58
43
|
requirements:
|
|
59
44
|
- - "~>"
|
|
60
45
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
46
|
+
version: '13.3'
|
|
62
47
|
type: :development
|
|
63
48
|
prerelease: false
|
|
64
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
50
|
requirements:
|
|
66
51
|
- - "~>"
|
|
67
52
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
53
|
+
version: '13.3'
|
|
69
54
|
- !ruby/object:Gem::Dependency
|
|
70
55
|
name: minitest
|
|
71
56
|
requirement: !ruby/object:Gem::Requirement
|
|
72
57
|
requirements:
|
|
73
58
|
- - "~>"
|
|
74
59
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '5.
|
|
60
|
+
version: '5.25'
|
|
76
61
|
type: :development
|
|
77
62
|
prerelease: false
|
|
78
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
64
|
requirements:
|
|
80
65
|
- - "~>"
|
|
81
66
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '5.
|
|
67
|
+
version: '5.25'
|
|
83
68
|
- !ruby/object:Gem::Dependency
|
|
84
69
|
name: webmock
|
|
85
70
|
requirement: !ruby/object:Gem::Requirement
|
|
86
71
|
requirements:
|
|
87
72
|
- - "~>"
|
|
88
73
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
74
|
+
version: 3.25.1
|
|
90
75
|
type: :development
|
|
91
76
|
prerelease: false
|
|
92
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
78
|
requirements:
|
|
94
79
|
- - "~>"
|
|
95
80
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
81
|
+
version: 3.25.1
|
|
97
82
|
description: Get F1 results from formula1.com
|
|
98
83
|
email: dazonic@me.com
|
|
99
84
|
executables:
|
|
@@ -119,7 +104,6 @@ homepage: https://github.com/daz/f1results
|
|
|
119
104
|
licenses:
|
|
120
105
|
- MIT
|
|
121
106
|
metadata: {}
|
|
122
|
-
post_install_message:
|
|
123
107
|
rdoc_options: []
|
|
124
108
|
require_paths:
|
|
125
109
|
- lib
|
|
@@ -134,9 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
134
118
|
- !ruby/object:Gem::Version
|
|
135
119
|
version: '0'
|
|
136
120
|
requirements: []
|
|
137
|
-
|
|
138
|
-
rubygems_version: 2.4.5
|
|
139
|
-
signing_key:
|
|
121
|
+
rubygems_version: 3.6.7
|
|
140
122
|
specification_version: 4
|
|
141
123
|
summary: F1 Results
|
|
142
124
|
test_files: []
|