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.
Files changed (63) hide show
  1. checksums.yaml +5 -5
  2. data/.envrc +7 -0
  3. data/.gitignore +3 -3
  4. data/.rspec +1 -0
  5. data/.standard.yml +2 -0
  6. data/.travis.yml +6 -0
  7. data/CHANGELOG.md +11 -0
  8. data/Gemfile +1 -1
  9. data/README.md +94 -22
  10. data/Rakefile +5 -6
  11. data/bin/console +1 -1
  12. data/bin/setup +8 -0
  13. data/lib/nfl_data.rb +9 -18
  14. data/lib/nfl_data/api/player.rb +8 -26
  15. data/lib/nfl_data/api/schedule.rb +19 -0
  16. data/lib/nfl_data/api/statline.rb +8 -22
  17. data/lib/nfl_data/game.rb +5 -0
  18. data/lib/nfl_data/my_sports_feeds/client.rb +45 -0
  19. data/lib/nfl_data/my_sports_feeds/players_feed.rb +18 -0
  20. data/lib/nfl_data/my_sports_feeds/seasonal_games_feed.rb +22 -0
  21. data/lib/nfl_data/my_sports_feeds/weekly_player_gamelogs.rb +21 -0
  22. data/lib/nfl_data/parsers/player_parser.rb +21 -77
  23. data/lib/nfl_data/parsers/schedule_parser.rb +20 -0
  24. data/lib/nfl_data/parsers/statline_parser.rb +27 -71
  25. data/lib/nfl_data/player.rb +13 -0
  26. data/lib/nfl_data/schedule.rb +12 -0
  27. data/lib/nfl_data/statline.rb +20 -0
  28. data/lib/nfl_data/version.rb +1 -1
  29. data/nfl_data.gemspec +23 -22
  30. data/spec/api/player_spec.rb +24 -0
  31. data/spec/api/schedule_spec.rb +20 -0
  32. data/spec/api/statline_spec.rb +31 -0
  33. data/spec/models/game_spec.rb +15 -0
  34. data/spec/models/player_spec.rb +42 -0
  35. data/spec/models/schedule_spec.rb +17 -0
  36. data/spec/models/statline_spec.rb +63 -0
  37. data/spec/my_sports_feeds/client_spec.rb +17 -0
  38. data/spec/my_sports_feeds/players_feed_spec.rb +50 -0
  39. data/spec/my_sports_feeds/seasonal_games_feed_spec.rb +41 -0
  40. data/spec/my_sports_feeds/weekly_player_gamelogs_spec.rb +144 -0
  41. data/spec/parsers/player_parser_spec.rb +63 -0
  42. data/spec/parsers/schedule_parser_spec.rb +41 -0
  43. data/spec/parsers/statline_parser_spec.rb +68 -0
  44. data/spec/spec_helper.rb +55 -0
  45. metadata +98 -52
  46. data/.codeclimate.yml +0 -5
  47. data/.rubocop.yml +0 -2
  48. data/lib/nfl_data/api/team.rb +0 -23
  49. data/lib/nfl_data/models/player.rb +0 -20
  50. data/lib/nfl_data/models/statline.rb +0 -21
  51. data/lib/nfl_data/models/team.rb +0 -38
  52. data/lib/nfl_data/parsers/parser_helper.rb +0 -6
  53. data/lib/nfl_data/parsers/team_parser.rb +0 -94
  54. data/test/nfl_data/api/player_test.rb +0 -25
  55. data/test/nfl_data/api/statline_test.rb +0 -21
  56. data/test/nfl_data/api/team_test.rb +0 -13
  57. data/test/nfl_data/models/player_test.rb +0 -79
  58. data/test/nfl_data/models/statline_test.rb +0 -121
  59. data/test/nfl_data/models/team_test.rb +0 -43
  60. data/test/nfl_data/parsers/player_parser_test.rb +0 -86
  61. data/test/nfl_data/parsers/statline_parser_test.rb +0 -34
  62. data/test/nfl_data/parsers/team_parser_test.rb +0 -41
  63. 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
