sports_data_api 0.0.1
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/.gitignore +25 -0
- data/.rspec +2 -0
- data/.rvmrc +38 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/Guardfile +9 -0
- data/LICENSE.md +24 -0
- data/README.md +57 -0
- data/Rakefile +7 -0
- data/lib/nfl.rb +90 -0
- data/lib/sports_data_api/version.rb +3 -0
- data/lib/sports_data_api.rb +18 -0
- data/spec/lib/nfl_spec.rb +67 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/xml/game.xml +69 -0
- data/spec/xml/schedule.xml +4067 -0
- data/spec/xml/teams.xml +119 -0
- data/sports_data_api.gemspec +29 -0
- metadata +237 -0
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
|
19
|
+
# YARD artifacts
|
20
|
+
.yardoc
|
21
|
+
_yardoc
|
22
|
+
doc/
|
23
|
+
|
24
|
+
# OSX artifacts
|
25
|
+
.DS_Store
|
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
|
7
|
+
# Only full ruby name is supported here, for short names use:
|
8
|
+
# echo "rvm use 1.9.3" > .rvmrc
|
9
|
+
environment_id="ruby-1.9.3-p194@sports-data"
|
10
|
+
|
11
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
12
|
+
# rvmrc_rvm_version="1.14.11 (stable)" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
|
18
|
+
# First we attempt to load the desired environment directly from the environment
|
19
|
+
# file. This is very fast and efficient compared to running through the entire
|
20
|
+
# CLI and selector. If you want feedback on which environment was used then
|
21
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
22
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
|
23
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
24
|
+
then
|
25
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
26
|
+
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
|
27
|
+
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
|
28
|
+
if [[ $- == *i* ]] # check for interactive shells
|
29
|
+
then echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
|
30
|
+
else echo "Using: $GEM_HOME" # don't use colors in non-interactive shells
|
31
|
+
fi
|
32
|
+
else
|
33
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
34
|
+
rvm --create use "$environment_id" || {
|
35
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
36
|
+
return 1
|
37
|
+
}
|
38
|
+
fi
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2012 Ryan Lovelett ( [@rlovelett](http://twitter.com/#!/rlovelett) )
|
3
|
+
=================================================================================================
|
4
|
+
|
5
|
+
The "SportsDataApi" RubyGem is released under the **MIT LICENSE**
|
6
|
+
----------------------------------------------------------
|
7
|
+
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
10
|
+
in the Software without restriction, including without limitation the rights
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
13
|
+
furnished to do so, subject to the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be included in
|
16
|
+
all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
24
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# SportsDataApi [](https://travis-ci.org/RLovelett/sports_data_api)
|
2
|
+
|
3
|
+
SportsDataApi is an attempt to make a Ruby interface to the
|
4
|
+
[SportsData](http://www.sportsdatallc.com/) API. The goal is to
|
5
|
+
eventaully support the full API. Pull requests that extend the API
|
6
|
+
support are always welcome.
|
7
|
+
|
8
|
+
[SportsData](http://www.sportsdatallc.com/)’s comprehensive data coverage includes all major U.S. sports,
|
9
|
+
plus hundreds of leagues throughout the world. Their live game analysts
|
10
|
+
capture every possible event of every game, in real time and with
|
11
|
+
accuracy standards developed from years of experience.
|
12
|
+
|
13
|
+
## Author
|
14
|
+
|
15
|
+
[Ryan Lovelett](http://ryan.lovelett.me/) ( [@rlovelett](http://twitter.com/#!/rlovelett) )
|
16
|
+
|
17
|
+
Drop me a message for any questions, suggestions, requests, bugs or
|
18
|
+
submit them to the [issue
|
19
|
+
log](https://github.com/rlovelett/sports_data_api/issues).
|
20
|
+
|
21
|
+
## API Support
|
22
|
+
|
23
|
+
* [NFL](http://developer.sportsdatallc.com/docs/NFL_API)
|
24
|
+
* TODO
|
25
|
+
1. [MLB](http://developer.sportsdatallc.com/docs/MLB_API)
|
26
|
+
2. [NHL](http://developer.sportsdatallc.com/docs/NHL_API)
|
27
|
+
3. [NBA](http://developer.sportsdatallc.com/docs/NBA_API)
|
28
|
+
4. [NCAA Basketball](http://developer.sportsdatallc.com/docs/NCAA_Mens_Backetball)
|
29
|
+
5. [NCAA Football](http://developer.sportsdatallc.com/docs/NCAA_Football_API)
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
Add this line to your application's Gemfile:
|
34
|
+
|
35
|
+
gem 'sports_data_api'
|
36
|
+
|
37
|
+
And then execute:
|
38
|
+
|
39
|
+
$ bundle
|
40
|
+
|
41
|
+
Or install it yourself as:
|
42
|
+
|
43
|
+
$ gem install sports_data_api
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
The specs for this Gem should give you some idea of how to make use of
|
48
|
+
the API. For now they will be the usage information. As always Pull
|
49
|
+
Requests for better documentation are always welcome.
|
50
|
+
|
51
|
+
## Contributing
|
52
|
+
|
53
|
+
1. Fork it
|
54
|
+
2. Create a topic branch (`git checkout -b topic`)
|
55
|
+
3. Make your changes
|
56
|
+
4. Squash your changes into one commit
|
57
|
+
5. Create new Pull Request against this squashed commit
|
data/Rakefile
ADDED
data/lib/nfl.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
module SportsDataApi
|
2
|
+
module Nfl
|
3
|
+
|
4
|
+
class Exception < ::Exception
|
5
|
+
end
|
6
|
+
|
7
|
+
class Season
|
8
|
+
attr_reader :year, :type, :weeks
|
9
|
+
|
10
|
+
def initialize(xml)
|
11
|
+
@weeks = []
|
12
|
+
if xml.is_a? Nokogiri::XML::NodeSet
|
13
|
+
@year = xml.first["season"].to_i
|
14
|
+
@type = xml.first["type"].to_sym
|
15
|
+
@weeks = xml.first.xpath("week").map do |week_xml|
|
16
|
+
Week.new(week_xml)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Week
|
23
|
+
attr_reader :number, :games
|
24
|
+
|
25
|
+
def initialize(xml)
|
26
|
+
@games = []
|
27
|
+
if xml.is_a? Nokogiri::XML::Element
|
28
|
+
@number = xml["week"].to_i
|
29
|
+
@games = xml.xpath("game").map do |game_xml|
|
30
|
+
Game.new(game_xml)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Game
|
37
|
+
attr_reader :id, :scheduled, :home, :away, :status
|
38
|
+
|
39
|
+
def initialize(xml)
|
40
|
+
if xml.is_a? Nokogiri::XML::Element
|
41
|
+
@id = xml["id"]
|
42
|
+
@scheduled = Time.parse xml["scheduled"]
|
43
|
+
@home = xml["home"]
|
44
|
+
@away = xml["away"]
|
45
|
+
@status = xml["status"]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
BASE_URL = "http://api.sportsdatallc.org/nfl-%{access_level}%{version}"
|
51
|
+
SEASONS = [:PRE, :REG, :PST]
|
52
|
+
|
53
|
+
def self.schedule(year, season, version = 1)
|
54
|
+
base_url = BASE_URL % { access_level: SportsDataApi.access_level, version: version }
|
55
|
+
season = season.to_s.upcase.to_sym
|
56
|
+
raise SportsDataApi::Nfl::Exception.new("#{season} is not a valid season") unless season?(season)
|
57
|
+
url = "#{base_url}/#{year}/#{season}/schedule.xml"
|
58
|
+
|
59
|
+
begin
|
60
|
+
# Perform the request
|
61
|
+
response = RestClient.get(url, params: { api_key: SportsDataApi.key })
|
62
|
+
|
63
|
+
# Load the XML and ignore namespaces in Nokogiri
|
64
|
+
schedule = Nokogiri::XML(response.to_s)
|
65
|
+
schedule.remove_namespaces!
|
66
|
+
|
67
|
+
return Season.new(schedule.xpath("/season"))
|
68
|
+
rescue RestClient::Exception => e
|
69
|
+
message = if e.response.headers.key? :x_server_error
|
70
|
+
JSON.parse(error_json, { symbolize_names: true })[:message]
|
71
|
+
elsif e.response.headers.key? :x_mashery_error_code
|
72
|
+
e.response.headers[:x_mashery_error_code]
|
73
|
+
else
|
74
|
+
"The server did not specify a message"
|
75
|
+
end
|
76
|
+
raise SportsDataApi::Exception, message
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Check if the requested season is a valid
|
82
|
+
# NFL season type.
|
83
|
+
#
|
84
|
+
# The only valid types are: :PRE, :REG, :PST
|
85
|
+
def self.season?(season)
|
86
|
+
SEASONS.include?(season)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "sports_data_api/version"
|
2
|
+
require "nokogiri"
|
3
|
+
require "rest_client"
|
4
|
+
require "time"
|
5
|
+
|
6
|
+
module SportsDataApi
|
7
|
+
def self.key
|
8
|
+
"garbage"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.access_level
|
12
|
+
"t"
|
13
|
+
end
|
14
|
+
|
15
|
+
autoload :Nfl, File.join(File.dirname(__FILE__), 'nfl')
|
16
|
+
|
17
|
+
class Exception < ::Exception; end
|
18
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SportsDataApi::Nfl do
|
4
|
+
let(:key) { "1234567890abcdef" }
|
5
|
+
describe ".schedule" do
|
6
|
+
it "creates a valid Sports Data LLC url" do
|
7
|
+
SportsDataApi.stub(:key).and_return(key)
|
8
|
+
RestClient.stub(:get).with("http://api.sportsdatallc.org/nfl-t1/2012/REG/schedule.xml", params: { api_key: key }).and_return(schedule_xml)
|
9
|
+
subject.schedule(2012, :REG)
|
10
|
+
end
|
11
|
+
it "creates a SportsDataApi::Exception when there is no response from the api" do
|
12
|
+
error = RestClient::ResourceNotFound.new
|
13
|
+
error.stub_chain(:response, :headers).and_return(Hash.new)
|
14
|
+
SportsDataApi.stub(:key).and_return(key)
|
15
|
+
RestClient.stub(:get).and_raise(error)
|
16
|
+
expect { subject.schedule(2999, :REG) }.to raise_error(SportsDataApi::Exception)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "returned data structures" do
|
20
|
+
before(:each) { RestClient.stub(:get).and_return(schedule_xml) }
|
21
|
+
let(:season) { SportsDataApi::Nfl.schedule(2012, :REG) }
|
22
|
+
describe "SportsDataApi::Nfl::Season" do
|
23
|
+
subject { season }
|
24
|
+
it { should be_an_instance_of(SportsDataApi::Nfl::Season) }
|
25
|
+
its(:year) { should eq 2012 }
|
26
|
+
its(:type) { should eq :REG }
|
27
|
+
its(:weeks) { should have(17).weeks }
|
28
|
+
end
|
29
|
+
describe "SportsDataApi::Nfl::Week" do
|
30
|
+
subject { season.weeks.first }
|
31
|
+
it { should be_an_instance_of(SportsDataApi::Nfl::Week) }
|
32
|
+
its(:number) { should eq 1 }
|
33
|
+
its(:games) { should have(16).games }
|
34
|
+
end
|
35
|
+
describe "SportsDataApi::Nfl::Game" do
|
36
|
+
subject { season.weeks.first.games.first }
|
37
|
+
it { should be_an_instance_of(SportsDataApi::Nfl::Game) }
|
38
|
+
its(:id) { should eq "8c0bce5a-7ca2-41e5-9838-d1b8c356ddc3" }
|
39
|
+
its(:scheduled) { should eq Time.new(2012, 9, 5, 19, 30, 00, "-05:00") }
|
40
|
+
its(:home) { should eq "NYG" }
|
41
|
+
its(:away) { should eq "DAL" }
|
42
|
+
its(:status) { should eq "closed" }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe ".season?" do
|
48
|
+
context :PRE do
|
49
|
+
it { subject.season?(:PRE).should be_true }
|
50
|
+
end
|
51
|
+
context :REG do
|
52
|
+
it { subject.season?(:REG).should be_true }
|
53
|
+
end
|
54
|
+
context :PST do
|
55
|
+
it { subject.season?(:PST).should be_true }
|
56
|
+
end
|
57
|
+
context :pre do
|
58
|
+
it { subject.season?(:pre).should be_false }
|
59
|
+
end
|
60
|
+
context :reg do
|
61
|
+
it { subject.season?(:reg).should be_false }
|
62
|
+
end
|
63
|
+
context :pst do
|
64
|
+
it { subject.season?(:pst).should be_false }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "sports_data_api"
|
2
|
+
|
3
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
4
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
5
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
6
|
+
# loaded once.
|
7
|
+
#
|
8
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
9
|
+
|
10
|
+
def load_xml(filename)
|
11
|
+
File.read("#{File.dirname(__FILE__)}/xml/#{filename}.xml")
|
12
|
+
end
|
13
|
+
|
14
|
+
def schedule_xml
|
15
|
+
load_xml("schedule")
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
20
|
+
config.run_all_when_everything_filtered = true
|
21
|
+
config.filter_run :focus
|
22
|
+
|
23
|
+
# Run specs in random order to surface order dependencies. If you find an
|
24
|
+
# order dependency and want to debug it, you can fix the order by providing
|
25
|
+
# the seed, which is printed after each run.
|
26
|
+
# --seed 1234
|
27
|
+
config.order = 'random'
|
28
|
+
end
|
data/spec/xml/game.xml
ADDED
@@ -0,0 +1,69 @@
|
|
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 -->
|