i3pystatus.scores.mlb: Update module to use new statsapi (#739)
This commit is contained in:
parent
14cfde967c
commit
68d1158c9b
@ -9,9 +9,9 @@ import time
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
||||||
LIVE_URL = 'http://mlb.mlb.com/mlb/gameday/index.jsp?gid=%s'
|
LIVE_URL = 'https://www.mlb.com/gameday/%s'
|
||||||
SCOREBOARD_URL = 'http://m.mlb.com/scoreboard'
|
SCOREBOARD_URL = 'http://m.mlb.com/scoreboard'
|
||||||
API_URL = 'http://gd2.mlb.com/components/game/mlb/year_%04d/month_%02d/day_%02d/miniscoreboard.json'
|
API_URL = 'https://statsapi.mlb.com/api/v1/schedule?sportId=1,51&date=%04d-%02d-%02d&gameTypes=E,S,R,A,F,D,L,W&hydrate=team(),linescore(matchup,runners),stats,game(content(media(featured,epg),summary),tickets),seriesStatus(useOverride=true)&useLatestGames=false&language=en&leagueId=103,104,420'
|
||||||
|
|
||||||
|
|
||||||
class MLB(ScoresBackend):
|
class MLB(ScoresBackend):
|
||||||
@ -190,9 +190,10 @@ class MLB(ScoresBackend):
|
|||||||
self.get_api_date()
|
self.get_api_date()
|
||||||
url = self.api_url % (self.date.year, self.date.month, self.date.day)
|
url = self.api_url % (self.date.year, self.date.month, self.date.day)
|
||||||
|
|
||||||
game_list = self.get_nested(self.api_request(url),
|
game_list = self.get_nested(
|
||||||
'data:games:game',
|
self.api_request(url),
|
||||||
default=[])
|
'dates:0:games',
|
||||||
|
default=[])
|
||||||
if not isinstance(game_list, list):
|
if not isinstance(game_list, list):
|
||||||
# When only one game is taking place during a given day, the game
|
# When only one game is taking place during a given day, the game
|
||||||
# data is just a single dict containing that game's data, rather
|
# data is just a single dict containing that game's data, rather
|
||||||
@ -205,17 +206,25 @@ class MLB(ScoresBackend):
|
|||||||
team_game_map = {}
|
team_game_map = {}
|
||||||
for game in game_list:
|
for game in game_list:
|
||||||
try:
|
try:
|
||||||
id_ = game['id']
|
id_ = game['gamePk']
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
away_abbrev = self.get_nested(
|
||||||
for team in (game['home_name_abbrev'], game['away_name_abbrev']):
|
game,
|
||||||
team = team.upper()
|
'teams:away:team:abbreviation',
|
||||||
if team in self.favorite_teams:
|
default='').upper()
|
||||||
team_game_map.setdefault(team, []).append(id_)
|
home_abbrev = self.get_nested(
|
||||||
except KeyError:
|
game,
|
||||||
continue
|
'teams:home:team:abbreviation',
|
||||||
|
default='').upper()
|
||||||
|
if away_abbrev and home_abbrev:
|
||||||
|
try:
|
||||||
|
for team in (home_abbrev, away_abbrev):
|
||||||
|
if team in self.favorite_teams:
|
||||||
|
team_game_map.setdefault(team, []).append(id_)
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
data[id_] = game
|
data[id_] = game
|
||||||
|
|
||||||
@ -224,106 +233,85 @@ class MLB(ScoresBackend):
|
|||||||
def process_game(self, game):
|
def process_game(self, game):
|
||||||
ret = {}
|
ret = {}
|
||||||
|
|
||||||
def _update(ret_key, game_key=None, callback=None, default='?'):
|
|
||||||
ret[ret_key] = self.get_nested(game,
|
|
||||||
game_key or ret_key,
|
|
||||||
callback=callback,
|
|
||||||
default=default)
|
|
||||||
|
|
||||||
self.logger.debug('Processing %s game data: %s',
|
self.logger.debug('Processing %s game data: %s',
|
||||||
self.__class__.__name__, game)
|
self.__class__.__name__, game)
|
||||||
|
|
||||||
for key in ('id', 'venue'):
|
linescore = self.get_nested(game, 'linescore', default={})
|
||||||
_update(key)
|
|
||||||
|
|
||||||
for key in ('inning', 'outs'):
|
ret['id'] = game['gamePk']
|
||||||
_update(key, callback=self.force_int, default=0)
|
ret['inning'] = self.get_nested(linescore, 'currentInning', default=0)
|
||||||
|
ret['outs'] = self.get_nested(linescore, 'outs', default='')
|
||||||
|
ret['live_url'] = self.live_url % ret['id']
|
||||||
|
|
||||||
ret['live_url'] = self.live_url % game['gameday_link']
|
for team in ('away', 'home'):
|
||||||
|
team_data = self.get_nested(game, 'teams:%s' % team, default={})
|
||||||
|
|
||||||
for team in ('home', 'away'):
|
if team == 'home':
|
||||||
_update('%s_wins' % team, '%s_win' % team,
|
ret['venue'] = self.get_nested(team_data, 'venue:name', default='')
|
||||||
callback=self.force_int)
|
|
||||||
_update('%s_losses' % team, '%s_loss' % team,
|
|
||||||
callback=self.force_int)
|
|
||||||
_update('%s_score' % team, '%s_team_runs' % team,
|
|
||||||
callback=self.force_int, default=0)
|
|
||||||
|
|
||||||
_update('%s_abbrev' % team, '%s_name_abbrev' % team)
|
ret['%s_city' % team] = self.get_nested(
|
||||||
for item in ('city', 'name'):
|
team_data,
|
||||||
_update('%s_%s' % (team, item), '%s_team_%s' % (team, item))
|
'team:locationName',
|
||||||
|
default='')
|
||||||
|
ret['%s_name' % team] = self.get_nested(
|
||||||
|
team_data,
|
||||||
|
'team:teamName',
|
||||||
|
default='')
|
||||||
|
ret['%s_abbrev' % team] = self.get_nested(
|
||||||
|
team_data,
|
||||||
|
'team:abbreviation',
|
||||||
|
default='')
|
||||||
|
|
||||||
try:
|
ret['%s_wins' % team] = self.get_nested(
|
||||||
ret['status'] = game.get('status').lower().replace(' ', '_')
|
team_data,
|
||||||
except AttributeError:
|
'leagueRecord:wins',
|
||||||
# During warmup ret['status'] may be a dictionary. Treat these as
|
default=0)
|
||||||
# pregame
|
ret['%s_losses' % team] = self.get_nested(
|
||||||
ret['status'] = 'pregame'
|
team_data,
|
||||||
|
'leagueRecord:losses',
|
||||||
|
default=0)
|
||||||
|
|
||||||
|
ret['%s_score' % team] = self.get_nested(
|
||||||
|
linescore,
|
||||||
|
'teams:%s:runs' % team,
|
||||||
|
default=0)
|
||||||
|
|
||||||
for key in ('delay', 'postponed', 'suspended'):
|
for key in ('delay', 'postponed', 'suspended'):
|
||||||
ret[key] = ''
|
ret[key] = ''
|
||||||
|
|
||||||
|
ret['status'] = self.get_nested(game, 'status:detailedState', default='').replace(' ', '_').lower()
|
||||||
|
|
||||||
if ret['status'] == 'delayed_start':
|
if ret['status'] == 'delayed_start':
|
||||||
ret['status'] = 'pregame'
|
ret['status'] = 'pregame'
|
||||||
ret['delay'] = game.get('reason', 'Unknown')
|
ret['delay'] = self.get_nested(game, 'status:reason', default='Unknown')
|
||||||
elif ret['status'] == 'delayed':
|
elif ret['status'].startswith('delayed'):
|
||||||
ret['status'] = 'in_progress'
|
ret['status'] = 'in_progress'
|
||||||
ret['delay'] = game.get('reason', 'Unknown')
|
ret['delay'] = game['status']['detailedState'].split(':', 1)[-1].strip()
|
||||||
elif ret['status'] == 'postponed':
|
elif ret['status'] == 'postponed':
|
||||||
ret['postponed'] = game.get('reason', 'Unknown Reason')
|
ret['postponed'] = self.get_nested(game, 'status:reason', default='Unknown Reason')
|
||||||
elif ret['status'] == 'suspended':
|
elif ret['status'] == 'suspended':
|
||||||
ret['suspended'] = game.get('reason', 'Unknown Reason')
|
ret['suspended'] = self.get_nested(game, 'status:reason', default='Unknown Reason')
|
||||||
elif ret['status'] in ('game_over', 'completed_early'):
|
elif ret['status'].startswith('completed_early') or ret['status'] == 'game_over':
|
||||||
ret['status'] = 'final'
|
ret['status'] = 'final'
|
||||||
elif ret['status'] not in ('in_progress', 'final'):
|
elif ret['status'] not in ('in_progress', 'final'):
|
||||||
ret['status'] = 'pregame'
|
ret['status'] = 'pregame'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
inning = game.get('inning', '0')
|
ret['extra_innings'] = ret['inning'] \
|
||||||
ret['extra_innings'] = inning \
|
if ret['status'] == 'final' and ret['inning'] != 9 \
|
||||||
if ret['status'] == 'final' and int(inning) != 9 \
|
|
||||||
else ''
|
else ''
|
||||||
except ValueError:
|
except ValueError:
|
||||||
ret['extra_innings'] = ''
|
ret['extra_innings'] = ''
|
||||||
|
|
||||||
top_bottom = game.get('top_inning')
|
top_bottom = self.get_nested(linescore, 'inningHalf', default='').lower()
|
||||||
ret['top_bottom'] = self.inning_top if top_bottom == 'Y' \
|
ret['top_bottom'] = self.inning_top if top_bottom == 'top' \
|
||||||
else self.inning_bottom if top_bottom == 'N' \
|
else self.inning_bottom if top_bottom == 'bottom' \
|
||||||
else ''
|
else ''
|
||||||
|
|
||||||
time_zones = {
|
|
||||||
'PT': 'US/Pacific',
|
|
||||||
'MT': 'US/Mountain',
|
|
||||||
'CT': 'US/Central',
|
|
||||||
'ET': 'US/Eastern',
|
|
||||||
}
|
|
||||||
game_tz = pytz.timezone(
|
|
||||||
time_zones.get(
|
|
||||||
game.get('time_zone', 'ET'),
|
|
||||||
'US/Eastern'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
date_and_time = []
|
|
||||||
if 'resume_time_date' in game and game['resume_time_date']:
|
|
||||||
date_and_time.append(game['resume_time_date'])
|
|
||||||
elif 'time_date' in game and game['time_date']:
|
|
||||||
date_and_time.append(game['time_date'])
|
|
||||||
else:
|
|
||||||
keys = ('original_date', 'time')
|
|
||||||
if all(key in game for key in keys):
|
|
||||||
for key in keys:
|
|
||||||
if game[key]:
|
|
||||||
date_and_time.append(game[key])
|
|
||||||
if 'resume_ampm' in game and game['resume_ampm']:
|
|
||||||
date_and_time.append(game['resume_ampm'])
|
|
||||||
elif 'ampm' in game and game['ampm']:
|
|
||||||
date_and_time.append(game['ampm'])
|
|
||||||
|
|
||||||
game_time_str = ' '.join(date_and_time)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
game_time = datetime.strptime(game_time_str, '%Y/%m/%d %I:%M %p')
|
game_time = datetime.strptime(
|
||||||
|
self.get_nested(game, 'gameDate', default=''),
|
||||||
|
'%Y-%m-%dT%H:%M:%SZ')
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
# Log when the date retrieved from the API return doesn't match the
|
# Log when the date retrieved from the API return doesn't match the
|
||||||
# expected format (to help troubleshoot API changes), and set an
|
# expected format (to help troubleshoot API changes), and set an
|
||||||
@ -333,12 +321,12 @@ class MLB(ScoresBackend):
|
|||||||
self.logger.error(
|
self.logger.error(
|
||||||
'Error encountered determining %s game time for game %s:',
|
'Error encountered determining %s game time for game %s:',
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
game['id'],
|
game['gamePk'],
|
||||||
exc_info=True
|
exc_info=True
|
||||||
)
|
)
|
||||||
game_time = datetime(1970, 1, 1)
|
game_time = datetime(1970, 1, 1)
|
||||||
|
|
||||||
ret['start_time'] = game_tz.localize(game_time).astimezone()
|
ret['start_time'] = pytz.timezone('UTC').localize(game_time).astimezone()
|
||||||
|
|
||||||
self.logger.debug('Returned %s formatter data: %s',
|
self.logger.debug('Returned %s formatter data: %s',
|
||||||
self.__class__.__name__, ret)
|
self.__class__.__name__, ret)
|
||||||
|
Loading…
Reference in New Issue
Block a user