nfl_data 0.0.14 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  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 +7 -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 +10 -19
  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/models/game.rb +5 -0
  18. data/lib/nfl_data/models/player.rb +11 -18
  19. data/lib/nfl_data/models/schedule.rb +12 -0
  20. data/lib/nfl_data/models/statline.rb +18 -19
  21. data/lib/nfl_data/my_sports_feeds/client.rb +45 -0
  22. data/lib/nfl_data/my_sports_feeds/players_feed.rb +18 -0
  23. data/lib/nfl_data/my_sports_feeds/seasonal_games_feed.rb +22 -0
  24. data/lib/nfl_data/my_sports_feeds/weekly_player_gamelogs.rb +21 -0
  25. data/lib/nfl_data/parsers/player_parser.rb +20 -93
  26. data/lib/nfl_data/parsers/schedule_parser.rb +20 -0
  27. data/lib/nfl_data/parsers/statline_parser.rb +27 -71
  28. data/lib/nfl_data/version.rb +1 -1
  29. data/nfl_data.gemspec +23 -23
  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 +49 -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 +83 -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/team.rb +0 -38
  50. data/lib/nfl_data/parsers/data/teams.rb +0 -36
  51. data/lib/nfl_data/parsers/parser_helper.rb +0 -6
  52. data/lib/nfl_data/parsers/team_parser.rb +0 -76
  53. data/test/nfl_data/api/player_test.rb +0 -25
  54. data/test/nfl_data/api/statline_test.rb +0 -21
  55. data/test/nfl_data/api/team_test.rb +0 -13
  56. data/test/nfl_data/models/player_test.rb +0 -81
  57. data/test/nfl_data/models/statline_test.rb +0 -121
  58. data/test/nfl_data/models/team_test.rb +0 -43
  59. data/test/nfl_data/parsers/player_parser_test.rb +0 -81
  60. data/test/nfl_data/parsers/statline_parser_test.rb +0 -34
  61. data/test/nfl_data/parsers/team_parser_test.rb +0 -37
  62. data/test/test_helper.rb +0 -14
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NflData
4
+ module MySportsFeeds
5
+ class Client
6
+ attr_reader :base_url, :api_key, :api_host, :api_version, :format
7
+
8
+ def initialize(
9
+ api_host: ENV.fetch("MYSPORTSFEEDS_API_HOST"),
10
+ api_key: ENV.fetch("MYSPORTSFEEDS_API_KEY"),
11
+ api_version: ENV.fetch("MYSPORTSFEEDS_API_VERSION")
12
+ )
13
+ @api_host = api_host
14
+ @api_key = api_key
15
+ @api_version = api_version
16
+ @format = "json"
17
+ @base_url = "#{@api_host}/#{@api_version}/pull/nfl/"
18
+ end
19
+
20
+ def get(endpoint:, params: {})
21
+ request(method: :get, endpoint: endpoint, params: params)
22
+ end
23
+
24
+ private
25
+
26
+ def request(method:, endpoint:, params:)
27
+ request = Typhoeus::Request.new(
28
+ "#{base_url}#{endpoint}.#{format}",
29
+ method: method,
30
+ params: params,
31
+ accept_encoding: "gzip",
32
+ userpwd: "#{api_key}:MYSPORTSFEEDS"
33
+ )
34
+
35
+ # request.on_complete do |response|
36
+ # if response.success?
37
+ # end
38
+ # end
39
+
40
+ response = request.run
41
+ JSON.parse(response.body)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -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,101 +1,28 @@
1
- require 'typhoeus'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module NflData
4
- class PlayerParser
5
- include ParserHelper
6
-
7
- attr_reader :base_url
8
-
9
- def initialize
10
- @base_url = 'http://www.nfl.com/players/search?category=position&conferenceAbbr=null&playerType=current&conference=ALL&filter='
11
- end
12
-
13
- def get_by_position(position)
14
- if position == :all
15
- {
16
- quarterbacks: get('quarterback'),
17
- runningbacks: get('runningback'),
18
- wide_receivers: get('widereceiver'),
19
- tight_ends: get('tightend')
20
- }
21
- else
22
- # We have to remove the '_' and 's' because the NFL url has
23
- # singular position names and all are one word.
24
- { position => get(position.to_s.gsub(/s|_/, '')) }
25
- end
26
- end
27
-
28
- private
29
-
30
- def get(position)
31
- page_number = 1
32
- url = "#{@base_url}#{position}&d-447263-p=#{page_number}"
33
- return_val = []
34
-
35
- loop do
36
- players_found = update_or_create_players(url)
37
- return_val.push(players_found[1])
38
- break if players_found[0] == 0
39
- url = url.chomp(page_number.to_s)
40
- page_number += 1
41
- url += page_number.to_s
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
42
10
  end
