lib/sportradar/api/soccer/match.rb
module Sportradar
module Api
module Soccer
class Match < Data
attr_reader :id, :league_group, :scheduled, :start_time_tbd, :status, :tournament_round, :match_status, :venue
attr_reader :home_score, :away_score, :winner_id, :aggregate_home_score, :aggregate_away_score, :aggregate_winner_id
attr_reader :referee, :weather_info, :coverage_info, :probabilities
attr_reader :home, :away, :tournament_id
attr_reader :period, :score, :broadcast, :coverage # these are for consistency with other sports
attr_reader :match_time, :stoppage_time
attr_reader :team_stats, :player_stats
def initialize(data = {}, season: nil, **opts)
@response = data
@id = data['id'] || data.dig("sport_event", 'id')
@api = opts[:api]
@season = season
@updates = {}
@changes = {}
@timeline_hash = {}
@lineups_hash = {}
# get_tournament_id(data, **opts)
@scoring_raw = Scoring.new(data, game: self)
@home = Team.new(data['home'].to_h, api: api, match: self)
@away = Team.new(data['away'].to_h, api: api, match: self)
@teams_hash = { away: @away, home: @home }
@team_ids = { away: @away.id, home: @home.id }
@team_stats = {}
@player_stats = {}
update(data, **opts)
end
def update(data, **opts)
if data["sport_event"]
update(data["sport_event"])
end
if data["sport_event_status"]
update(data["sport_event_status"])
end
if data['sport_event_conditions']
update(data['sport_event_conditions'])
end
if data['probabilities']
# update(data['probabilities'])
@probabilities = data['probabilities'] # tidy this up later
end
if data['lineups']
create_data(@lineups_hash, data['lineups'], klass: Lineup, identifier: 'team', api: api)
end
if (stats = data.dig('statistics', 'totals', 'competitors'))
update_teams(stats)
end
@scheduled = Time.parse(data['scheduled']) if data['scheduled']
@scheduled = Time.parse(data['start_time']) if data['start_time']
@start_time_tbd = data['start_time_tbd'] if data.key?('start_time_tbd')
@status = data['status'] if data['status']
@match_status = data['match_status'] if data['match_status']
@tournament_round = data['tournament_round'] if data['tournament_round']
@venue = Venue.new(data['venue']) if data['venue']
@weather_info = OpenStruct.new(data["weather_info"]) if data["weather_info"]
@referee = OpenStruct.new(data["referee"]) if data["referee"]
@coverage_info = OpenStruct.new(data["coverage_info"]) if data["coverage_info"]
@home_score = data['home_score'] if data['home_score']
@away_score = data['away_score'] if data['away_score']
@period = parse_period(data['match_status']) if data['match_status']
@period = data['period'] if data['period']
@match_time = data.dig('clock', 'match_time') if data.dig('clock', 'match_time')
@match_time = data.dig('clock', 'played') if data.dig('clock', 'played')
@stoppage_time = data.dig('clock', 'stoppage_time')
@ball_locations = data['ball_locations'] if data['ball_locations']
@winner_id = data['winner_id'] if data['winner_id']
@aggregate_home_score = data['aggregate_home_score'] if data['aggregate_home_score']
@aggregate_away_score = data['aggregate_away_score'] if data['aggregate_away_score']
@aggregate_winner_id = data['aggregate_winner_id'] if data['aggregate_winner_id']
@scoring_raw.update(data, source: opts[:source])
create_data(@timeline_hash, data['timeline'], klass: Event, api: api)
if data['competitors']
update_teams(data['competitors'])
end
# parse_nested_data(data)
end
def title
[@home, @away].compact.map(&:name).join(' vs ')
end
def scoring
@scoring_raw.scores.each { |period, hash| hash[@home.id] = hash['home']; hash[@away.id] = hash['away'] }
end
def score
{@home.id => @home_score, @away.id => @away_score}
end
# status helpers
def realtime_state_short
if future?
'Scheduled' # ??
elsif finished?
'FT'
elsif postponed?
'PPD'
elsif halftime?
'HT'
else
clock_display
end
end
def realtime_state
if future?
'Scheduled'
elsif finished?
'Final'
elsif postponed?
'Postponed'
elsif halftime?
'Halftime'
else
clock_display
end
end
def halftime?
@match_status == "halftime"
end
def parse_period(status)
case status
when '1st_half', 'halftime'
1
when '2nd_half'
2
when 'overtime', '1st_extra'
3
when '2nd_extra'
4
end
end
def postponed?
'postponed' == status
end
def unnecessary?
'unnecessary' == status
end
def cancelled?
['unnecessary', 'postponed'].include? status
end
def future?
['not_started', 'scheduled', 'delayed', 'created', 'time-tbd', 'if-necessary'].include? status
end
def started?
['live'].include?(@status) || ['halftime', '1st_half', '2nd_half'].include?(@match_status)
end
def finished?
['ended', 'complete', 'closed'].include?(@status) || ['ended'].include?(@match_status)
end
def completed?
['ended', 'complete'].include? status
end
def closed?
'closed' == status
end
def clock_display
return unless @match_time
if @stoppage_time && (@match_time == '45:00' || @match_time == '90:00') # stoppage time
@match_time.split(':').first + ?' + '+' + @stoppage_time.split(':').first
else
@match_time.split(':').first + ?'
end
end
alias :clock :clock_display
def match_seconds
return nil unless @match_time
mm, ss = @match_time.split(':').map(&:to_i)
time = mm * 60 + ss
if @stoppage_time && (@match_time == '45:00' || @match_time == '90:00') # stoppage time
mm, ss = @stoppage_time.split(':').map(&:to_i)
stop_time = mm * 60 + ss
time += stop_time
end
time
end
alias :game_seconds :match_seconds
def update_teams(data)
home_hash = data.detect { |team_hash| team_hash["qualifier"] == "home" || team_hash["team"] == "home" }
away_hash = (data - [home_hash]).first
if home_hash && away_hash
@home.update(home_hash, match: self)
@away.update(away_hash, match: self)
@teams_hash[@home.id] = @home
@teams_hash[@away.id] = @away
@team_ids = { away: @away.id, home: @home.id }
end
end
def update_stats(team, stats)
@team_stats.merge!(team.id => stats.merge(team: team))
end
def update_player_stats(player, stats)
@player_stats.merge!(player.id => stats.merge!(player: player))
end
def stats(team_id)
team_id.is_a?(Symbol) ? @team_stats[@team_ids[team_id]] : @team_stats[team_id]
end
def get_tournament_id(data, **opts)
@tournament_id ||= if opts[:tournament]
opts[:tournament].id
elsif opts[:season]
opts[:season].tournament_id
elsif opts[:match]
opts[:match].tournament_id
elsif data['tournament']
data.dig('tournament', 'id')
elsif data['season']
data.dig('season', 'tournament_id')
end
end
def team(place_or_id)
@teams_hash[place_or_id]
end
def timeline(type = nil)
if type
@timeline_hash.each_value.select { |ev| ev.type == type }
else
@timeline_hash.values
end
end
def timeline_by_minute(minute_start, minute_end = nil)
if minute_end
@timeline_hash.each_value.select { |ev| (minute_start..minute_end).cover?(ev.match_time.to_i) }
else
@timeline_hash.each_value.select { |ev| minute_start === ev.match_time }
end
end
def plays_by_minute(type, minute_start, minute_end = nil)
timeline_by_minute(minute_start, minute_end).select { |ev| ev.type == type }
end
def lineups(which = nil)
if which
@lineups_hash[which.to_s]
else
@lineups_hash.values
end
end
def api
@api ||= Sportradar::Api::Soccer::Api.new(league_group: @league_group)
end
def path_base
"sport_events/#{ id }"
end
def path_summary
"#{ path_base }/summary"
end
def get_summary
data = api.get_data(path_summary).to_h
ingest_summary(data)
end
def ingest_summary(data)
update(data)
data
end
def queue_summary
url, headers, options, timeout = api.get_request_info(path_summary)
{url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_summary)}
end
def path_lineups
"#{ path_base }/lineups"
end
def get_lineups
data = api.get_data(path_lineups).to_h
ingest_lineups(data)
end
def ingest_lineups(data)
update(data)
data
end
def queue_lineups
url, headers, options, timeout = api.get_request_info(path_lineups)
{url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_lineups)}
end
def path_facts
"#{ path_base }/funfacts"
end
def get_facts
data = api.get_data(path_facts).to_h
ingest_facts(data)
end
def ingest_facts(data)
update(data)
data
end
def queue_facts
url, headers, options, timeout = api.get_request_info(path_facts)
{url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_facts)}
end
def path_probabilities
"#{ path_base }/probabilities"
end
def get_probabilities
data = api.get_data(path_probabilities).to_h
ingest_probabilities(data)
end
def ingest_probabilities(data)
update(data)
data
end
def queue_probabilities
url, headers, options, timeout = api.get_request_info(path_probabilities)
{url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_probabilities)}
end
def path_timeline
"#{ path_base }/timeline"
end
def get_timeline
data = api.get_data(path_timeline).to_h
ingest_timeline(data)
end
def ingest_timeline(data)
update(data, source: :pbp)
check_newness(:pbp, timeline.last&.updated)
check_newness(:clock, self.match_seconds.to_s)
data
end
def queue_timeline
url, headers, options, timeout = api.get_request_info(path_timeline)
{url: url, headers: headers, params: options, timeout: timeout, callback: method(:ingest_timeline)}
end
# tracking updates
def remember(key, object)
@updates[key] = object&.dup
end
def not_updated?(key, object)
@updates[key] == object
end
def changed?(key)
@changes[key]
end
def check_newness(key, new_object)
@changes[key] = !not_updated?(key, new_object)
remember(key, new_object)
end
end
end
end
end
__END__
"id"=>"sr:match:12090446",
"scheduled"=>"2018-05-20T18:45:00+00:00",
"start_time_tbd"=>true,
"status"=>"not_started",
"tournament_round"=>{"type"=>"group", "number"=>38},
"season"=>{"id"=>"sr:season:42720", "name"=>"Serie A 17/18", "start_date"=>"2017-08-19", "end_date"=>"2018-05-21", "year"=>"17/18", "tournament_id"=>"sr:tournament:23"},
"tournament"=>{"id"=>"sr:tournament:23", "name"=>"Serie A", "sport"=>{"id"=>"sr:sport:1", "name"=>"Soccer"}, "category"=>{"id"=>"sr:category:31", "name"=>"Italy", "country_code"=>"ITA"}},
"competitors"=>
[{"id"=>"sr:competitor:2793", "name"=>"US Sassuolo", "country"=>"Italy", "country_code"=>"ITA", "abbreviation"=>"SAS", "qualifier"=>"home"},
{"id"=>"sr:competitor:2702", "name"=>"AS Roma", "country"=>"Italy", "country_code"=>"ITA", "abbreviation"=>"ROM", "qualifier"=>"away"}]
"sport_event_status"=>
{"status"=>"live",
"match_status"=>"1st_half",
"home_score"=>1,
"away_score"=>0,
"period"=>1,
"clock"=>{"match_time"=>"19:25"},
"clock"=>{"match_time"=>"45:00", "stoppage_time"=>"00:05"}
"ball_locations"=>[{"x"=>"95", "y"=>"50", "team"=>"away"}, {"x"=>"80", "y"=>"62", "team"=>"home"}, {"x"=>"82", "y"=>"60", "team"=>"home"}, {"x"=>"79", "y"=>"58", "team"=>"home"}]},
m = Sportradar::Api::Soccer::Match.new({"id"=>"sr:match:12090446"}, league_group: 'eu')
res = m.get_summary
res = m.get_lineups
res = m.get_facts
res = m.get_probabilities
res = m.get_timeline