- module NflData
2
- class PlayerParser
3
- include ParserHelper
4
-
5
- attr_reader :base_url
1
+ # frozen_string_literal: true
6
2
 
7
- def initialize
8
- @base_url = 'http://www.nfl.com/players/search?category=position&conferenceAbbr=null&playerType=current&conference=ALL&filter='
9
- end
10
-
11
- def get_by_position(position)
12
- if position == :all
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
- def get(position)
29
- page_number = 1
30
- url = "#{@base_url}#{position}&d-447263-p=#{page_number}"
31
- return_val = []
32
-
33
- loop do
34
- players_found = update_or_create_players(url)
35
- return_val.push(players_found[1])
36
- break if players_found[0] == 0
37
- url = url.chomp(page_number.to_s)
38
- page_number += 1
39
- url += page_number.to_s
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
- module NflData
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
- private
18
-
19
- def get_player_id_from_profile_url(element)
20
- old_url = 'http://nfl.com' +
21
- element.search('a').first.attributes['href'].value
22
- new_url = ''
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
- new_url.gsub('http://www.nfl.com', '').to_s.split('/')[3]
33
- end
34
-
35
- def grab_week(weeknum, year, stat_type)
36
- url = 'http://www.nfl.com/stats/weeklyleaders?type=REG' \
37
- "&week=#{weeknum}&season=#{year}&showCategory=#{stat_type}"
38
-
39
- doc = open(url) { |f| Nokogiri(f) }
40
-
41
- odds = doc.search('tr.odd')
42
- evens = doc.search('tr.even')
43
-
44
- all = odds + evens
45
-
46
- all.map do |player_row|
47
- elements = player_row.search('td')
48
-
49
- statline = Statline.new
50
-
51
- statline.nfl_player_id = get_player_id_from_profile_url(elements[0])
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,13 @@
1
+ module NflData
2
+ Player = Struct.new(
3
+ :first_name,
4
+ :last_name,
5
+ :full_name,
6
+ :position,
7
+ :number,
8
+ :team,
9
+ :msf_player_id,
10
+ :image_source,
11
+ keyword_init: true
12
+ )
13
+ end
@@ -0,0 +1,12 @@
1
+ module NflData
2
+ Schedule = Struct.new(:games, keyword_init: true) {
3
+ def initialize(games: [])
4
+ super
5
+ end
6
+
7
+ def to_h
8
+ games.map!(&:to_h)
9
+ super
10
+ end
11
+ }
12
+ 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
@@ -1,3 +1,3 @@
1
1
  module NflData
2
- VERSION = '0.0.11'
2
+ VERSION = "0.1.1"
3
3
  end
@@ -1,30 +1,31 @@
1
- # coding: utf-8
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 'nfl_data/version'
4
+ require "nfl_data/version"
6
5
 
7
6
  Gem::Specification.new do |spec|
8
- spec.name = 'nfl_data'
9
- spec.version = NflData::VERSION
10
- spec.authors = ['thetizzo']
11
- spec.email = ['j.m.taylor1@gmail.com']
12
- spec.homepage = 'https://github.com/thetizzo/nfl_data'
13
- spec.license = 'MIT'
14
- spec.summary = 'Parse NFL data like a boss'
15
- spec.description = 'Parse NFL data like a boss'
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 = `git ls-files -z`.split("\x0")
18
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
- spec.require_paths = ['lib']
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 'bundler', '~> 1.6'
23
- spec.add_development_dependency 'rake'
24
- spec.add_development_dependency 'minitest', '~> 5.4.1'
25
- spec.add_development_dependency 'vcr', '~> 2.9.3'
26
- spec.add_development_dependency 'webmock', '~> 1.19.0'
27
- spec.add_development_dependency 'rubocop', '~> 0.35.1'
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 'nokogiri', '~> 1.6'
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