43
11
 
44
- return_val.flatten.map(&:to_hash)
45
-
46
- return_val = return_val.flatten
47
- populate_picture_links(return_val)
48
- return_val.map(&:to_hash)
49
- end
50
-
51
- def update_or_create_players(url)
52
- doc = open(url) { |f| Nokogiri(f) }
53
-
54
- # NFL.com stores players in 2 types of rows.
55
- # css class = odd or even.
56
- all_rows = doc.search('tr.odd') + doc.search('tr.even')
57
- players = all_rows.map { |row| parse_player_from_row(row.search('td')) }
58
- [players.count, players]
59
- end
60
-
61
- def parse_player_from_row(elements)
62
- # Get NFL.com Unique player id
63
- nfl_player_id = elements[2].to_s.split('/')[3]
64
-
65
- # player id is the only one with complicated parsing so we
66
- # can just extract the inner text out of the rest of the elements
67
- elements = elements.map(&:inner_text).map(&:strip)
68
- names = elements[2].split(',').map(&:strip).reverse
69
-
70
- Player.new(
71
- nfl_player_id: nfl_player_id,
72
- position: elements[0],
73
- number: elements[1],
74
- status: elements[3],
75
- team: make_jacksonville_abbreviation_consistent(elements[12]),
76
- first_name: names[0],
77
- last_name: names[1],
78
- full_name: names.join(' '),
79
- profile_link: get_profile_link(nfl_player_id, names[0], names[1])
80
- )
81
- end
82
-
83
- def get_profile_link(nfl_player_id, first_name, last_name)
84
- "http://www.nfl.com/player/#{first_name.gsub(/\s/, '')}#{last_name.gsub(/\s/, '')}/#{nfl_player_id}/profile"
85
- end
86
-
87
- def populate_picture_links(players)
88
- hydra = Typhoeus::Hydra.hydra
89
- players.each do |player|
90
- request = Typhoeus::Request.new(player.profile_link, followlocation: true, accept_encoding: 'gzip')
91
- request.on_complete do |response|
92
- doc = Nokogiri::HTML(response.body)
93
- img_element = doc.search('div.player-photo img').first
94
- player.picture_link = img_element.attributes['src'].value unless img_element.nil?
95
- end
96
- hydra.queue(request)
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
+ )
97
25
  end
98
- hydra.run
99
26
  end
100
27
  end
101
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
@@ -1,3 +1,3 @@
1
1
  module NflData
2
- VERSION = '0.0.14'
2
+ VERSION = "0.1.0"
3
3
  end
@@ -1,31 +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.16.5'
23
- spec.add_development_dependency 'rake'
24
- spec.add_development_dependency 'minitest', '~> 5.11.3'
25
- spec.add_development_dependency 'vcr', '~> 4.0.0'
26
- spec.add_development_dependency 'webmock', '~> 3.4.2'
27
- spec.add_development_dependency 'rubocop', '~> 0.59.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.1"
28
27
 
29
- spec.add_dependency 'typhoeus', '~> 1.3'
30
- 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"
31
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