sports_data_api 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -1,3 +1,3 @@
1
1
  module SportsDataApi
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -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
@@ -15,6 +15,10 @@ def schedule_xml
15
15
  load_xml("schedule")
16
16
  end
17
17
 
18
+ def boxscore_xml
19
+ load_xml("boxscore")
20
+ end
21
+
18
22
  RSpec.configure do |config|
19
23
  config.treat_symbols_as_metadata_keys_with_true_values = true
20
24
  config.run_all_when_everything_filtered = true
@@ -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.1
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/game.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: -1347555009981213205
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: -1347555009981213205
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/game.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 -->