sports_data_api 0.0.1 → 0.0.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/lib/nfl.rb +60 -1
- data/lib/sports_data_api/version.rb +1 -1
- data/lib/sports_data_api.rb +10 -2
- data/spec/lib/nfl_spec.rb +58 -0
- data/spec/lib/sports_data_api_spec.rb +18 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/xml/boxscore.xml +93 -0
- metadata +7 -5
- data/spec/xml/game.xml +0 -69
data/lib/nfl.rb
CHANGED
@@ -34,17 +34,47 @@ module SportsDataApi
|
|
34
34
|
end
|
35
35
|
|
36
36
|
class Game
|
37
|
-
attr_reader :id, :scheduled, :home, :away, :status
|
37
|
+
attr_reader :id, :scheduled, :home, :home_team, :away, :away_team, :status, :quarter, :clock
|
38
38
|
|
39
39
|
def initialize(xml)
|
40
|
+
xml = xml.first if xml.is_a? Nokogiri::XML::NodeSet
|
40
41
|
if xml.is_a? Nokogiri::XML::Element
|
41
42
|
@id = xml["id"]
|
42
43
|
@scheduled = Time.parse xml["scheduled"]
|
43
44
|
@home = xml["home"]
|
44
45
|
@away = xml["away"]
|
45
46
|
@status = xml["status"]
|
47
|
+
@quarter = xml["quarter"].to_i
|
48
|
+
@clock = xml["clock"]
|
49
|
+
|
50
|
+
team_xml = xml.xpath("team")
|
51
|
+
@home_team = Team.new(team_xml.first)
|
52
|
+
@away_team = Team.new(team_xml.last)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Team
|
58
|
+
attr_reader :id, :name, :market, :remaining_challenges, :remaining_timeouts, :score, :quarters
|
59
|
+
|
60
|
+
def initialize(xml)
|
61
|
+
if xml.is_a? Nokogiri::XML::Element
|
62
|
+
@id = xml["id"]
|
63
|
+
@name = xml["name"]
|
64
|
+
@market = xml["market"]
|
65
|
+
@remaining_challenges = xml["remaining_challenges"].to_i
|
66
|
+
@remaining_timeouts = xml["remaining_timeouts"].to_i
|
67
|
+
@quarters = xml.xpath("scoring/quarter").map do |quarter|
|
68
|
+
quarter["points"].to_i
|
69
|
+
end
|
70
|
+
@quarters = @quarters.fill(0, @quarters.size, 4 - @quarters.size)
|
46
71
|
end
|
47
72
|
end
|
73
|
+
|
74
|
+
# Sum the score of each quarter
|
75
|
+
def score
|
76
|
+
@quarters.inject(:+)
|
77
|
+
end
|
48
78
|
end
|
49
79
|
|
50
80
|
BASE_URL = "http://api.sportsdatallc.org/nfl-%{access_level}%{version}"
|
@@ -77,6 +107,35 @@ module SportsDataApi
|
|
77
107
|
end
|
78
108
|
end
|
79
109
|
|
110
|
+
##
|
111
|
+
#
|
112
|
+
def self.boxscore(year, season, week, home, away, version = 1)
|
113
|
+
base_url = BASE_URL % { access_level: SportsDataApi.access_level, version: version }
|
114
|
+
season = season.to_s.upcase.to_sym
|
115
|
+
raise SportsDataApi::Nfl::Exception.new("#{season} is not a valid season") unless season?(season)
|
116
|
+
url = "#{base_url}/#{year}/#{season}/#{week}/#{home}/#{away}/boxscore.xml"
|
117
|
+
|
118
|
+
begin
|
119
|
+
# Perform the request
|
120
|
+
response = RestClient.get(url, params: { api_key: SportsDataApi.key })
|
121
|
+
|
122
|
+
# Load the XML and ignore namespaces in Nokogiri
|
123
|
+
boxscore = Nokogiri::XML(response.to_s)
|
124
|
+
boxscore.remove_namespaces!
|
125
|
+
|
126
|
+
return Game.new(boxscore.xpath("/game"))
|
127
|
+
rescue RestClient::Exception => e
|
128
|
+
message = if e.response.headers.key? :x_server_error
|
129
|
+
JSON.parse(error_json, { symbolize_names: true })[:message]
|
130
|
+
elsif e.response.headers.key? :x_mashery_error_code
|
131
|
+
e.response.headers[:x_mashery_error_code]
|
132
|
+
else
|
133
|
+
"The server did not specify a message"
|
134
|
+
end
|
135
|
+
raise SportsDataApi::Exception, message
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
80
139
|
##
|
81
140
|
# Check if the requested season is a valid
|
82
141
|
# NFL season type.
|
data/lib/sports_data_api.rb
CHANGED
@@ -5,11 +5,19 @@ require "time"
|
|
5
5
|
|
6
6
|
module SportsDataApi
|
7
7
|
def self.key
|
8
|
-
"garbage"
|
8
|
+
@key ||= "garbage"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.key=(new_key)
|
12
|
+
@key = new_key
|
9
13
|
end
|
10
14
|
|
11
15
|
def self.access_level
|
12
|
-
"t"
|
16
|
+
@access_level ||= "t"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.access_level=(new_level)
|
20
|
+
@access_level = new_level
|
13
21
|
end
|
14
22
|
|
15
23
|
autoload :Nfl, File.join(File.dirname(__FILE__), 'nfl')
|
data/spec/lib/nfl_spec.rb
CHANGED
@@ -44,6 +44,64 @@ describe SportsDataApi::Nfl do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
describe ".boxscore" do
|
48
|
+
it "creates a valid SportsData LLC url" do
|
49
|
+
SportsDataApi.stub(:key).and_return(key)
|
50
|
+
RestClient.stub(:get).with("http://api.sportsdatallc.org/nfl-t1/2012/REG/9/MIA/IND/boxscore.xml", params: { api_key: key }).and_return(boxscore_xml)
|
51
|
+
subject.boxscore(2012, :REG, 9, "MIA", "IND")
|
52
|
+
end
|
53
|
+
it "creates a SportsDataApi::Exception when there is no response from the api" do
|
54
|
+
error = RestClient::ResourceNotFound.new
|
55
|
+
error.stub_chain(:response, :headers).and_return(Hash.new)
|
56
|
+
SportsDataApi.stub(:key).and_return(key)
|
57
|
+
RestClient.stub(:get).and_raise(error)
|
58
|
+
expect { subject.boxscore(2999, :REG, 9, "MIA", "IND") }.to raise_error(SportsDataApi::Exception)
|
59
|
+
end
|
60
|
+
describe "returned data structures" do
|
61
|
+
let(:boxscore) { SportsDataApi::Nfl.boxscore(2012, :REG, 9, "IND", "MIA") }
|
62
|
+
before(:each) { RestClient.stub(:get).and_return(boxscore_xml) }
|
63
|
+
describe "SportsDataApi::Nfl::Game" do
|
64
|
+
subject { boxscore }
|
65
|
+
it { should be_an_instance_of(SportsDataApi::Nfl::Game) }
|
66
|
+
its(:id) { should eq "55d0b262-98ff-49fa-95c8-5ab8ec8cbd34" }
|
67
|
+
its(:scheduled) { should eq Time.new(2012, 11, 4, 18, 00, 00, "+00:00") }
|
68
|
+
its(:home) { should eq "IND" }
|
69
|
+
its(:away) { should eq "MIA" }
|
70
|
+
its(:status) { should eq "inprogress" }
|
71
|
+
its(:quarter) { should eq 2 }
|
72
|
+
its(:clock) { should eq "02:29" }
|
73
|
+
its(:home_team) { should be_an_instance_of(SportsDataApi::Nfl::Team) }
|
74
|
+
its(:away_team) { should be_an_instance_of(SportsDataApi::Nfl::Team) }
|
75
|
+
end
|
76
|
+
describe "SportsDataApi::Nfl::Team" do
|
77
|
+
describe "home team" do
|
78
|
+
subject { boxscore.home_team }
|
79
|
+
it { should be_an_instance_of(SportsDataApi::Nfl::Team) }
|
80
|
+
its(:id) { should eq "IND" }
|
81
|
+
its(:name) { should eq "Colts" }
|
82
|
+
its(:market) { should eq "Indianapolis" }
|
83
|
+
its(:remaining_challenges) { should eq 1 }
|
84
|
+
its(:remaining_timeouts) { should eq 2 }
|
85
|
+
its(:score) { should eq 10 }
|
86
|
+
its(:quarters) { should have(4).scores }
|
87
|
+
its(:quarters) { should include(7, 3, 0, 0) }
|
88
|
+
end
|
89
|
+
describe "away team" do
|
90
|
+
subject { boxscore.away_team }
|
91
|
+
it { should be_an_instance_of(SportsDataApi::Nfl::Team) }
|
92
|
+
its(:id) { should eq "MIA" }
|
93
|
+
its(:name) { should eq "Dolphins" }
|
94
|
+
its(:market) { should eq "Miami" }
|
95
|
+
its(:remaining_challenges) { should eq 2 }
|
96
|
+
its(:remaining_timeouts) { should eq 2 }
|
97
|
+
its(:score) { should eq 17 }
|
98
|
+
its(:quarters) { should have(4).scores }
|
99
|
+
its(:quarters) { should include(3, 14, 0, 0) }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
47
105
|
describe ".season?" do
|
48
106
|
context :PRE do
|
49
107
|
it { subject.season?(:PRE).should be_true }
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe SportsDataApi do
|
4
|
+
context "default values" do
|
5
|
+
its(:key) { should eq "garbage" }
|
6
|
+
its(:access_level) { should eq "t" }
|
7
|
+
end
|
8
|
+
context "user supplied values" do
|
9
|
+
let(:key) { "testing_key123" }
|
10
|
+
let(:level) { "b" }
|
11
|
+
before(:each) do
|
12
|
+
SportsDataApi.key = key
|
13
|
+
SportsDataApi.access_level = level
|
14
|
+
end
|
15
|
+
its(:key) { should eq key }
|
16
|
+
its(:access_level) { should eq level }
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,93 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!-- Generation started at Sun Nov 04 19:18:23 +0000 2012 -->
|
3
|
+
<game xmlns="http://feed.elasticstats.com/schema/nfl/boxscore-v1.0.xsd" id="55d0b262-98ff-49fa-95c8-5ab8ec8cbd34" scheduled="2012-11-04T18:00:00+00:00" home="IND" away="MIA" status="inprogress" quarter="2" clock="02:29">
|
4
|
+
<team id="IND" name="Colts" market="Indianapolis" remaining_challenges="1" remaining_timeouts="2">
|
5
|
+
<scoring points="10">
|
6
|
+
<quarter number="1" points="7"/>
|
7
|
+
<quarter number="2" points="3"/>
|
8
|
+
</scoring>
|
9
|
+
</team>
|
10
|
+
<team id="MIA" name="Dolphins" market="Miami" remaining_challenges="2" remaining_timeouts="2">
|
11
|
+
<scoring points="17">
|
12
|
+
<quarter number="1" points="3"/>
|
13
|
+
<quarter number="2" points="14"/>
|
14
|
+
</scoring>
|
15
|
+
</team>
|
16
|
+
<scoring_drives>
|
17
|
+
<drive sequence="1" clock="09:34" quarter="1" team="MIA">
|
18
|
+
<score type="fieldgoal" clock="04:21" quarter="1" points="3">
|
19
|
+
<summary>
|
20
|
+
<![CDATA[5-D.Carpenter 37 yards Field Goal is Good.]]>
|
21
|
+
</summary>
|
22
|
+
<links>
|
23
|
+
<link rel="summary" href="/2012/REG/9/MIA/IND/plays/f98b06d5-cfa1-475f-9d4b-b794599501d7.xml" type="application/xml"/>
|
24
|
+
</links>
|
25
|
+
</score>
|
26
|
+
</drive>
|
27
|
+
<drive sequence="2" clock="04:15" quarter="1" team="IND">
|
28
|
+
<score type="touchdown" clock=":53" quarter="1" points="6">
|
29
|
+
<summary>
|
30
|
+
<![CDATA[12-A.Luck complete to 87-R.Wayne. 87-R.Wayne runs 9 yards for a touchdown.]]>
|
31
|
+
</summary>
|
32
|
+
<links>
|
33
|
+
<link rel="summary" href="/2012/REG/9/MIA/IND/plays/000eabfe-4b36-40d7-ad4b-6f137ff9e615.xml" type="application/xml"/>
|
34
|
+
</links>
|
35
|
+
</score>
|
36
|
+
<score type="extrapoint" clock=":53" quarter="1" points="1">
|
37
|
+
<summary>
|
38
|
+
<![CDATA[4-A.Vinatieri extra point is good.]]>
|
39
|
+
</summary>
|
40
|
+
<links>
|
41
|
+
<link rel="summary" href="/2012/REG/9/MIA/IND/plays/429691e9-d940-4647-9e6e-99ebbe6e11e8.xml" type="application/xml"/>
|
42
|
+
</links>
|
43
|
+
</score>
|
44
|
+
</drive>
|
45
|
+
<drive sequence="3" clock=":47" quarter="1" team="MIA">
|
46
|
+
<score type="touchdown" clock="13:32" quarter="2" points="6">
|
47
|
+
<summary>
|
48
|
+
<![CDATA[17-R.Tannehill complete to 42-C.Clay. 42-C.Clay runs 31 yards for a touchdown.]]>
|
49
|
+
</summary>
|
50
|
+
<links>
|
51
|
+
<link rel="summary" href="/2012/REG/9/MIA/IND/plays/c34a88cd-6e5d-4072-98f8-1c8bb7103e6f.xml" type="application/xml"/>
|
52
|
+
</links>
|
53
|
+
</score>
|
54
|
+
<score type="extrapoint" clock="13:32" quarter="2" points="1">
|
55
|
+
<summary>
|
56
|
+
<![CDATA[5-D.Carpenter extra point is good.]]>
|
57
|
+
</summary>
|
58
|
+
<links>
|
59
|
+
<link rel="summary" href="/2012/REG/9/MIA/IND/plays/3b73e8a4-e44d-4b97-b198-42a2e8954c3c.xml" type="application/xml"/>
|
60
|
+
</links>
|
61
|
+
</score>
|
62
|
+
</drive>
|
63
|
+
<drive sequence="4" clock="13:25" quarter="2" team="IND">
|
64
|
+
<score type="fieldgoal" clock="09:42" quarter="2" points="3">
|
65
|
+
<summary>
|
66
|
+
<![CDATA[4-A.Vinatieri 23 yards Field Goal is Good.]]>
|
67
|
+
</summary>
|
68
|
+
<links>
|
69
|
+
<link rel="summary" href="/2012/REG/9/MIA/IND/plays/ceb258a0-ee6e-4b5b-8271-c42af4fa6a72.xml" type="application/xml"/>
|
70
|
+
</links>
|
71
|
+
</score>
|
72
|
+
</drive>
|
73
|
+
<drive sequence="5" clock="09:37" quarter="2" team="MIA">
|
74
|
+
<score type="touchdown" clock="05:16" quarter="2" points="6">
|
75
|
+
<summary>
|
76
|
+
<![CDATA[22-R.Bush runs 18 yards for a touchdown.]]>
|
77
|
+
</summary>
|
78
|
+
<links>
|
79
|
+
<link rel="summary" href="/2012/REG/9/MIA/IND/plays/595a0a07-1d75-40d5-9c3c-d52eb8d7f3a6.xml" type="application/xml"/>
|
80
|
+
</links>
|
81
|
+
</score>
|
82
|
+
<score type="extrapoint" clock="05:16" quarter="2" points="1">
|
83
|
+
<summary>
|
84
|
+
<![CDATA[5-D.Carpenter extra point is good.]]>
|
85
|
+
</summary>
|
86
|
+
<links>
|
87
|
+
<link rel="summary" href="/2012/REG/9/MIA/IND/plays/c9e83168-d56a-434b-8ba2-5673ab571806.xml" type="application/xml"/>
|
88
|
+
</links>
|
89
|
+
</score>
|
90
|
+
</drive>
|
91
|
+
</scoring_drives>
|
92
|
+
</game>
|
93
|
+
<!-- Generation finished at Sun Nov 04 19:18:24 +0000 2012 -->
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sports_data_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -191,8 +191,9 @@ files:
|
|
191
191
|
- lib/sports_data_api.rb
|
192
192
|
- lib/sports_data_api/version.rb
|
193
193
|
- spec/lib/nfl_spec.rb
|
194
|
+
- spec/lib/sports_data_api_spec.rb
|
194
195
|
- spec/spec_helper.rb
|
195
|
-
- spec/xml/
|
196
|
+
- spec/xml/boxscore.xml
|
196
197
|
- spec/xml/schedule.xml
|
197
198
|
- spec/xml/teams.xml
|
198
199
|
- sports_data_api.gemspec
|
@@ -210,7 +211,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
210
211
|
version: '0'
|
211
212
|
segments:
|
212
213
|
- 0
|
213
|
-
hash:
|
214
|
+
hash: 3259236789151721605
|
214
215
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
215
216
|
none: false
|
216
217
|
requirements:
|
@@ -219,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
219
220
|
version: '0'
|
220
221
|
segments:
|
221
222
|
- 0
|
222
|
-
hash:
|
223
|
+
hash: 3259236789151721605
|
223
224
|
requirements: []
|
224
225
|
rubyforge_project:
|
225
226
|
rubygems_version: 1.8.24
|
@@ -231,7 +232,8 @@ summary: SportsData’s comprehensive data coverage includes all major U.S. spor
|
|
231
232
|
from years of experience.
|
232
233
|
test_files:
|
233
234
|
- spec/lib/nfl_spec.rb
|
235
|
+
- spec/lib/sports_data_api_spec.rb
|
234
236
|
- spec/spec_helper.rb
|
235
|
-
- spec/xml/
|
237
|
+
- spec/xml/boxscore.xml
|
236
238
|
- spec/xml/schedule.xml
|
237
239
|
- spec/xml/teams.xml
|
data/spec/xml/game.xml
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<!-- Generation started at Fri Nov 02 02:27:02 +0000 2012 -->
|
3
|
-
<game xmlns="http://feed.elasticstats.com/schema/nfl/boxscore-v1.0.xsd" id="82748625-c24e-437a-b991-cab7f766c249" scheduled="2012-11-02T00:20:00+00:00" home="SD" away="KC" status="inprogress" quarter="3" clock=":53">
|
4
|
-
<team id="SD" name="Chargers" market="San Diego" remaining_challenges="2" remaining_timeouts="3">
|
5
|
-
<scoring points="10">
|
6
|
-
<quarter number="1" points="7"/>
|
7
|
-
<quarter number="2" points="3"/>
|
8
|
-
<quarter number="3" points="0"/>
|
9
|
-
</scoring>
|
10
|
-
</team>
|
11
|
-
<team id="KC" name="Chiefs" market="Kansas City" remaining_challenges="2" remaining_timeouts="3">
|
12
|
-
<scoring points="6">
|
13
|
-
<quarter number="1" points="0"/>
|
14
|
-
<quarter number="2" points="3"/>
|
15
|
-
<quarter number="3" points="3"/>
|
16
|
-
</scoring>
|
17
|
-
</team>
|
18
|
-
<scoring_drives>
|
19
|
-
<drive sequence="1" clock="15:00" quarter="1" team="SD">
|
20
|
-
<score type="touchdown" clock="10:31" quarter="1" points="6">
|
21
|
-
<summary>
|
22
|
-
<![CDATA[17-P.Rivers complete to 85-A.Gates. 85-A.Gates runs 14 yards for a touchdown.]]>
|
23
|
-
</summary>
|
24
|
-
<links>
|
25
|
-
<link rel="summary" href="/2012/REG/9/KC/SD/plays/fb5c3b6f-be0a-4f3f-819e-f1134204e312.xml" type="application/xml"/>
|
26
|
-
</links>
|
27
|
-
</score>
|
28
|
-
<score type="extrapoint" clock="10:24" quarter="1" points="1">
|
29
|
-
<summary>
|
30
|
-
<![CDATA[9-N.Novak extra point is good.]]>
|
31
|
-
</summary>
|
32
|
-
<links>
|
33
|
-
<link rel="summary" href="/2012/REG/9/KC/SD/plays/cecb31f7-febb-470d-b833-b32b9411fa95.xml" type="application/xml"/>
|
34
|
-
</links>
|
35
|
-
</score>
|
36
|
-
</drive>
|
37
|
-
<drive sequence="2" clock="02:18" quarter="1" team="SD">
|
38
|
-
<score type="fieldgoal" clock="09:13" quarter="2" points="3">
|
39
|
-
<summary>
|
40
|
-
<![CDATA[9-N.Novak 25 yards Field Goal is Good.]]>
|
41
|
-
</summary>
|
42
|
-
<links>
|
43
|
-
<link rel="summary" href="/2012/REG/9/KC/SD/plays/2036be2d-f58b-4290-9e45-2e171c37588e.xml" type="application/xml"/>
|
44
|
-
</links>
|
45
|
-
</score>
|
46
|
-
</drive>
|
47
|
-
<drive sequence="3" clock="09:10" quarter="2" team="KC">
|
48
|
-
<score type="fieldgoal" clock="02:56" quarter="2" points="3">
|
49
|
-
<summary>
|
50
|
-
<![CDATA[6-R.Succop 49 yards Field Goal is Good.]]>
|
51
|
-
</summary>
|
52
|
-
<links>
|
53
|
-
<link rel="summary" href="/2012/REG/9/KC/SD/plays/68de7467-99b7-4e87-939d-7dc9b90c0462.xml" type="application/xml"/>
|
54
|
-
</links>
|
55
|
-
</score>
|
56
|
-
</drive>
|
57
|
-
<drive sequence="4" clock="07:09" quarter="3" team="KC">
|
58
|
-
<score type="fieldgoal" clock=":58" quarter="3" points="3">
|
59
|
-
<summary>
|
60
|
-
<![CDATA[6-R.Succop 41 yards Field Goal is Good.]]>
|
61
|
-
</summary>
|
62
|
-
<links>
|
63
|
-
<link rel="summary" href="/2012/REG/9/KC/SD/plays/35b31551-62ce-430b-8d1e-4153a1e81483.xml" type="application/xml"/>
|
64
|
-
</links>
|
65
|
-
</score>
|
66
|
-
</drive>
|
67
|
-
</scoring_drives>
|
68
|
-
</game>
|
69
|
-
<!-- Generation finished at Fri Nov 02 02:27:02 +0000 2012 -->
|