sportradar-api 0.10.13 → 0.10.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ module Sportradar
2
+ module Api
3
+ module Baseball
4
+ class HalfInning < Data
5
+ attr_accessor :response, :id, :inning, :type, :half, :number
6
+
7
+ def initialize(data, **opts)
8
+ @response = data
9
+ @api = opts[:api]
10
+ @inning = opts[:inning]
11
+ @id = data["id"]
12
+ *@inning_id, @number, @half = data['id'].split('-')
13
+
14
+ @events_hash = {}
15
+
16
+ update(data)
17
+
18
+ end
19
+
20
+ def update(data, **opts)
21
+ @half = data['half']
22
+ @events = data['events'].map{ |hash| Event.new(hash, half_inning: self) }
23
+ # create_data(@events_hash, data['events'], klass: Event, api: @api, half_inning: self)
24
+ end
25
+
26
+ def over?
27
+ pitches.last.count&.dig('outs') == 3
28
+ end
29
+
30
+ def pitches
31
+ at_bats.flat_map(&:pitches)
32
+ end
33
+
34
+ def at_bats
35
+ events.map(&:at_bat).compact
36
+ end
37
+
38
+ def lineup_changes
39
+ # events_by_klass(LineupChange)
40
+ end
41
+
42
+ def events
43
+ # @events_hash.values
44
+ @events
45
+ end
46
+
47
+ # private def events_by_klass(klass)
48
+ # @events_hash.each_value.grep(klass)
49
+ # end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,42 @@
1
+ module Sportradar
2
+ module Api
3
+ module Baseball
4
+ class Inning < Data
5
+ attr_accessor :response, :id, :game, :number, :sequence, :scoring
6
+
7
+ def initialize(data, **opts)
8
+ @response = data
9
+ @api = opts[:api]
10
+ @game = opts[:game]
11
+
12
+ @id = data["id"]
13
+ @number = data['number']
14
+ @sequence = data['sequence']
15
+
16
+ parse_scoring(data['scoring']) if data['scoring']
17
+
18
+ @half_innings_hash = {}
19
+
20
+ update(data)
21
+ end
22
+ def update(data, **opts)
23
+ # update scoring
24
+ halfs = data['halfs'].each { |inning| inning['id'] = "#{data['id']}-#{inning['half']}" }
25
+ create_data(@half_innings_hash, halfs, klass: HalfInning, api: @api, inning: self)
26
+ end
27
+ def parse_scoring(data)
28
+ @scoring = data.each_with_object({}) { |(_, data), hash| hash[data['id']] = data['runs'].to_s } # from PBP
29
+ end
30
+
31
+ def half_innings
32
+ @half_innings_hash.values
33
+ end
34
+
35
+ def events
36
+ half_innings.flat_map(&:events)
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,32 @@
1
+ module Sportradar
2
+ module Api
3
+ module Baseball
4
+ class Mlb
5
+ class Division < Data
6
+ attr_accessor :response, :id, :name, :alias
7
+
8
+ def initialize(data, **opts)
9
+ @response = data
10
+ @api = opts[:api]
11
+
12
+ @id = data["id"]
13
+ @name = data["name"]
14
+ @alias = data["alias"]
15
+ @assigned_teams = nil
16
+ @teams_hash = {}
17
+ @teams_hash = create_data({}, data["teams"], klass: Team, division: self, api: @api) if response["teams"]
18
+
19
+ end
20
+
21
+ def teams
22
+ @assigned_teams || @teams_hash.values
23
+ end
24
+ def teams=(array)
25
+ @assigned_teams = array
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,267 @@
1
+ module Sportradar
2
+ module Api
3
+ module Baseball
4
+ class Mlb
5
+ class Hierarchy < Data
6
+ attr_accessor :response, :id, :name, :alias, :year, :type
7
+ def all_attributes
8
+ [:name, :alias, :leagues, :divisions, :teams]
9
+ end
10
+
11
+ def initialize(data = {}, **opts)
12
+ # @response = data
13
+ @api = opts[:api]
14
+ @id = data.dig('league', 'id')
15
+
16
+ @leagues_hash = {}
17
+ @games_hash = {}
18
+ @teams_hash = {}
19
+
20
+ end
21
+
22
+ def update(data, source: nil, **opts)
23
+ # update stuff
24
+ @name = data.dig('league', 'name') if data.dig('league', 'name')
25
+ @alias = data.dig('league', 'alias') if data.dig('league', 'alias')
26
+
27
+ @year = data.dig('season', 'year') if data.dig('season', 'year')
28
+ @type = data.dig('season', 'type') if data.dig('season', 'type')
29
+
30
+ @leagues_hash = create_data({}, data['leagues'], klass: League, hierarchy: self, api: api) if data['leagues']
31
+ @teams_hash = create_data({}, data['teams'], klass: Team, hierarchy: self, api: api) if data['teams']
32
+ if data['games']
33
+ if data['games'].first.keys == ['game']
34
+ data['games'].map! { |hash| hash['game'] }
35
+ end
36
+ @games_hash = create_data({}, data['games'], klass: Game, hierarchy: self, api: api)
37
+ end
38
+ end
39
+
40
+ def schedule
41
+ get_schedule if games.empty?
42
+ self
43
+ end
44
+
45
+ def standings
46
+ get_standings if teams.first&.record.nil?
47
+ self
48
+ end
49
+
50
+ def daily_schedule
51
+ # TODO
52
+ end
53
+
54
+ def games
55
+ @games_hash.values
56
+ end
57
+
58
+ def leagues
59
+ @leagues_hash.values
60
+ end
61
+ def divisions
62
+ leagues.flat_map(&:divisions)
63
+ end
64
+ def teams
65
+ teams = divisions.flat_map(&:teams)
66
+ if teams.empty?
67
+ if @teams_hash.empty?
68
+ get_hierarchy
69
+ divisions.flat_map(&:teams)
70
+ else
71
+ @teams_hash.values
72
+ end
73
+ else
74
+ teams
75
+ end
76
+ end
77
+ def team(team_id)
78
+ teams.detect { |team| team.id == team_id }
79
+ end
80
+
81
+
82
+
83
+ # api stuff
84
+ def api
85
+ @api || Sportradar::Api::Baseball::Mlb.new
86
+ end
87
+
88
+ def default_year
89
+ Date.today.year
90
+ end
91
+ def default_date
92
+ Date.today
93
+ end
94
+ def default_season
95
+ 'reg'
96
+ end
97
+ def season_year
98
+ @year || default_year
99
+ end
100
+ def mlb_season
101
+ @type || default_season
102
+ end
103
+
104
+
105
+ # url paths
106
+ def path_base
107
+ "league"
108
+ end
109
+ def path_schedule
110
+ "games/#{season_year}/#{mlb_season}/schedule"
111
+ end
112
+ def path_series
113
+ "series/#{season_year}/#{mlb_season}/schedule"
114
+ end
115
+ def path_rankings
116
+ "seasontd/#{season_year}/#{mlb_season}/rankings"
117
+ end
118
+ def path_hierarchy
119
+ "#{ path_base }/hierarchy"
120
+ end
121
+ def path_depth_charts
122
+ "#{ path_base }/depth_charts"
123
+ end
124
+ def path_standings
125
+ "seasontd/#{season_year}/#{mlb_season}/standings"
126
+ end
127
+ def path_daily_summary(date)
128
+ "games/#{date.year}/#{date.month}/#{date.day}/summary"
129
+ end
130
+ def path_daily_boxscore(date)
131
+ "#{ path_base }/games/#{date.year}/#{date.month}/#{date.day}/boxscore"
132
+ end
133
+
134
+
135
+ # data retrieval
136
+
137
+ ## schedule
138
+ def get_schedule
139
+ data = api.get_data(path_schedule).to_h
140
+ ingest_schedule(data)
141
+ end
142
+
143
+ def ingest_schedule(data)
144
+ update(data, source: :games)
145
+ data
146
+ end
147
+
148
+ def queue_schedule
149
+ url, headers, options, timeout = api.get_request_info(path_schedule)
150
+ {url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_schedule)}
151
+ end
152
+
153
+ ## hierarchy
154
+ def get_hierarchy
155
+ data = api.get_data(path_hierarchy).to_h
156
+ ingest_hierarchy(data)
157
+ end
158
+
159
+ def ingest_hierarchy(data)
160
+ update(data, source: :teams)
161
+ data
162
+ end
163
+
164
+ def queue_hierarchy
165
+ url, headers, options, timeout = api.get_request_info(path_hierarchy)
166
+ {url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_hierarchy)}
167
+ end
168
+
169
+ ## depth_charts
170
+ def get_depth_charts
171
+ data = api.get_data(path_depth_charts).to_h
172
+ ingest_depth_charts(data)
173
+ end
174
+
175
+ def ingest_depth_charts(data)
176
+ update(data, source: :teams)
177
+ data
178
+ end
179
+
180
+ def queue_depth_charts
181
+ url, headers, options, timeout = api.get_request_info(path_depth_charts)
182
+ {url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_depth_charts)}
183
+ end
184
+
185
+ ## standings
186
+ def get_standings
187
+ data = api.get_data(path_standings).to_h
188
+ ingest_standings(data)
189
+ end
190
+
191
+ def ingest_standings(data)
192
+ update(data.dig('league','season'), source: :teams)
193
+ data
194
+ end
195
+
196
+ def queue_standings
197
+ url, headers, options, timeout = api.get_request_info(path_standings)
198
+ {url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_standings)}
199
+ end
200
+
201
+ ## depth_charts
202
+ def get_depth_charts
203
+ data = api.get_data(path_depth_charts).to_h
204
+ ingest_depth_charts(data)
205
+ end
206
+
207
+ def ingest_depth_charts(data)
208
+ update(data, source: :teams)
209
+ data
210
+ end
211
+
212
+ def queue_depth_charts
213
+ url, headers, options, timeout = api.get_request_info(path_depth_charts)
214
+ {url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_depth_charts)}
215
+ end
216
+
217
+ ## daily summary
218
+ def get_daily_summary(date = Date.today)
219
+ data = api.get_data(path_daily_summary(date)).to_h
220
+ ingest_daily_summary(data)
221
+ end
222
+
223
+ def ingest_daily_summary(data)
224
+ update(data.dig('league'), source: :games)
225
+ data
226
+ end
227
+
228
+ def queue_daily_summary(date = Date.today)
229
+ url, headers, options, timeout = api.get_request_info(path_daily_summary(date))
230
+ {url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_daily_summary)}
231
+ end
232
+
233
+ ## venues
234
+ # def get_venues
235
+ # data = api.get_data(path_venues).to_h
236
+ # ingest_venues(data)
237
+ # end
238
+
239
+ # def ingest_venues(data)
240
+ # update(data, source: :teams)
241
+ # data
242
+ # end
243
+
244
+ # def queue_venues
245
+ # url, headers, options, timeout = api.get_request_info(path_venues)
246
+ # {url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_venues)}
247
+ # end
248
+
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
254
+
255
+ __END__
256
+
257
+ mlb = Sportradar::Api::Baseball::Mlb::Hierarchy.new
258
+ res = mlb.get_daily_summary;
259
+ res = mlb.get_hierarchy;
260
+ t = mlb.teams.first;
261
+ t.get_season_stats;
262
+ t.players.sample
263
+
264
+ res = mlb.get_schedule;
265
+ g = mlb.games.sort_by(&:scheduled).first
266
+
267
+
@@ -0,0 +1,27 @@
1
+ module Sportradar
2
+ module Api
3
+ module Baseball
4
+ class Mlb
5
+ class League < Data
6
+ attr_accessor :response, :id, :name, :alias
7
+
8
+ def initialize(data, **opts)
9
+ @response = data
10
+ @api = opts[:api]
11
+
12
+ @id = data["id"]
13
+ @name = data["name"]
14
+ @alias = data["alias"]
15
+ @divisions_hash = create_data({}, data["divisions"], klass: Division, conference: self, api: @api) # if response["division"]
16
+ end
17
+
18
+ def divisions
19
+ @divisions_hash.values
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,87 @@
1
+ module Sportradar
2
+ module Api
3
+ module Baseball
4
+ class Mlb < Request
5
+ attr_accessor :league, :access_level, :simulation, :error
6
+
7
+ def initialize(access_level = default_access_level)
8
+ @league = 'mlb'
9
+ raise Sportradar::Api::Error::InvalidAccessLevel unless allowed_access_levels.include? access_level
10
+ @access_level = access_level
11
+ end
12
+
13
+ def get_data(url)
14
+ data = get request_url(url)
15
+ if data.is_a?(Sportradar::Api::Error)
16
+ puts request_url(url)
17
+ puts
18
+ puts data.inspect
19
+ raise 'Sportradar error'
20
+ end
21
+ data
22
+ end
23
+
24
+ def default_year
25
+ Date.today.year
26
+ end
27
+ def default_date
28
+ Date.today
29
+ end
30
+ def default_season
31
+ 'reg'
32
+ end
33
+ def default_access_level
34
+ if (ENV['SPORTRADAR_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']) == 'production'
35
+ 'p'
36
+ else
37
+ 't'
38
+ end
39
+ end
40
+
41
+ def league_hierarchy
42
+ response = get request_url("league/hierarchy")
43
+ if response.success?
44
+ Sportradar::Api::Baseball::Mlb::Hierarchy.new(response.to_h, api: self)
45
+ else
46
+ response
47
+ end
48
+ end
49
+ alias :hierarchy :league_hierarchy
50
+
51
+ def content_format
52
+ 'json'
53
+ end
54
+
55
+ private
56
+
57
+ def request_url(path)
58
+ "/mlb-#{access_level}#{version}/#{path}"
59
+ end
60
+
61
+ def api_key
62
+ if access_level != 't'
63
+ Sportradar::Api.api_key_params('mlb', 'production')
64
+ else
65
+ Sportradar::Api.api_key_params('mlb')
66
+ end
67
+ end
68
+
69
+ def version
70
+ Sportradar::Api.version('mlb')
71
+ end
72
+
73
+ def allowed_access_levels
74
+ %w[p t]
75
+ end
76
+
77
+ def allowed_seasons
78
+ ["pre", "reg", "pst"]
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ __END__
87
+ # mlb = Sportradar::Api::Baseball::Mlb::Hierarchy.new
@@ -0,0 +1,87 @@
1
+ module Sportradar
2
+ module Api
3
+ module Baseball
4
+ class Outcome < Data
5
+ attr_accessor :response, :type, :current_inning, :current_inning_half, :count, :hitter, :pitcher, :runners
6
+
7
+ def initialize(data, **opts)
8
+ @response = data
9
+ @api = opts[:api]
10
+ @game = opts[:game]
11
+
12
+ @scores = {}
13
+ @id = data['id']
14
+
15
+ update(data, **opts)
16
+ end
17
+
18
+ def update(data, source: nil, **opts)
19
+ update_from_outcome(data['outcome']) if data['outcome']
20
+ end
21
+
22
+ def update_from_outcome(data)
23
+ @type = data['type'] if data['type']
24
+ @current_inning = data['current_inning'] if data['current_inning']
25
+ @current_inning_half = data['current_inning_half'] if data['current_inning_half']
26
+ @count = data['count'] if data['count']
27
+ @hitter = data['hitter'] if data['hitter']
28
+ @pitcher = data['pitcher'] if data['pitcher']
29
+ @runners = data['runners'] if data['runners']
30
+ end
31
+
32
+
33
+ private
34
+
35
+ def parse_from_pbp(data)
36
+ scoring = data['innings'].map {|i| i['scoring'] }.compact
37
+ return {} if scoring.empty?
38
+ scoring.each_with_object({}).with_index(1) do |(hash, memo), idx|
39
+ memo[idx] = {hash.dig('home', 'id') => hash.dig('home', 'runs'), hash.dig('away', 'id') => hash.dig('away', 'runs')}
40
+ end
41
+ end
42
+
43
+ def parse_from_box(data)
44
+ id = data.dig('home', 'id')
45
+ da = data.dig('home', 'scoring')
46
+ return {} unless da
47
+ da.each { |h| h[id] = h.delete('runs') }
48
+ id = data.dig('away', 'id')
49
+ db = data.dig('away', 'scoring')
50
+ return {} unless db
51
+ db.each { |h| h[id] = h.delete('runs') }
52
+ da.zip(db).map{ |a, b| [a['sequence'].to_i, a.merge(b)] }.sort{ |(a,_), (b,_)| a <=> b }.to_h
53
+ end
54
+
55
+ def parse_from_summary(data)
56
+ #
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # inprogress data from daily summary
65
+ {"id"=>"e1b6d4cc-74f0-4f80-b1d4-d506624d7ba0",
66
+ "status"=>"inprogress",
67
+ "coverage"=>"full",
68
+ "game_number"=>1,
69
+ "day_night"=>"D",
70
+ "scheduled"=>"2017-05-04T17:10:00+00:00",
71
+ "home_team"=>"aa34e0ed-f342-4ec6-b774-c79b47b60e2d",
72
+ "away_team"=>"27a59d3b-ff7c-48ea-b016-4798f560f5e1",
73
+ "venue"=>{"id"=>"302f8dcd-eed6-4b83-8609-81548d51e955", "name"=>"Target Field", "market"=>"Minnesota", "capacity"=>39021, "surface"=>"grass", "address"=>"353 N 5th Street", "city"=>"Minneapolis", "state"=>"MN", "zip"=>"55403", "country"=>"USA"},
74
+ "broadcast"=>{"network"=>"FS-N"},
75
+ "outcome"=>
76
+ {"type"=>"pitch",
77
+ "current_inning"=>3,
78
+ "current_inning_half"=>"B",
79
+ "count"=>{"strikes"=>2, "balls"=>0, "outs"=>1, "inning"=>3, "inning_half"=>"B", "half_over"=>false},
80
+ "hitter"=>{"id"=>"aecc630f-57da-4b23-842b-fd65394e81be", "outcome_id"=>"kF", "ab_over"=>false, "last_name"=>"Sano", "first_name"=>"Miguel", "preferred_name"=>"Miguel", "jersey_number"=>"22"},
81
+ "pitcher"=>{"id"=>"c1f19b5a-9dee-4053-9cad-ee4196f921e1", "last_name"=>"Cotton", "first_name"=>"Jharel", "preferred_name"=>"Jharel", "jersey_number"=>"45", "pitch_type"=>"FA", "pitch_speed"=>90.0, "pitch_zone"=>9, "pitch_x"=>52, "pitch_y"=>-39},
82
+ "runners"=>[{"id"=>"29a80d91-946d-4701-af7d-034850bdef00", "starting_base"=>1, "ending_base"=>1, "outcome_id"=>"", "out"=>false, "last_name"=>"Dozier", "first_name"=>"James", "preferred_name"=>"Brian", "jersey_number"=>"2"}]},
83
+ "officials"=>
84
+ [{"full_name"=>"Mike Winters", "first_name"=>"Mike", "last_name"=>"Winters", "assignment"=>"2B", "experience"=>"24", "id"=>"344565d2-3276-4948-ac8e-28e4e49be9d9"},
85
+ {"full_name"=>"Mark Wegner", "first_name"=>"Mark", "last_name"=>"Wegner", "assignment"=>"3B", "experience"=>"15", "id"=>"27109ca4-3484-45ad-a78d-1a639c1bfabd"},
86
+ {"full_name"=>"Marty Foster", "first_name"=>"Marty", "last_name"=>"Foster", "assignment"=>"1B", "experience"=>"15", "id"=>"af6b8841-0bfd-402b-88de-1439d7d4ea74"},
87
+ {"full_name"=>"Mike Muchlinski", "first_name"=>"Mike", "last_name"=>"Muchlinski", "assignment"=>"HP", "experience"=>"2", "id"=>"ef45c7e3-9136-4704-b3a6-bfbd61cf9416"}]}