nfl_data 0.0.11 → 0.1.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.
- checksums.yaml +5 -5
- data/.envrc +7 -0
- data/.gitignore +3 -3
- data/.rspec +1 -0
- data/.standard.yml +2 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +1 -1
- data/README.md +94 -22
- data/Rakefile +5 -6
- data/bin/console +1 -1
- data/bin/setup +8 -0
- data/lib/nfl_data.rb +9 -18
- data/lib/nfl_data/api/player.rb +8 -26
- data/lib/nfl_data/api/schedule.rb +19 -0
- data/lib/nfl_data/api/statline.rb +8 -22
- data/lib/nfl_data/game.rb +5 -0
- data/lib/nfl_data/my_sports_feeds/client.rb +45 -0
- data/lib/nfl_data/my_sports_feeds/players_feed.rb +18 -0
- data/lib/nfl_data/my_sports_feeds/seasonal_games_feed.rb +22 -0
- data/lib/nfl_data/my_sports_feeds/weekly_player_gamelogs.rb +21 -0
- data/lib/nfl_data/parsers/player_parser.rb +21 -77
- data/lib/nfl_data/parsers/schedule_parser.rb +20 -0
- data/lib/nfl_data/parsers/statline_parser.rb +27 -71
- data/lib/nfl_data/player.rb +13 -0
- data/lib/nfl_data/schedule.rb +12 -0
- data/lib/nfl_data/statline.rb +20 -0
- data/lib/nfl_data/version.rb +1 -1
- data/nfl_data.gemspec +23 -22
- data/spec/api/player_spec.rb +24 -0
- data/spec/api/schedule_spec.rb +20 -0
- data/spec/api/statline_spec.rb +31 -0
- data/spec/models/game_spec.rb +15 -0
- data/spec/models/player_spec.rb +42 -0
- data/spec/models/schedule_spec.rb +17 -0
- data/spec/models/statline_spec.rb +63 -0
- data/spec/my_sports_feeds/client_spec.rb +17 -0
- data/spec/my_sports_feeds/players_feed_spec.rb +50 -0
- data/spec/my_sports_feeds/seasonal_games_feed_spec.rb +41 -0
- data/spec/my_sports_feeds/weekly_player_gamelogs_spec.rb +144 -0
- data/spec/parsers/player_parser_spec.rb +63 -0
- data/spec/parsers/schedule_parser_spec.rb +41 -0
- data/spec/parsers/statline_parser_spec.rb +68 -0
- data/spec/spec_helper.rb +55 -0
- metadata +98 -52
- data/.codeclimate.yml +0 -5
- data/.rubocop.yml +0 -2
- data/lib/nfl_data/api/team.rb +0 -23
- data/lib/nfl_data/models/player.rb +0 -20
- data/lib/nfl_data/models/statline.rb +0 -21
- data/lib/nfl_data/models/team.rb +0 -38
- data/lib/nfl_data/parsers/parser_helper.rb +0 -6
- data/lib/nfl_data/parsers/team_parser.rb +0 -94
- data/test/nfl_data/api/player_test.rb +0 -25
- data/test/nfl_data/api/statline_test.rb +0 -21
- data/test/nfl_data/api/team_test.rb +0 -13
- data/test/nfl_data/models/player_test.rb +0 -79
- data/test/nfl_data/models/statline_test.rb +0 -121
- data/test/nfl_data/models/team_test.rb +0 -43
- data/test/nfl_data/parsers/player_parser_test.rb +0 -86
- data/test/nfl_data/parsers/statline_parser_test.rb +0 -34
- data/test/nfl_data/parsers/team_parser_test.rb +0 -41
- data/test/test_helper.rb +0 -14
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NflData
|
4
|
+
module MySportsFeeds
|
5
|
+
class PlayersFeed
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
def initialize(client: MySportsFeeds::Client.new)
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def feed(position: nil)
|
13
|
+
params = {position: position}
|
14
|
+
client.get(endpoint: "players", params: params)["players"]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NflData
|
4
|
+
module MySportsFeeds
|
5
|
+
class SeasonalGamesFeed
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
def initialize(client: MySportsFeeds::Client.new)
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def feed(season_start_year:)
|
13
|
+
response = client.get(endpoint: "#{season_slug(season_start_year)}/games")
|
14
|
+
response["games"].map { |data| data["schedule"] }
|
15
|
+
end
|
16
|
+
|
17
|
+
def season_slug(year)
|
18
|
+
"#{year}-#{year + 1}-regular"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NflData
|
4
|
+
module MySportsFeeds
|
5
|
+
class WeeklyPlayerGamelogs
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
def initialize(client: MySportsFeeds::Client.new)
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def feed(season_start_year:, week:)
|
13
|
+
client.get(endpoint: "#{season_slug(season_start_year)}/week/#{week}/player_gamelogs")["gamelogs"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def season_slug(year)
|
17
|
+
"#{year}-#{year + 1}-regular"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,84 +1,28 @@
|
|
1
|
-
|
2
|
-
class PlayerParser
|
3
|
-
include ParserHelper
|
4
|
-
|
5
|
-
attr_reader :base_url
|
1
|
+
# frozen_string_literal: true
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
quarterbacks: get('quarterback'),
|
15
|
-
runningbacks: get('runningback'),
|
16
|
-
wide_receivers: get('widereceiver'),
|
17
|
-
tight_ends: get('tightend')
|
18
|
-
}
|
19
|
-
else
|
20
|
-
# We have to remove the '_' and 's' because the NFL url has
|
21
|
-
# singular position names and all are one word.
|
22
|
-
{ position => get(position.to_s.gsub(/s|_/, '')) }
|
3
|
+
module NflData
|
4
|
+
module Parsers
|
5
|
+
class PlayerParser
|
6
|
+
def parse(player_data:)
|
7
|
+
player_data.map { |data| data["player"] }.map do |data|
|
8
|
+
init_player(data)
|
9
|
+
end
|
23
10
|
end
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
11
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
12
|
+
private
|
13
|
+
|
14
|
+
def init_player(data)
|
15
|
+
Player.new(
|
16
|
+
first_name: data["firstName"],
|
17
|
+
last_name: data["lastName"],
|
18
|
+
full_name: "#{data["firstName"]} #{data["lastName"]}".chomp,
|
19
|
+
position: data["primaryPosition"],
|
20
|
+
number: data["jerseyNumber"],
|
21
|
+
team: data.dig("currentTeam", "abbreviation").to_s,
|
22
|
+
msf_player_id: data["id"],
|
23
|
+
image_source: data["officialImageSrc"].to_s
|
24
|
+
)
|
40
25
|
end
|
41
|
-
|
42
|
-
return_val.flatten.map(&:to_hash)
|
43
|
-
end
|
44
|
-
|
45
|
-
def update_or_create_players(url)
|
46
|
-
doc = open(url) { |f| Nokogiri(f) }
|
47
|
-
|
48
|
-
# NFL.com stores players in 2 types of rows.
|
49
|
-
# css class = odd or even.
|
50
|
-
all_rows = doc.search('tr.odd') + doc.search('tr.even')
|
51
|
-
players = all_rows.map { |row| parse_player_from_row(row.search('td')) }
|
52
|
-
[players.count, players]
|
53
|
-
end
|
54
|
-
|
55
|
-
def parse_player_from_row(elements)
|
56
|
-
# Get NFL.com Unique player id
|
57
|
-
nfl_player_id = elements[2].to_s.split('/')[3]
|
58
|
-
|
59
|
-
# player id is the only one with complicated parsing so we
|
60
|
-
# can just extract the inner text out of the rest of the elements
|
61
|
-
elements = elements.map(&:inner_text).map(&:strip)
|
62
|
-
names = elements[2].split(',').map(&:strip).reverse
|
63
|
-
|
64
|
-
Player.new(
|
65
|
-
nfl_player_id: nfl_player_id,
|
66
|
-
position: elements[0],
|
67
|
-
number: elements[1],
|
68
|
-
status: elements[3],
|
69
|
-
team: make_jacksonville_abbreviation_consistent(elements[12]),
|
70
|
-
first_name: names[0],
|
71
|
-
last_name: names[1],
|
72
|
-
full_name: names.join(' '),
|
73
|
-
picture_link: get_picture_link(nfl_player_id, names[0], names[1])
|
74
|
-
)
|
75
|
-
end
|
76
|
-
|
77
|
-
def get_picture_link(nfl_player_id, first_name, last_name)
|
78
|
-
return nil if ENV['NFL_DATA_ENV'] == 'test'
|
79
|
-
url = "http://www.nfl.com/player/#{first_name.gsub(/\s/, '')}#{last_name.gsub(/\s/, '')}/#{nfl_player_id}/profile"
|
80
|
-
doc = open(url) { |f| Nokogiri(f) }
|
81
|
-
doc.search('div.player-photo img').first.attributes['src'].value
|
82
26
|
end
|
83
27
|
end
|
84
28
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module NflData
|
2
|
+
module Parsers
|
3
|
+
class ScheduleParser
|
4
|
+
def parse(schedule_data:)
|
5
|
+
Schedule.new(games: schedule_data.map { |data| init_game(data) })
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def init_game(data)
|
11
|
+
Game.new(
|
12
|
+
week: data["week"],
|
13
|
+
home_team: data.dig("homeTeam", "abbreviation"),
|
14
|
+
away_team: data.dig("awayTeam", "abbreviation"),
|
15
|
+
start_time: data["startTime"]
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,78 +1,34 @@
|
|
1
|
-
|
2
|
-
class StatlineParser
|
3
|
-
def initialize
|
4
|
-
end
|
5
|
-
|
6
|
-
def get(week, year, stat_type)
|
7
|
-
case stat_type
|
8
|
-
when :passing
|
9
|
-
grab_week(week, year, 'Passing')
|
10
|
-
when :rushing
|
11
|
-
grab_week(week, year, 'Rushing')
|
12
|
-
when :receiving
|
13
|
-
grab_week(week, year, 'Receiving')
|
14
|
-
end
|
15
|
-
end
|
1
|
+
# frozen_string_literal: true
|
16
2
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
begin
|
25
|
-
open(old_url) do |resp|
|
26
|
-
new_url = resp.base_uri.to_s
|
3
|
+
module NflData
|
4
|
+
module Parsers
|
5
|
+
class StatlineParser
|
6
|
+
def parse(statline_data:)
|
7
|
+
statline_data.map do |data|
|
8
|
+
init_statline(data)
|
27
9
|
end
|
28
|
-
rescue
|
29
|
-
return nil
|
30
10
|
end
|
31
11
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
statline.week = weeknum
|
53
|
-
statline.year = year
|
54
|
-
|
55
|
-
if stat_type == 'Rushing'
|
56
|
-
statline.rush_atts = elements[4].inner_text.strip
|
57
|
-
statline.rush_yards = elements[5].inner_text.strip
|
58
|
-
statline.rush_tds = elements[7].inner_text.strip
|
59
|
-
statline.fumbles = elements[8].inner_text.strip
|
60
|
-
elsif stat_type == 'Passing'
|
61
|
-
statline.pass_comp = elements[4].inner_text.strip
|
62
|
-
statline.pass_att = elements[5].inner_text.strip
|
63
|
-
statline.pass_yards = elements[6].inner_text.strip
|
64
|
-
statline.pass_tds = elements[7].inner_text.strip
|
65
|
-
statline.ints = elements[8].inner_text.strip
|
66
|
-
statline.qb_rating = elements[11].inner_text.strip
|
67
|
-
statline.fumbles = elements [10].inner_text.strip
|
68
|
-
elsif stat_type == 'Receiving'
|
69
|
-
statline.receptions = elements[4].inner_text.strip
|
70
|
-
statline.rec_yards = elements[5].inner_text.strip
|
71
|
-
statline.rec_tds = elements[7].inner_text.strip
|
72
|
-
statline.fumbles = elements[8].inner_text.strip
|
73
|
-
end
|
74
|
-
|
75
|
-
statline.to_hash
|
12
|
+
private
|
13
|
+
|
14
|
+
def init_statline(data)
|
15
|
+
Statline.new(
|
16
|
+
rush_atts: data.dig("stats", "rushing", "rushAttempts") || 0,
|
17
|
+
rush_yards: data.dig("stats", "rushing", "rushYards") || 0,
|
18
|
+
rush_tds: data.dig("stats", "rushing", "rushTD") || 0,
|
19
|
+
fumbles: data.dig("stats", "fumbles", "fumbles") || 0,
|
20
|
+
pass_comp: data.dig("stats", "passing", "passCompletions") || 0,
|
21
|
+
pass_att: data.dig("stats", "passing", "passAttempts") || 0,
|
22
|
+
pass_yards: data.dig("stats", "passing", "passYards") || 0,
|
23
|
+
pass_tds: data.dig("stats", "passing", "passTD") || 0,
|
24
|
+
ints: data.dig("stats", "interceptions", "interceptions") || 0,
|
25
|
+
qb_rating: data.dig("stats", "passing", "qbRating") || 0,
|
26
|
+
receptions: data.dig("stats", "receiving", "receptions") || 0,
|
27
|
+
rec_yards: data.dig("stats", "receiving", "recYards") || 0,
|
28
|
+
rec_tds: data.dig("stats", "receiving", "recTD") || 0,
|
29
|
+
msf_game_id: data.dig("game", "id"),
|
30
|
+
msf_player_id: data.dig("player", "id")
|
31
|
+
)
|
76
32
|
end
|
77
33
|
end
|
78
34
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module NflData
|
2
|
+
Statline = Struct.new(
|
3
|
+
:rush_atts,
|
4
|
+
:rush_yards,
|
5
|
+
:rush_tds,
|
6
|
+
:fumbles,
|
7
|
+
:pass_comp,
|
8
|
+
:pass_att,
|
9
|
+
:pass_yards,
|
10
|
+
:pass_tds,
|
11
|
+
:ints,
|
12
|
+
:qb_rating,
|
13
|
+
:receptions,
|
14
|
+
:rec_yards,
|
15
|
+
:rec_tds,
|
16
|
+
:msf_game_id,
|
17
|
+
:msf_player_id,
|
18
|
+
keyword_init: true
|
19
|
+
)
|
20
|
+
end
|
data/lib/nfl_data/version.rb
CHANGED
data/nfl_data.gemspec
CHANGED
@@ -1,30 +1,31 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
|
5
|
-
require
|
4
|
+
require "nfl_data/version"
|
6
5
|
|
7
6
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name
|
9
|
-
spec.version
|
10
|
-
spec.authors
|
11
|
-
spec.email
|
12
|
-
spec.homepage
|
13
|
-
spec.license
|
14
|
-
spec.summary
|
15
|
-
spec.description
|
7
|
+
spec.name = "nfl_data"
|
8
|
+
spec.version = NflData::VERSION
|
9
|
+
spec.authors = ["thetizzo"]
|
10
|
+
spec.email = ["j.m.taylor1@gmail.com"]
|
11
|
+
spec.homepage = "https://github.com/thetizzo/nfl_data"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.summary = "Parse NFL data like a boss"
|
14
|
+
spec.description = "Parse NFL data like a boss"
|
16
15
|
|
17
|
-
spec.files
|
18
|
-
spec.executables
|
19
|
-
spec.test_files
|
20
|
-
spec.require_paths = [
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
21
20
|
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
21
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3.9"
|
23
|
+
spec.add_development_dependency "rspec-resembles_json_matchers", "~> 0.9"
|
24
|
+
spec.add_development_dependency "vcr", "~> 4.0.0"
|
25
|
+
spec.add_development_dependency "webmock", "~> 3.8"
|
26
|
+
spec.add_development_dependency "standard", "~> 0.5"
|
28
27
|
|
29
|
-
spec.add_dependency
|
28
|
+
spec.add_dependency "nokogiri", "~> 1.10"
|
29
|
+
spec.add_dependency "typhoeus", "~> 1.4"
|
30
|
+
spec.add_dependency "zeitwerk", "~> 2.4"
|
30
31
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe NflData::Api::Player do
|
4
|
+
subject { described_class.new }
|
5
|
+
|
6
|
+
it "provides the players in json" do
|
7
|
+
VCR.use_cassette("msf_players") do
|
8
|
+
expect(JSON.parse(subject.players)).to resemble_json(
|
9
|
+
"players": [
|
10
|
+
{
|
11
|
+
"first_name": "B",
|
12
|
+
"last_name": "2Anger2",
|
13
|
+
"full_name": "B 2Anger2",
|
14
|
+
"position": "LB",
|
15
|
+
"number": 19,
|
16
|
+
"team": "JAX",
|
17
|
+
"msf_player_id": 19407,
|
18
|
+
"image_source": "google.com"
|
19
|
+
}
|
20
|
+
]
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe NflData::Api::Schedule do
|
4
|
+
subject { described_class.new }
|
5
|
+
|
6
|
+
it "provides the season in json" do
|
7
|
+
VCR.use_cassette("msf_seasonal_games") do
|
8
|
+
expect(JSON.parse(subject.season(year: 2020))).to resemble_json(
|
9
|
+
"games": [
|
10
|
+
{
|
11
|
+
"week": 1,
|
12
|
+
"away_team": "KC",
|
13
|
+
"home_team": "DEN",
|
14
|
+
"start_time": Time.now
|
15
|
+
}
|
16
|
+
]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|