Update scores backends (#812)
1. Remove EPL as it has been broken for several years 2. Fix NBA to reflect backend API changes 3. Replace percent string formatting with f-strings now that i3pystatus only supports Python 3.6+
This commit is contained in:
parent
3976efe5cc
commit
b0826cf6ca
@ -29,57 +29,55 @@ class ScoresBackend(SettingsBase):
|
||||
# in team_colors.
|
||||
if len(self.team_colors) != len(self._default_colors):
|
||||
self.logger.debug(
|
||||
'Overriding %s team colors with: %s',
|
||||
self.__class__.__name__,
|
||||
self.team_colors
|
||||
f'Overriding {self.name} team colors '
|
||||
f'with: {self.team_colors}'
|
||||
)
|
||||
new_colors = copy.copy(self._default_colors)
|
||||
new_colors.update(self.team_colors)
|
||||
self.team_colors = new_colors
|
||||
self.logger.debug('%s team colors: %s',
|
||||
self.__class__.__name__, self.team_colors)
|
||||
self.logger.debug(f'{self.name} team colors: {self.team_colors}')
|
||||
|
||||
def api_request(self, url):
|
||||
self.logger.debug('Making %s API request to %s',
|
||||
self.__class__.__name__, url)
|
||||
self.logger.debug(f'Making {self.name} API request to {url}')
|
||||
try:
|
||||
with urlopen(url) as content:
|
||||
try:
|
||||
if content.url != url:
|
||||
self.logger.debug('Request to %s was redirected to %s',
|
||||
url, content.url)
|
||||
self.logger.debug(
|
||||
f'Request to {url} was redirected to {content.url}'
|
||||
)
|
||||
content_type = dict(content.getheaders())['Content-Type']
|
||||
mime_type = content_type.split(';')[0].lower()
|
||||
if 'json' not in mime_type:
|
||||
self.logger.debug('Response from %s is not JSON',
|
||||
content.url)
|
||||
return {}
|
||||
charset = re.search(r'charset=(.*)', content_type).group(1)
|
||||
except AttributeError:
|
||||
charset = 'utf-8'
|
||||
response_json = content.read().decode(charset).strip()
|
||||
if not response_json:
|
||||
self.logger.debug('JSON response from %s was blank', url)
|
||||
self.logger.debug(f'JSON response from {url} was blank')
|
||||
return {}
|
||||
try:
|
||||
response = json.loads(response_json)
|
||||
except json.decoder.JSONDecodeError as exc:
|
||||
self.logger.error('Error loading JSON: %s', exc)
|
||||
self.logger.debug('JSON text that failed to load: %s',
|
||||
response_json)
|
||||
self.logger.exception(f'Error encountered while loading JSON')
|
||||
self.logger.debug(f'Text that failed to load: {response_json}')
|
||||
return {}
|
||||
self.logger.log(5, 'API response: %s', response)
|
||||
self.logger.log(5, f'API response: {response}')
|
||||
return response
|
||||
except HTTPError as exc:
|
||||
self.logger.critical(
|
||||
'Error %s (%s) making request to %s',
|
||||
exc.code, exc.reason, exc.url,
|
||||
f'Error {exc.code} ({exc.reason}) making request to {exc.url}'
|
||||
)
|
||||
return {}
|
||||
except (ConnectionResetError, URLError) as exc:
|
||||
self.logger.critical('Error making request to %s: %s', url, exc)
|
||||
self.logger.critical(f'Error making request to {url}: {exc}')
|
||||
return {}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
'''
|
||||
Return the backend name
|
||||
'''
|
||||
return self.__class__.__name__
|
||||
|
||||
def get_api_date(self):
|
||||
'''
|
||||
Figure out the date to use for API requests. Assumes yesterday's date
|
||||
@ -94,7 +92,7 @@ class ScoresBackend(SettingsBase):
|
||||
try:
|
||||
api_date = datetime.strptime(self.date, '%Y-%m-%d')
|
||||
except (TypeError, ValueError):
|
||||
self.logger.warning('Invalid date \'%s\'', self.date)
|
||||
self.logger.warning(f"Invalid date '{self.date}'")
|
||||
|
||||
if api_date is None:
|
||||
utc_time = pytz.utc.localize(datetime.utcnow())
|
||||
@ -113,10 +111,11 @@ class ScoresBackend(SettingsBase):
|
||||
except ValueError:
|
||||
return number
|
||||
if 4 <= number <= 20:
|
||||
return '%d%s' % (number, 'th')
|
||||
suffix = 'th'
|
||||
else:
|
||||
ord_map = {1: 'st', 2: 'nd', 3: 'rd'}
|
||||
return '%d%s' % (number, ord_map.get(number % 10, 'th'))
|
||||
suffix = ordmap.get(number % 10, 'th')
|
||||
return f'{number}{suffix}'
|
||||
|
||||
@staticmethod
|
||||
def force_int(value):
|
||||
@ -135,8 +134,10 @@ class ScoresBackend(SettingsBase):
|
||||
key = int(key)
|
||||
data = data[key]
|
||||
except (KeyError, IndexError, TypeError):
|
||||
self.logger.debug('No %s data found at %s, falling back to %s',
|
||||
self.__class__.__name__, expr, repr(default))
|
||||
self.logger.debug(
|
||||
f'No {self.name} data found at {expr}, '
|
||||
f'falling back to {repr(default)}'
|
||||
)
|
||||
return default
|
||||
return callback(data)
|
||||
|
||||
@ -401,10 +402,8 @@ class Scores(Module):
|
||||
# Check to make sure the team abbreviation is valid
|
||||
if team_uc not in backend._valid_teams:
|
||||
raise ValueError(
|
||||
'Invalid %s team \'%s\'' % (
|
||||
backend.__class__.__name__,
|
||||
backend.favorite_teams[index]
|
||||
)
|
||||
f'Invalid {backend.name} team '
|
||||
f"'{backend.favorite_teams[index]}'"
|
||||
)
|
||||
backend.favorite_teams[index] = team_uc
|
||||
|
||||
@ -413,10 +412,8 @@ class Scores(Module):
|
||||
# Check to make sure the display order item is valid
|
||||
if order_lc not in backend._valid_display_order:
|
||||
raise ValueError(
|
||||
'Invalid %s display_order \'%s\'' % (
|
||||
backend.__class__.__name__,
|
||||
backend.display_order[index]
|
||||
)
|
||||
f'Invalid {backend.name} display_order '
|
||||
f"'{backend.display_order[index]}'"
|
||||
)
|
||||
backend.display_order[index] = order_lc
|
||||
|
||||
@ -432,12 +429,11 @@ class Scores(Module):
|
||||
self.condition.wait(self.interval)
|
||||
self.check_scores(force='scheduled')
|
||||
except Exception:
|
||||
msg = 'Exception in {thread} at {time}, module {name}'.format(
|
||||
thread=threading.current_thread().name,
|
||||
time=time.strftime('%c'),
|
||||
name=self.__class__.__name__,
|
||||
thread = threading.current_thread().name,
|
||||
timestamp = time.strftime('%c')
|
||||
self.logger.exception(
|
||||
f'Exception in {thread} at {timestamp}, module {self.name}'
|
||||
)
|
||||
self.logger.error(msg, exc_info=True)
|
||||
|
||||
@property
|
||||
def current_backend(self):
|
||||
@ -465,11 +461,8 @@ class Scores(Module):
|
||||
cur_index = self.current_scroll_index
|
||||
if cur_index is None:
|
||||
self.logger.debug(
|
||||
'Cannot scroll, no tracked {backend} games for '
|
||||
'{date:%Y-%m-%d}'.format(
|
||||
backend=self.current_backend.__class__.__name__,
|
||||
date=self.current_backend.date,
|
||||
)
|
||||
f'Cannot scroll, no tracked {self.current_backend.name} games '
|
||||
f'for {self.current_backend.date:%Y-%m-%d}'
|
||||
)
|
||||
else:
|
||||
new_index = (cur_index + step) % len(self.current_backend.scroll_order)
|
||||
@ -480,22 +473,17 @@ class Scores(Module):
|
||||
# self.current_scroll_index serves as a shorthand.
|
||||
self.game_map[self.backend_id] = new_index
|
||||
self.logger.debug(
|
||||
'Scrolled from %s game %d (ID: %s) to %d (ID: %s)',
|
||||
self.current_backend.__class__.__name__,
|
||||
cur_index,
|
||||
cur_id,
|
||||
new_index,
|
||||
self.current_backend.scroll_order[new_index],
|
||||
f'Scrolled from {self.current_backend.name} '
|
||||
f'game {cur_index} (ID: {cur_id}) to {new_index} '
|
||||
f'(ID: {self.current_backend.scroll_order[new_index]})'
|
||||
)
|
||||
self.refresh_display()
|
||||
else:
|
||||
self.logger.debug(
|
||||
'Cannot scroll, only one tracked {backend} game '
|
||||
'(ID: {id_}) for {date:%Y-%m-%d}'.format(
|
||||
backend=self.current_backend.__class__.__name__,
|
||||
id_=self.current_game_id,
|
||||
date=self.current_backend.date,
|
||||
)
|
||||
f'Cannot scroll, only one tracked '
|
||||
f'{self.current_backend.name} game '
|
||||
f'(ID: {self.current_game_id}) for '
|
||||
f'{self.current_backend.date:%Y-%m-%d}'
|
||||
)
|
||||
|
||||
def cycle_backend(self, step=1):
|
||||
@ -509,9 +497,8 @@ class Scores(Module):
|
||||
# Set the new backend
|
||||
self.backend_id = (self.backend_id + step) % len(self.backends)
|
||||
self.logger.debug(
|
||||
'Changed scores backend from %s to %s',
|
||||
self.backends[old].__class__.__name__,
|
||||
self.current_backend.__class__.__name__,
|
||||
f'Changed scores backend from {self.backends[old].name} to '
|
||||
f'{self.current_backend.name}'
|
||||
)
|
||||
# Display the score for the new backend. This gets rid of lag between
|
||||
# when the mouse is clicked and when the new backend is shown, caused
|
||||
@ -524,15 +511,14 @@ class Scores(Module):
|
||||
if self.current_backend.games:
|
||||
self.game_map[self.backend_id] = 0
|
||||
self.logger.debug(
|
||||
'Resetting to first game in %s scroll list (ID: %s)',
|
||||
self.current_backend.__class__.__name__,
|
||||
self.current_game_id,
|
||||
f'Resetting to first game in {self.current_backend.name} '
|
||||
f'scroll list (ID: {self.current_game_id})'
|
||||
)
|
||||
self.refresh_display()
|
||||
else:
|
||||
self.logger.debug(
|
||||
'No %s games, cannot reset to first game in scroll list',
|
||||
self.current_backend.__class__.__name__,
|
||||
f'No {self.current_backend.name} games, cannot reset to first '
|
||||
'game in scroll list',
|
||||
)
|
||||
|
||||
def launch_web(self):
|
||||
@ -541,7 +527,7 @@ class Scores(Module):
|
||||
live_url = self.current_backend.scoreboard_url
|
||||
else:
|
||||
live_url = game['live_url']
|
||||
self.logger.debug('Launching %s in browser', live_url)
|
||||
self.logger.debug(f'Launching {live_url} in browser')
|
||||
user_open(live_url)
|
||||
|
||||
@require(internet)
|
||||
@ -550,27 +536,30 @@ class Scores(Module):
|
||||
if not self.current_backend.last_update:
|
||||
update_needed = True
|
||||
self.logger.debug(
|
||||
'Performing initial %s score check',
|
||||
self.current_backend.__class__.__name__,
|
||||
f'Performing initial {self.current_backend.name} score check'
|
||||
)
|
||||
elif force:
|
||||
update_needed = True
|
||||
self.logger.debug(
|
||||
'%s score check triggered (%s)',
|
||||
self.current_backend.__class__.__name__,
|
||||
force
|
||||
f'{self.current_backend.name} score check triggered ({force})'
|
||||
)
|
||||
else:
|
||||
update_diff = time.time() - self.current_backend.last_update
|
||||
msg = ('Seconds since last %s update (%f) ' %
|
||||
(self.current_backend.__class__.__name__, update_diff))
|
||||
msg = (
|
||||
f'Seconds since last {self.current_backend.name} update '
|
||||
f'({update_diff}) '
|
||||
)
|
||||
if update_diff >= self.interval:
|
||||
update_needed = True
|
||||
msg += ('meets or exceeds update interval (%d), update '
|
||||
'triggered' % self.interval)
|
||||
msg += (
|
||||
f'meets or exceeds update interval ({self.interval}), '
|
||||
'update triggered'
|
||||
)
|
||||
else:
|
||||
msg += ('does not exceed update interval (%d), update '
|
||||
'skipped' % self.interval)
|
||||
msg += (
|
||||
f'does not exceed update interval ({self.interval}), '
|
||||
'update skipped'
|
||||
)
|
||||
self.logger.debug(msg)
|
||||
|
||||
if update_needed:
|
||||
@ -584,10 +573,8 @@ class Scores(Module):
|
||||
# status.
|
||||
if cur_id is None:
|
||||
self.logger.debug(
|
||||
'No tracked {backend} games for {date:%Y-%m-%d}'.format(
|
||||
backend=self.current_backend.__class__.__name__,
|
||||
date=self.current_backend.date,
|
||||
)
|
||||
f'No tracked {self.current_backend.name} games for '
|
||||
f'{self.current_backend.date:%Y-%m-%d}'
|
||||
)
|
||||
else:
|
||||
cur_pos = self.game_map[self.backend_id]
|
||||
@ -595,20 +582,15 @@ class Scores(Module):
|
||||
if cur_pos != new_pos:
|
||||
self.game_map[self.backend_id] = new_pos
|
||||
self.logger.debug(
|
||||
'Scroll position for current %s game (%s) updated '
|
||||
'from %d to %d',
|
||||
self.current_backend.__class__.__name__,
|
||||
cur_id,
|
||||
cur_pos,
|
||||
new_pos,
|
||||
f'Scroll position for current '
|
||||
f'{self.current_backend.name} game '
|
||||
f'({cur_id}) updated from {cur_pos} to {new_pos}'
|
||||
)
|
||||
else:
|
||||
self.logger.debug(
|
||||
'Scroll position (%d) for current %s game (ID: %s) '
|
||||
'unchanged',
|
||||
cur_pos,
|
||||
self.current_backend.__class__.__name__,
|
||||
cur_id,
|
||||
f'Scroll position ({cur_pos}) for current '
|
||||
f'{self.current_backend.name} game (ID: {cur_id}) '
|
||||
'unchanged'
|
||||
)
|
||||
else:
|
||||
# Reset the index to 0 if there are any tracked games,
|
||||
@ -617,18 +599,15 @@ class Scores(Module):
|
||||
if self.current_backend.games:
|
||||
self.game_map[self.backend_id] = 0
|
||||
self.logger.debug(
|
||||
'Tracked %s games updated, setting scroll position to '
|
||||
'0 (ID: %s)',
|
||||
self.current_backend.__class__.__name__,
|
||||
self.current_game_id
|
||||
f'Tracked {self.current_backend.name} games updated, '
|
||||
f'setting scroll position to 0 '
|
||||
f'(ID: {self.current_game_id})'
|
||||
)
|
||||
else:
|
||||
self.game_map[self.backend_id] = None
|
||||
self.logger.debug(
|
||||
'No tracked {backend} games for {date:%Y-%m-%d}'.format(
|
||||
backend=self.current_backend.__class__.__name__,
|
||||
date=self.current_backend.date,
|
||||
)
|
||||
f'No tracked {self.current_backend.name} games for '
|
||||
f'{self.current_backend.date:%Y-%m-%d}'
|
||||
)
|
||||
self.current_backend.last_update = time.time()
|
||||
self.refresh_display()
|
||||
@ -644,15 +623,12 @@ class Scores(Module):
|
||||
else:
|
||||
game = copy.copy(self.current_game)
|
||||
|
||||
fstr = str(getattr(
|
||||
self.current_backend,
|
||||
'format_%s' % game['status']
|
||||
))
|
||||
fstr = str(getattr(self.current_backend, f'format_{game["status"]}'))
|
||||
|
||||
for team in ('home', 'away'):
|
||||
abbrev_key = '%s_abbrev' % team
|
||||
abbrev_key = f'{team}_abbrev'
|
||||
# Set favorite icon, if applicable
|
||||
game['%s_favorite' % team] = self.favorite_icon \
|
||||
game[f'{team}_favorite'] = self.favorite_icon \
|
||||
if game[abbrev_key] in self.current_backend.favorite_teams \
|
||||
else ''
|
||||
|
||||
@ -663,10 +639,9 @@ class Scores(Module):
|
||||
)
|
||||
if color is not None:
|
||||
for item in ('abbrev', 'city', 'name', 'name_short'):
|
||||
key = '%s_%s' % (team, item)
|
||||
key = f'{team}_{item}'
|
||||
if key in game:
|
||||
val = '<span color="%s">%s</span>' % (color, game[key])
|
||||
game[key] = val
|
||||
game[key] = f'<span color="{color}">{game[key]}</span>'
|
||||
|
||||
game['scroll'] = self.scroll_arrow \
|
||||
if len(self.current_backend.games) > 1 \
|
||||
|
@ -1,383 +0,0 @@
|
||||
from i3pystatus.core.util import internet, require
|
||||
from i3pystatus.scores import ScoresBackend
|
||||
|
||||
import copy
|
||||
import pytz
|
||||
import time
|
||||
from collections import namedtuple
|
||||
from datetime import datetime
|
||||
|
||||
LIVE_URL = 'http://live.premierleague.com/#/gameweek/%s/matchday/%s/match/%s'
|
||||
CONTEXT_URL = 'http://live.premierleague.com/syndicationdata/context.json'
|
||||
SCOREBOARD_URL = 'http://live.premierleague.com/'
|
||||
API_URL = 'http://live.premierleague.com/syndicationdata/competitionId=%s/seasonId=%s/gameWeekId=%s/scores.json'
|
||||
STATS_URL = 'http://live.premierleague.com/syndicationdata/competitionId=%s/seasonId=%s/matchDayId=%s/league-table.json'
|
||||
MATCH_DETAILS_URL = 'http://live.premierleague.com/syndicationdata/competitionId=%s/seasonId=%s/matchDayId=%s/matchId=%s/match-details.json'
|
||||
|
||||
MATCH_STATUS_PREGAME = 1
|
||||
MATCH_STATUS_IN_PROGRESS = 2
|
||||
MATCH_STATUS_FINAL = 3
|
||||
MATCH_STATUS_HALFTIME = 4
|
||||
|
||||
|
||||
class EPL(ScoresBackend):
|
||||
'''
|
||||
Backend to retrieve scores from the English Premier League. For usage
|
||||
examples, see :py:mod:`here <.scores>`.
|
||||
|
||||
.. rubric:: Promotion / Relegation
|
||||
|
||||
Due to promotion/relegation, the **team_colors** configuration will
|
||||
eventuall become out of date. When this happens, it will be necessary to
|
||||
manually set the colors for the newly-promoted teams until the source for
|
||||
this module is updated. An example of setting colors for newly promoted
|
||||
teams can be seen below:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from i3pystatus import Status
|
||||
from i3pystatus.scores import epl
|
||||
|
||||
status = Status()
|
||||
|
||||
status.register(
|
||||
'scores',
|
||||
hints={'markup': 'pango'},
|
||||
colorize_teams=True,
|
||||
backends=[
|
||||
epl.EPL(
|
||||
teams=['LIV'],
|
||||
team_colors={
|
||||
'ABC': '#1D78CA',
|
||||
'DEF': '#8AFEC3',
|
||||
'GHI': '#33FA6D',
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
status.run()
|
||||
|
||||
.. rubric:: Available formatters
|
||||
|
||||
* `{home_name}` — Name of home team (e.g. **Tottenham Hotspur**)
|
||||
* `{home_name_short}` — Shortened team name (e.g. **Spurs**)
|
||||
* `{home_abbrev}` — 2 or 3-letter abbreviation for home team's city (e.g.
|
||||
**TOT**)
|
||||
* `{home_score}` — Home team's current score
|
||||
* `{home_wins}` — Home team's number of wins
|
||||
* `{home_losses}` — Home team's number of losses
|
||||
* `{home_draws}` — Home team's number of draws
|
||||
* `{home_points}` — Home team's number of standings points
|
||||
* `{home_favorite}` — Displays the value for the :py:mod:`.scores` module's
|
||||
``favorite`` attribute, if the home team is one of the teams being
|
||||
followed. Otherwise, this formatter will be blank.
|
||||
* `{away_name}` — Name of away team (e.g. **Manchester United**)
|
||||
* `{away_name_short}` — Name of away team's city (e.g. **Man Utd**)
|
||||
* `{away_abbrev}` — 2 or 3-letter abbreviation for away team's name (e.g.
|
||||
**MUN**)
|
||||
* `{away_score}` — Away team's current score
|
||||
* `{away_wins}` — Away team's number of wins
|
||||
* `{away_losses}` — Away team's number of losses
|
||||
* `{away_draws}` — Away team's number of draws
|
||||
* `{away_points}` — Away team's number of standings points
|
||||
* `{away_favorite}` — Displays the value for the :py:mod:`.scores` module's
|
||||
``favorite`` attribute, if the away team is one of the teams being
|
||||
followed. Otherwise, this formatter will be blank.
|
||||
* `{minute}` — Current minute of game when in progress
|
||||
* `{start_time}` — Start time of game in system's localtime (supports
|
||||
strftime formatting, e.g. `{start_time:%I:%M %p}`)
|
||||
|
||||
.. rubric:: Team abbreviations
|
||||
|
||||
* **ARS** — Arsenal
|
||||
* **AVL** — Aston Villa
|
||||
* **BOU** — Bournemouth
|
||||
* **CHE** — Chelsea
|
||||
* **CRY** — Crystal Palace
|
||||
* **EVE** — Everton
|
||||
* **LEI** — Leicester City
|
||||
* **LIV** — Liverpool
|
||||
* **MCI** — Manchester City
|
||||
* **MUN** — Manchester United
|
||||
* **NEW** — Newcastle United
|
||||
* **NOR** — Norwich City
|
||||
* **SOU** — Southampton
|
||||
* **STK** — Stoke City
|
||||
* **SUN** — Sunderland Association
|
||||
* **SWA** — Swansea City
|
||||
* **TOT** — Tottenham Hotspur
|
||||
* **WAT** — Watford
|
||||
* **WBA** — West Bromwich Albion
|
||||
* **WHU** — West Ham United
|
||||
'''
|
||||
interval = 300
|
||||
|
||||
settings = (
|
||||
('favorite_teams', 'List of abbreviations of favorite teams. Games '
|
||||
'for these teams will appear first in the scroll '
|
||||
'list. A detailed description of how games are '
|
||||
'ordered can be found '
|
||||
':ref:`here <scores-game-order>`.'),
|
||||
('all_games', 'If set to ``True``, all games will be present in '
|
||||
'the scroll list. If set to ``False``, then only '
|
||||
'games from **favorite_teams** will be present in '
|
||||
'the scroll list.'),
|
||||
('display_order', 'When **all_games** is set to ``True``, this '
|
||||
'option will dictate the order in which games from '
|
||||
'teams not in **favorite_teams** are displayed'),
|
||||
('format_no_games', 'Format used when no tracked games are scheduled '
|
||||
'for the current day (does not support formatter '
|
||||
'placeholders)'),
|
||||
('format_pregame', 'Format used when the game has not yet started'),
|
||||
('format_in_progress', 'Format used when the game is in progress'),
|
||||
('format_final', 'Format used when the game is complete'),
|
||||
('team_colors', 'Dictionary mapping team abbreviations to hex color '
|
||||
'codes. If overridden, the passed values will be '
|
||||
'merged with the defaults, so it is not necessary to '
|
||||
'define all teams if specifying this value.'),
|
||||
('date', 'Date for which to display game scores, in **YYYY-MM-DD** '
|
||||
'format. If unspecified, the date will be determined by '
|
||||
'the return value of an API call to the **context_url**. '
|
||||
'Due to API limitations, the date can presently only be '
|
||||
'overridden to another date in the current week. This '
|
||||
'option exists primarily for troubleshooting purposes.'),
|
||||
('live_url', 'URL string to launch EPL Live Match Centre. This value '
|
||||
'should not need to be changed.'),
|
||||
('scoreboard_url', 'Link to the EPL scoreboard page. Like '
|
||||
'**live_url**, this value should not need to be '
|
||||
'changed.'),
|
||||
('api_url', 'Alternate URL string from which to retrieve score data. '
|
||||
'Like **live_url**, this value should not need to be '
|
||||
'changed.'),
|
||||
('stats_url', 'Alternate URL string from which to retrieve team '
|
||||
'statistics. Like **live_url**, this value should not '
|
||||
'need to be changed.'),
|
||||
('match_details_url', 'Alternate URL string from which to retrieve '
|
||||
'match details. Like **live_url**, this value '
|
||||
'should not need to be changed.'),
|
||||
)
|
||||
|
||||
required = ()
|
||||
|
||||
_default_colors = {
|
||||
'ARS': '#ED1B22',
|
||||
'AVL': '#94BEE5',
|
||||
'BOU': '#CB0B0F',
|
||||
'CHE': '#195FAF',
|
||||
'CRY': '#195FAF',
|
||||
'EVE': '#004F9E',
|
||||
'LEI': '#304FB6',
|
||||
'LIV': '#D72129',
|
||||
'MCI': '#74B2E0',
|
||||
'MUN': '#DD1921',
|
||||
'NEW': '#06B3EB',
|
||||
'NOR': '#00A651',
|
||||
'SOU': '#DB1C26',
|
||||
'STK': '#D81732',
|
||||
'SUN': '#BC0007',
|
||||
'SWA': '#B28250',
|
||||
'TOT': '#DADADA',
|
||||
'WAT': '#E4D500',
|
||||
'WBA': '#B43C51',
|
||||
'WHU': '#9DE4FA',
|
||||
}
|
||||
|
||||
_valid_display_order = ['in_progress', 'final', 'pregame']
|
||||
|
||||
display_order = _valid_display_order
|
||||
format_no_games = 'EPL: No games'
|
||||
format_pregame = '[{scroll} ]EPL: [{away_favorite} ]{away_abbrev} ({away_points}, {away_wins}-{away_losses}-{away_draws}) at [{home_favorite} ]{home_abbrev} ({home_points}, {home_wins}-{home_losses}-{home_draws}) {start_time:%H:%M %Z}'
|
||||
format_in_progress = '[{scroll} ]EPL: [{away_favorite} ]{away_abbrev} {away_score}[ ({away_power_play})], [{home_favorite} ]{home_abbrev} {home_score}[ ({home_power_play})] ({minute})'
|
||||
format_final = '[{scroll} ]EPL: [{away_favorite} ]{away_abbrev} {away_score} ({away_points}, {away_wins}-{away_losses}-{away_draws}) at [{home_favorite} ]{home_abbrev} {home_score} ({home_points}, {home_wins}-{home_losses}-{home_draws}) (Final)'
|
||||
team_colors = _default_colors
|
||||
context_url = CONTEXT_URL
|
||||
live_url = LIVE_URL
|
||||
scoreboard_url = SCOREBOARD_URL
|
||||
api_url = API_URL
|
||||
stats_url = STATS_URL
|
||||
match_details_url = MATCH_DETAILS_URL
|
||||
|
||||
def get_api_date(self):
|
||||
# NOTE: We're not really using this date for EPL API calls, but we do
|
||||
# need it to allow for a 'date' param to override which date we use for
|
||||
# scores.
|
||||
if self.date is not None and not isinstance(self.date, datetime):
|
||||
try:
|
||||
self.date = datetime.strptime(self.date, '%Y-%m-%d')
|
||||
except (TypeError, ValueError):
|
||||
self.logger.warning('Invalid date \'%s\'', self.date)
|
||||
|
||||
if self.date is None:
|
||||
self.date = datetime.strptime(self.context.date, '%Y%m%d')
|
||||
|
||||
def get_context(self):
|
||||
response = self.api_request(self.context_url)
|
||||
if not response:
|
||||
# There is no context data, but we still need a date to use in
|
||||
# __init__.py to log that there are no games for the given date.
|
||||
# Fall back to the parent class' function to set a date.
|
||||
super(EPL, self).get_api_date()
|
||||
return False
|
||||
context_tuple = namedtuple(
|
||||
'Context',
|
||||
('competition', 'date', 'game_week', 'match_day', 'season')
|
||||
)
|
||||
self.context = context_tuple(
|
||||
*[
|
||||
response.get(x, '')
|
||||
for x in ('competitionId', 'currentDay', 'gameWeekId',
|
||||
'matchDayId', 'seasonId')
|
||||
]
|
||||
)
|
||||
return True
|
||||
|
||||
def get_team_stats(self):
|
||||
ret = {}
|
||||
url = self.stats_url % (self.context.competition,
|
||||
self.context.season,
|
||||
self.context.match_day)
|
||||
for item in self.api_request(url).get('Data', []):
|
||||
try:
|
||||
key = item.pop('TeamCode')
|
||||
except KeyError:
|
||||
self.logger.debug('Error occurred obtaining %s team stats',
|
||||
self.__class__.__name__,
|
||||
exc_info=True)
|
||||
continue
|
||||
ret[key] = item
|
||||
return ret
|
||||
|
||||
def get_minute(self, data, id_):
|
||||
match_status = data[id_].get('StatusId', MATCH_STATUS_PREGAME)
|
||||
if match_status == MATCH_STATUS_HALFTIME:
|
||||
return 'Halftime'
|
||||
if match_status == MATCH_STATUS_IN_PROGRESS:
|
||||
url = self.match_details_url % (self.context.competition,
|
||||
self.context.season,
|
||||
data[id_].get('MatchDayId', ''),
|
||||
id_)
|
||||
try:
|
||||
response = self.api_request(url)
|
||||
return '%s\'' % response['Data']['Minute']
|
||||
except (KeyError, TypeError):
|
||||
return '?\''
|
||||
else:
|
||||
return '?\''
|
||||
|
||||
def check_scores(self):
|
||||
if not self.get_context():
|
||||
data = team_game_map = {}
|
||||
else:
|
||||
self.get_api_date()
|
||||
|
||||
url = self.api_url % (self.context.competition,
|
||||
self.context.season,
|
||||
self.context.game_week)
|
||||
|
||||
for item in self.api_request(url).get('Data', []):
|
||||
if item.get('Key', '') == self.date.strftime('%Y%m%d'):
|
||||
game_list = item.get('Scores', [])
|
||||
break
|
||||
else:
|
||||
game_list = []
|
||||
|
||||
self.logger.debug('game_list = %s', game_list)
|
||||
|
||||
team_stats = self.get_team_stats()
|
||||
|
||||
# Convert list of games to dictionary for easy reference later on
|
||||
data = {}
|
||||
team_game_map = {}
|
||||
for game in game_list:
|
||||
try:
|
||||
id_ = game['Id']
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
try:
|
||||
for key in ('HomeTeam', 'AwayTeam'):
|
||||
team = game[key]['Code'].upper()
|
||||
if team in self.favorite_teams:
|
||||
team_game_map.setdefault(team, []).append(id_)
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
data[id_] = game
|
||||
# Merge in the team stats, because they are not returned in the
|
||||
# initial API request.
|
||||
for key in ('HomeTeam', 'AwayTeam'):
|
||||
team = game[key]['Code'].upper()
|
||||
data[id_][key]['Stats'] = team_stats.get(team, {})
|
||||
# Add the minute, if applicable
|
||||
data[id_]['Minute'] = self.get_minute(data, id_)
|
||||
|
||||
self.interpret_api_return(data, team_game_map)
|
||||
|
||||
def process_game(self, game):
|
||||
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.__class__.__name__, game)
|
||||
|
||||
_update('id', 'Id')
|
||||
_update('minute', 'Minute')
|
||||
ret['live_url'] = self.live_url % (self.context.game_week,
|
||||
self.context.match_day,
|
||||
ret['id'])
|
||||
|
||||
status_map = {
|
||||
MATCH_STATUS_PREGAME: 'pregame',
|
||||
MATCH_STATUS_IN_PROGRESS: 'in_progress',
|
||||
MATCH_STATUS_FINAL: 'final',
|
||||
MATCH_STATUS_HALFTIME: 'in_progress',
|
||||
}
|
||||
status_code = game.get('StatusId')
|
||||
if status_code is None:
|
||||
self.logger.debug('%s game %s is missing StatusId',
|
||||
self.__class__.__name__, ret['id'])
|
||||
status_code = 1
|
||||
ret['status'] = status_map[status_code]
|
||||
|
||||
for ret_key, game_key in (('home', 'HomeTeam'), ('away', 'AwayTeam')):
|
||||
_update('%s_score' % ret_key, '%s:Score' % game_key, default=0)
|
||||
_update('%s_name' % ret_key, '%s:Name' % game_key)
|
||||
_update('%s_name_short' % ret_key, '%s:ShortName' % game_key)
|
||||
_update('%s_abbrev' % ret_key, '%s:Code' % game_key)
|
||||
_update('%s_wins' % ret_key, '%s:Stats:Won' % game_key, default=0)
|
||||
_update('%s_losses' % ret_key, '%s:Stats:Lost' % game_key)
|
||||
_update('%s_draws' % ret_key, '%s:Stats:Drawn' % game_key)
|
||||
_update('%s_points' % ret_key, '%s:Stats:Points' % game_key)
|
||||
|
||||
try:
|
||||
game_time = datetime.strptime(
|
||||
game.get('DateTime', ''),
|
||||
'%Y-%m-%dT%H:%M:%S'
|
||||
)
|
||||
except ValueError as exc:
|
||||
# Log when the date retrieved from the API return doesn't match the
|
||||
# expected format (to help troubleshoot API changes), and set an
|
||||
# actual datetime so format strings work as expected. The times
|
||||
# will all be wrong, but the logging here will help us make the
|
||||
# necessary changes to adapt to any API changes.
|
||||
self.logger.error(
|
||||
'Error encountered determining game time for %s game %s:',
|
||||
self.__class__.__name__,
|
||||
ret['id'],
|
||||
exc_info=True
|
||||
)
|
||||
game_time = datetime.datetime(1970, 1, 1)
|
||||
|
||||
london = pytz.timezone('Europe/London')
|
||||
ret['start_time'] = london.localize(game_time).astimezone()
|
||||
|
||||
self.logger.debug('Returned %s formatter data: %s',
|
||||
self.__class__.__name__, ret)
|
||||
|
||||
return ret
|
@ -9,9 +9,9 @@ import time
|
||||
from datetime import datetime
|
||||
from urllib.request import urlopen
|
||||
|
||||
LIVE_URL = 'https://www.mlb.com/gameday/%s'
|
||||
LIVE_URL = 'https://www.mlb.com/gameday/{id}'
|
||||
SCOREBOARD_URL = 'http://m.mlb.com/scoreboard'
|
||||
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'
|
||||
API_URL = 'https://statsapi.mlb.com/api/v1/schedule?sportId=1,51&date={date:%Y-%m-%d}&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):
|
||||
@ -188,7 +188,7 @@ class MLB(ScoresBackend):
|
||||
@require(internet)
|
||||
def check_scores(self):
|
||||
self.get_api_date()
|
||||
url = self.api_url % (self.date.year, self.date.month, self.date.day)
|
||||
url = self.api_url.format(date=self.date)
|
||||
|
||||
game_list = self.get_nested(
|
||||
self.api_request(url),
|
||||
@ -231,44 +231,43 @@ class MLB(ScoresBackend):
|
||||
def process_game(self, game):
|
||||
ret = {}
|
||||
|
||||
self.logger.debug('Processing %s game data: %s',
|
||||
self.__class__.__name__, game)
|
||||
self.logger.debug(f'Processing {self.name} game data: {game}')
|
||||
|
||||
linescore = self.get_nested(game, 'linescore', default={})
|
||||
|
||||
ret['id'] = game['gamePk']
|
||||
ret['inning'] = self.get_nested(linescore, 'currentInning', default=0)
|
||||
ret['outs'] = self.get_nested(linescore, 'outs')
|
||||
ret['live_url'] = self.live_url % ret['id']
|
||||
ret['live_url'] = self.live_url.format(id=ret['id'])
|
||||
|
||||
for team in ('away', 'home'):
|
||||
team_data = self.get_nested(game, 'teams:%s' % team, default={})
|
||||
team_data = self.get_nested(game, f'teams:{team}', default={})
|
||||
|
||||
if team == 'home':
|
||||
ret['venue'] = self.get_nested(team_data, 'venue:name')
|
||||
|
||||
ret['%s_city' % team] = self.get_nested(
|
||||
ret[f'{team}_city'] = self.get_nested(
|
||||
team_data,
|
||||
'team:locationName')
|
||||
ret['%s_name' % team] = self.get_nested(
|
||||
ret[f'{team}_name'] = self.get_nested(
|
||||
team_data,
|
||||
'team:teamName')
|
||||
ret['%s_abbrev' % team] = self.get_nested(
|
||||
ret[f'{team}_abbrev'] = self.get_nested(
|
||||
team_data,
|
||||
'team:abbreviation')
|
||||
|
||||
ret['%s_wins' % team] = self.get_nested(
|
||||
ret[f'{team}_wins'] = self.get_nested(
|
||||
team_data,
|
||||
'leagueRecord:wins',
|
||||
default=0)
|
||||
ret['%s_losses' % team] = self.get_nested(
|
||||
ret[f'{team}_losses'] = self.get_nested(
|
||||
team_data,
|
||||
'leagueRecord:losses',
|
||||
default=0)
|
||||
|
||||
ret['%s_score' % team] = self.get_nested(
|
||||
ret[f'{team}_score'] = self.get_nested(
|
||||
linescore,
|
||||
'teams:%s:runs' % team,
|
||||
f'teams:{team}:runs',
|
||||
default=0)
|
||||
|
||||
for key in ('delay', 'postponed', 'suspended'):
|
||||
@ -313,17 +312,14 @@ class MLB(ScoresBackend):
|
||||
# actual datetime so format strings work as expected. The times
|
||||
# will all be wrong, but the logging here will help us make the
|
||||
# necessary changes to adapt to any API changes.
|
||||
self.logger.error(
|
||||
'Error encountered determining %s game time for game %s:',
|
||||
self.__class__.__name__,
|
||||
game['gamePk'],
|
||||
exc_info=True
|
||||
self.logger.exception(
|
||||
f'Error encountered determining {self.name} game time for '
|
||||
f'game {game["gamePk"]}'
|
||||
)
|
||||
game_time = datetime(1970, 1, 1)
|
||||
|
||||
ret['start_time'] = pytz.timezone('UTC').localize(game_time).astimezone()
|
||||
|
||||
self.logger.debug('Returned %s formatter data: %s',
|
||||
self.__class__.__name__, ret)
|
||||
self.logger.debug(f'Returned {self.name} formatter data: {ret}')
|
||||
|
||||
return ret
|
||||
|
@ -6,10 +6,8 @@ import pytz
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
LIVE_URL = 'http://www.nba.com/gametracker/#/%s/lp'
|
||||
SCOREBOARD_URL = 'http://www.nba.com/scores'
|
||||
API_URL = 'http://data.nba.com/data/10s/json/cms/noseason/scoreboard/%04d%02d%02d/games.json'
|
||||
STANDINGS_URL = 'http://data.nba.com/data/json/cms/%s/league/standings.json'
|
||||
LIVE_URL = 'https://www.nba.com/game/{id}'
|
||||
API_URL = 'https://cdn.nba.com/static/json/liveData/scoreboard/todaysScoreboard_00.json'
|
||||
|
||||
|
||||
class NBA(ScoresBackend):
|
||||
@ -43,7 +41,6 @@ class NBA(ScoresBackend):
|
||||
followed. Otherwise, this formatter will be blank.
|
||||
* `{time_remaining}` — Time remaining in the current quarter/OT period
|
||||
* `{quarter}` — Number of the current quarter
|
||||
* `{venue}` — Name of arena where game is being played
|
||||
* `{start_time}` — Start time of game in system's localtime (supports
|
||||
strftime formatting, e.g. `{start_time:%I:%M %p}`)
|
||||
* `{overtime}` — If the game ended in overtime, this formatter will show
|
||||
@ -171,66 +168,32 @@ class NBA(ScoresBackend):
|
||||
format_final = '[{scroll} ]NBA: [{away_favorite} ]{away_abbrev} {away_score} ({away_wins}-{away_losses}) at [{home_favorite} ]{home_abbrev} {home_score} ({home_wins}-{home_losses}) (Final[/{overtime}])'
|
||||
team_colors = _default_colors
|
||||
live_url = LIVE_URL
|
||||
scoreboard_url = SCOREBOARD_URL
|
||||
api_url = API_URL
|
||||
standings_url = STANDINGS_URL
|
||||
|
||||
def check_scores(self):
|
||||
self.get_api_date()
|
||||
url = self.api_url % (self.date.year, self.date.month, self.date.day)
|
||||
|
||||
response = self.api_request(url)
|
||||
game_list = self.get_nested(response,
|
||||
'sports_content:games:game',
|
||||
default=[])
|
||||
|
||||
standings_year = self.get_nested(
|
||||
response,
|
||||
'sports_content:sports_meta:season_meta:standings_season_year',
|
||||
default=self.date.year,
|
||||
)
|
||||
|
||||
stats_list = self.get_nested(
|
||||
self.api_request(self.standings_url % standings_year),
|
||||
'sports_content:standings:team',
|
||||
default=[],
|
||||
)
|
||||
team_stats = {}
|
||||
for item in stats_list:
|
||||
try:
|
||||
key = item.pop('abbreviation')
|
||||
except KeyError:
|
||||
self.logger.debug('Error occurred obtaining team stats',
|
||||
exc_info=True)
|
||||
continue
|
||||
team_stats[key] = item.get('team_stats', {})
|
||||
|
||||
self.logger.debug('%s team stats: %s',
|
||||
self.__class__.__name__, team_stats)
|
||||
response = self.api_request(self.api_url)
|
||||
game_list = self.get_nested(response, 'scoreboard:games', default=[])
|
||||
|
||||
# Convert list of games to dictionary for easy reference later on
|
||||
data = {}
|
||||
team_game_map = {}
|
||||
for game in game_list:
|
||||
try:
|
||||
id_ = game['game_url']
|
||||
id_ = game['gameId']
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
try:
|
||||
for key in ('home', 'visitor'):
|
||||
team = game[key]['abbreviation'].upper()
|
||||
for key in ('homeTeam', 'awayTeam'):
|
||||
team = game[key]['teamTricode']
|
||||
if team in self.favorite_teams:
|
||||
team_game_map.setdefault(team, []).append(id_)
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
data[id_] = game
|
||||
# Merge in the team stats, because they are not returned in the
|
||||
# initial API request.
|
||||
for key in ('home', 'visitor'):
|
||||
team = data[id_][key]['abbreviation'].upper()
|
||||
data[id_][key].update(team_stats.get(team, {}))
|
||||
|
||||
self.interpret_api_return(data, team_game_map)
|
||||
|
||||
@ -243,11 +206,10 @@ class NBA(ScoresBackend):
|
||||
callback=callback,
|
||||
default=default)
|
||||
|
||||
self.logger.debug('Processing %s game data: %s',
|
||||
self.__class__.__name__, game)
|
||||
self.logger.debug(f'Processing {self.name} game data: {game}')
|
||||
|
||||
_update('id', 'game_url')
|
||||
ret['live_url'] = self.live_url % ret['id']
|
||||
_update('id', 'gameId')
|
||||
ret['live_url'] = self.live_url.format(id=ret['id'])
|
||||
|
||||
status_map = {
|
||||
'1': 'pregame',
|
||||
@ -258,8 +220,9 @@ class NBA(ScoresBackend):
|
||||
status_code = period_data.get('game_status', '1')
|
||||
status = status_map.get(status_code)
|
||||
if status is None:
|
||||
self.logger.debug('Unknown %s game status code \'%s\'',
|
||||
self.__class__.__name__, status_code)
|
||||
self.logger.debug(
|
||||
f"Unknown {self.name} game status code '{status_code}'"
|
||||
)
|
||||
status_code = '1'
|
||||
ret['status'] = status_map[status_code]
|
||||
|
||||
@ -269,64 +232,59 @@ class NBA(ScoresBackend):
|
||||
period_diff = period_number - total_periods
|
||||
ret['quarter'] = 'OT' \
|
||||
if period_diff == 1 \
|
||||
else '%dOT' % period_diff if period_diff > 1 \
|
||||
else f'{period_diff}OT' if period_diff > 1 \
|
||||
else self.add_ordinal(period_number)
|
||||
else:
|
||||
ret['quarter'] = ''
|
||||
|
||||
ret['time_remaining'] = period_data.get('game_clock')
|
||||
ret['time_remaining'] = game.get('game_clock')
|
||||
if ret['time_remaining'] == '':
|
||||
ret['time_remaining'] = 'End'
|
||||
elif ret['time_remaining'] is None:
|
||||
ret['time_remaining'] = ''
|
||||
ret['overtime'] = ret['quarter'] if 'OT' in ret['quarter'] else ''
|
||||
|
||||
_update('venue', 'arena')
|
||||
|
||||
for ret_key, game_key in (('home', 'home'), ('away', 'visitor')):
|
||||
_update('%s_score' % ret_key, '%s:score' % game_key,
|
||||
for key in ('home', 'away'):
|
||||
team_key = f'{key}Team'
|
||||
_update(f'{key}_score', f'{team_key}:score',
|
||||
callback=self.force_int, default=0)
|
||||
_update('%s_city' % ret_key, '%s:city' % game_key)
|
||||
_update('%s_name' % ret_key, '%s:nickname' % game_key)
|
||||
_update('%s_abbrev' % ret_key, '%s:abbreviation' % game_key)
|
||||
_update(f'{key}_city', f'{team_key}:teamCity')
|
||||
_update(f'{key}_name', f'{team_key}:teamName')
|
||||
_update(f'{key}_abbrev', f'{team_key}:teamTricode')
|
||||
if 'playoffs' in game:
|
||||
_update('%s_wins' % ret_key, 'playoffs:%s_wins' % game_key,
|
||||
_update(f'{key}_wins', f'playoffs:{key}_wins',
|
||||
callback=self.force_int, default=0)
|
||||
_update('%s_seed' % ret_key, 'playoffs:%s_seed' % game_key,
|
||||
_update(f'{key}_seed', f'playoffs:{key}_seed',
|
||||
callback=self.force_int, default=0)
|
||||
else:
|
||||
_update('%s_wins' % ret_key, '%s:wins' % game_key,
|
||||
_update(f'{key}_wins', f'{team_key}:wins',
|
||||
callback=self.force_int, default=0)
|
||||
_update('%s_losses' % ret_key, '%s:losses' % game_key,
|
||||
_update(f'{key}_losses', f'{team_key}:losses',
|
||||
callback=self.force_int, default=0)
|
||||
ret['%s_seed' % ret_key] = ''
|
||||
ret[f'{key}_seed'] = ''
|
||||
|
||||
if 'playoffs' in game:
|
||||
ret['home_losses'] = ret['away_wins']
|
||||
ret['away_losses'] = ret['home_wins']
|
||||
|
||||
# From API data, date is YYYYMMDD, time is HHMM
|
||||
game_time_str = '%s%s' % (game.get('date', ''), game.get('time', ''))
|
||||
try:
|
||||
game_time = datetime.strptime(game_time_str, '%Y%m%d%H%M')
|
||||
game_et = game.get('gameEt', '')
|
||||
game_time = datetime.strptime(game_et, '%Y-%m-%dT%H:%M:%S%z')
|
||||
except ValueError as exc:
|
||||
# Log when the date retrieved from the API return doesn't match the
|
||||
# expected format (to help troubleshoot API changes), and set an
|
||||
# actual datetime so format strings work as expected. The times
|
||||
# will all be wrong, but the logging here will help us make the
|
||||
# necessary changes to adapt to any API changes.
|
||||
self.logger.error(
|
||||
'Error encountered determining game time for %s game %s:',
|
||||
self.__class__.__name__,
|
||||
game['id'],
|
||||
exc_info=True
|
||||
self.logger.exception(
|
||||
f'Error encountered determining game time for {self.name} '
|
||||
f'game {game["id"]} (time string: {game_et})'
|
||||
)
|
||||
game_time = datetime.datetime(1970, 1, 1)
|
||||
|
||||
eastern = pytz.timezone('US/Eastern')
|
||||
ret['start_time'] = eastern.localize(game_time).astimezone()
|
||||
ret['start_time'] = game_time.astimezone()
|
||||
|
||||
self.logger.debug('Returned %s formatter data: %s',
|
||||
self.__class__.__name__, ret)
|
||||
self.logger.debug(f'Returned {self.name} formatter data: {ret}')
|
||||
|
||||
return ret
|
||||
|
@ -9,9 +9,9 @@ import time
|
||||
from datetime import datetime
|
||||
from urllib.request import urlopen
|
||||
|
||||
LIVE_URL = 'https://www.nhl.com/gamecenter/%s'
|
||||
LIVE_URL = 'https://www.nhl.com/gamecenter/{id}'
|
||||
SCOREBOARD_URL = 'https://www.nhl.com/scores'
|
||||
API_URL = 'https://statsapi.web.nhl.com/api/v1/schedule?startDate=%04d-%02d-%02d&endDate=%04d-%02d-%02d&expand=schedule.teams,schedule.linescore,schedule.broadcasts.all&site=en_nhl&teamId='
|
||||
API_URL = 'https://statsapi.web.nhl.com/api/v1/schedule?startDate={date:%Y-%m-%d}&endDate={date:%Y-%m-%d}&expand=schedule.teams,schedule.linescore,schedule.broadcasts.all&site=en_nhl&teamId='
|
||||
|
||||
|
||||
class NHL(ScoresBackend):
|
||||
@ -213,8 +213,7 @@ class NHL(ScoresBackend):
|
||||
@require(internet)
|
||||
def check_scores(self):
|
||||
self.get_api_date()
|
||||
url = self.api_url % (self.date.year, self.date.month, self.date.day,
|
||||
self.date.year, self.date.month, self.date.day)
|
||||
url = self.api_url.format(date=self.date)
|
||||
|
||||
game_list = self.get_nested(self.api_request(url),
|
||||
'dates:0:games',
|
||||
@ -244,13 +243,12 @@ class NHL(ScoresBackend):
|
||||
def process_game(self, game):
|
||||
ret = {}
|
||||
|
||||
self.logger.debug('Processing %s game data: %s',
|
||||
self.__class__.__name__, game)
|
||||
self.logger.debug(f'Processing {self.name} game data: {game}')
|
||||
|
||||
linescore = self.get_nested(game, 'linescore', default={})
|
||||
|
||||
ret['id'] = game['gamePk']
|
||||
ret['live_url'] = self.live_url % ret['id']
|
||||
ret['live_url'] = self.live_url.format(id=ret['id'])
|
||||
ret['period'] = self.get_nested(
|
||||
linescore,
|
||||
'currentPeriodOrdinal')
|
||||
@ -359,17 +357,14 @@ class NHL(ScoresBackend):
|
||||
# actual datetime so format strings work as expected. The times
|
||||
# will all be wrong, but the logging here will help us make the
|
||||
# necessary changes to adapt to any API changes.
|
||||
self.logger.error(
|
||||
'Error encountered determining %s game time for game %s:',
|
||||
self.__class__.__name__,
|
||||
game['id'],
|
||||
exc_info=True
|
||||
self.logger.exception(
|
||||
f'Error encountered determining {self.name} game time for '
|
||||
f'game {game["id"]}'
|
||||
)
|
||||
game_time = datetime.datetime(1970, 1, 1)
|
||||
|
||||
ret['start_time'] = pytz.utc.localize(game_time).astimezone()
|
||||
|
||||
self.logger.debug('Returned %s formatter data: %s',
|
||||
self.__class__.__name__, ret)
|
||||
self.logger.debug(f'Returned {self.name} formatter data: {ret}')
|
||||
|
||||
return ret
|
||||
|
Loading…
Reference in New Issue
Block a user