Skip to content

Commit

Permalink
fix(nhlgt): update to use new NHL apis
Browse files Browse the repository at this point in the history
ver(nhlgt): 2.0.0
ver(pynhlapi): 0.1.0
ver(platform): 2023.10.1
  • Loading branch information
toddrob99 committed Oct 5, 2023
1 parent 7b964fa commit af780db
Show file tree
Hide file tree
Showing 21 changed files with 744 additions and 727 deletions.
473 changes: 197 additions & 276 deletions bots/nhl_game_threads/__init__.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bots/nhl_game_threads/pynhlapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
from .constants import APP_NAME
from .api import API

__version__ = "0.0.2"
__version__ = "0.1.0"

logger = logging.Logger(APP_NAME)
158 changes: 92 additions & 66 deletions bots/nhl_game_threads/pynhlapi/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,69 +8,75 @@


class API:
def __init__(self, api_url=constants.API_URL):
self.api_url = api_url
logger.debug(f"Set API URL to {self.api_url}")
def __init__(self):
pass

def game(self, game_pk, json=True, **kwargs):
url = f"{self.api_url}{constants.GAME_ENDPOINT.format(game_pk=game_pk)}"
def game(self, game_pk, **kwargs):
url = f"{constants.GAME_ENDPOINT.format(game_pk=game_pk)}"
url = self.add_kwargs_to_url(url, kwargs)
json = self.get_json(url)
if json:
return json

def game_content(self, game_pk, json=True, **kwargs):
url = f"{self.api_url}{constants.GAME_CONTENT_ENDPOINT.format(game_pk=game_pk)}"
def game_boxscore(self, game_pk, **kwargs):
url = f"{constants.GAME_BOXSCORE_ENDPOINT.format(game_pk=game_pk)}"
url = self.add_kwargs_to_url(url, kwargs)
json = self.get_json(url)
if json:
return json

def schedule(
def game_playbyplay(self, game_pk, **kwargs):
url = f"{constants.GAME_PLAYBYPLAY_ENDPOINT.format(game_pk=game_pk)}"
url = self.add_kwargs_to_url(url, kwargs)
json = self.get_json(url)
if json:
return json

def scoreboard(
self,
start_date=datetime.today().strftime("%Y-%m-%d"),
end_date=None,
team_id=None,
json=True,
**kwargs,
):
url = f"{self.api_url}{constants.SCHEDULE_ENDPOINT}"
url = f"{constants.SCOREBOARD_ENDPOINT.format(ymd=start_date)}"
if not self.check_date_format(start_date):
raise ValueError(
"Parameter start_date contains invalid value (format should be %Y-%m-%d e.g. '2021-10-05')."
"Parameter start_date contains invalid value (format should be %Y-%m-%d e.g. '2021-10-05', or 'now')."
)
if end_date and not self.check_date_format(end_date):
raise ValueError(
"Parameter end_date contains invalid value (format should be %Y-%m-%d e.g. '2021-10-05')."
"Parameter end_date contains invalid value (format should be %Y-%m-%d e.g. '2021-10-05', or 'now')."
)
url += f"?startDate={start_date}&endDate={end_date if end_date else start_date}"
url += f"&teamId={team_id}" if team_id else ""
url = self.add_kwargs_to_url(url, kwargs)
json = self.get_json(url)
if json:
return json

def season_by_date(
self, date_str, pre_season_allowance_days=30, json=True, **kwargs
):
games = json.get("games", [])
if games and end_date:
games = [x for x in games if x["gameDate"] <= end_date]
if games and team_id:
games = [
x
for x in games
if team_id in [x["awayTeam"]["id"], x["homeTeam"]["id"]]
]
return games

def season_by_date(self, date_str, **kwargs):
if not self.check_date_format(date_str):
raise ValueError(
"Parameter date_str contains invalid value (format should be %Y-%m-%d e.g. '2021-10-05')."
)
all_seasons = self.seasons(ids=[], **kwargs)
all_seasons = self.seasons(**kwargs)
date_obj = datetime.strptime(date_str, "%Y-%m-%d")
given_year = date_obj.strftime("%Y")
relevant_seasons = [x for x in all_seasons if given_year in str(x["seasonId"])]
relevant_seasons = [x for x in all_seasons if given_year in str(x["id"])]
in_season = next(
(
x
for x in relevant_seasons
if date_obj
>= (
datetime.strptime(x["regularSeasonStartDate"], "%Y-%m-%d")
- timedelta(days=pre_season_allowance_days)
)
and date_obj <= datetime.strptime(x["seasonEndDate"], "%Y-%m-%d")
if date_obj >= (datetime.fromisoformat(x["preseasonStartdate"]))
and date_obj <= datetime.fromisoformat(x["endDate"])
),
None,
)
Expand All @@ -79,68 +85,86 @@ def season_by_date(
return (
relevant_seasons[0]
if (
date_obj
- datetime.strptime(relevant_seasons[0]["seasonEndDate"], "%Y-%m-%d")
< (
datetime.strptime(
relevant_seasons[1]["regularSeasonStartDate"], "%Y-%m-%d"
)
- timedelta(days=pre_season_allowance_days)
)
date_obj - datetime.fromisoformat(relevant_seasons[0]["endDate"])
< (datetime.fromisoformat(relevant_seasons[1]["preseasonStartDate"]))
- date_obj
)
else relevant_seasons[1]
)

def season_by_id(self, season_id, json=True, **kwargs):
url = f"{self.api_url}{constants.SEASON_ENDPOINT.format(season_id=season_id)}"
url = self.add_kwargs_to_url(url, kwargs)
json = self.get_json(url)
if json:
return json.get("seasons", [])[0]
def season_by_id(self, season_id, **kwargs):
all_seasons = self.seasons(**kwargs)
return next(
(x for x in all_seasons if x["id"] == season_id),
None,
)

def seasons(self, ids=[], json=True, **kwargs):
url = f"{self.api_url}{constants.SEASONS_ENDPOINT}"
def seasons(self, ids=[], **kwargs):
url = f"{constants.SEASONS_ENDPOINT}"
url = self.add_kwargs_to_url(url, kwargs)
json = self.get_json(url)
if ids == []:
return json["seasons"]
return json["data"]
if isinstance(ids, int) or isinstance(ids, str):
ids = [str(ids)]
ids = [str(i) for i in ids]
seasons = [s for s in json["seasons"] if s["seasonId"] in ids]
seasons = [s for s in json["data"] if s["id"] in ids]
if json:
return seasons

def standings(self, season=None, **kwargs):
url = f"{self.api_url}{constants.STANDINGS_ENDPOINT}"
if season:
url += f"?season={season}"
def standings(self, ymd="now", **kwargs):
if not self.check_date_format(ymd):
raise ValueError(
"Parameter ymd contains invalid value (format should be %Y-%m-%d e.g. '2021-10-05', or 'now')."
)
url = f"{constants.STANDINGS_ENDPOINT.format(ymd=ymd)}"
url = self.add_kwargs_to_url(url, kwargs)
json = self.get_json(url)
if json:
return json.get("records", [])
return json.get("standings", [])

def team_by_id(self, team_id, json=True, **kwargs):
url = f"{self.api_url}{constants.TEAM_ENDPOINT.format(team_id=team_id)}"
url = self.add_kwargs_to_url(url, kwargs)
json = self.get_json(url)
if json:
return json.get("teams", [])[0]

def teams(self, ids=[], json=True, **kwargs):
if isinstance(ids, list):
ids = ",".join([str(id) for id in ids])
elif isinstance(ids, int):
ids = str(ids)
url = f"{self.api_url}{constants.TEAMS_ENDPOINT}"
if len(ids):
url += f"?teamId={ids}"
def team_by_id(self, team_id):
return next((x for x in self.teams() if x["id"] == team_id), None)

def teams(self, ymd="now", ids=[], **kwargs):
if not self.check_date_format(ymd):
raise ValueError(
"Parameter ymd contains invalid value (format should be %Y-%m-%d e.g. '2021-10-05', or 'now')."
)
url = f"{constants.TEAMS_ENDPOINT.format(ymd=ymd)}"
url = self.add_kwargs_to_url(url, kwargs)
json = self.get_json(url)
if json:
if not json:
return []
if ids:
return [x for x in json["teams"] if x["id"] in ids]
else:
return json["teams"]

def teams_with_conf_div(self, ymd="now", ids=[]):
all_teams = self.teams(ymd=ymd, ids=ids)
standings = self.standings()
if not standings:
logger.error(
"No data returned for standings/now. Teams won't have valid division or conference data included!"
)
standings = []
for t in all_teams:
st = next(
(x for x in standings if x["teamAbbrev"]["default"] == t["abbrev"]),
{},
)
t.update(
{
"conferenceAbbrev": st.get("conferenceAbbrev", "U"),
"conferenceName": st.get("conferenceName", "Unknown"),
"divisionAbbrev": st.get("divisionAbbrev", "U"),
"divisionName": st.get("divisionName", "Unknown"),
}
)

return all_teams

@staticmethod
def add_kwargs_to_url(url, kwargs=None):
if not kwargs or not len(kwargs):
Expand All @@ -152,6 +176,8 @@ def add_kwargs_to_url(url, kwargs=None):

@staticmethod
def check_date_format(d):
if d == "now":
return True
try:
datetime.strptime(d, "%Y-%m-%d")
except ValueError:
Expand Down
18 changes: 9 additions & 9 deletions bots/nhl_game_threads/pynhlapi/constants.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
APP_NAME = "pynhlapi"
API_URL = "https://statsapi.web.nhl.com"
SCHEDULE_ENDPOINT = "/api/v1/schedule"
TEAM_ENDPOINT = "/api/v1/teams/{team_id}"
TEAMS_ENDPOINT = "/api/v1/teams"
GAME_ENDPOINT = "/api/v1/game/{game_pk}/feed/live"
SEASON_ENDPOINT = "/api/v1/seasons/{season_id}"
SEASONS_ENDPOINT = "/api/v1/seasons"
STANDINGS_ENDPOINT = "/api/v1/standings"
GAME_CONTENT_ENDPOINT = "/api/v1/game/{game_pk}/content"
API_URL = "https://api-web.nhle.com"
TEAMS_ENDPOINT = API_URL + "/v1/schedule-calendar/{ymd}"
STANDINGS_ENDPOINT = API_URL + "/v1/standings/{ymd}"
SCOREBOARD_ENDPOINT = API_URL + "/v1/score/{ymd}"
SCHEDULE_ENDPOINT = API_URL + "/v1/schedule/{ymd}"
GAME_ENDPOINT = API_URL + "/v1/gamecenter/{game_pk}/landing"
SEASONS_ENDPOINT = "https://api.nhle.com/stats/rest/en/season?sort=%5B%7B%22property%22:%22id%22,%22direction%22:%22DESC%22%7D%5D"
GAME_BOXSCORE_ENDPOINT = API_URL + "/v1/gamecenter/{game_pk}/boxscore"
GAME_PLAYBYPLAY_ENDPOINT = API_URL + "/v1/gamecenter/{game_pk}/play-by-play"
37 changes: 20 additions & 17 deletions bots/nhl_game_threads/templates/decisions.mako
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@
awayTeam = data["myTeam"] if data["homeAway"] == "away" else data["oppTeam"]
homeTeam = data["myTeam"] if data["homeAway"] == "home" else data["oppTeam"]
def playerLink(p):
return f"[{p['fullName']}](https://www.nhl.com/player/{p['id']})"
return f"[{p['name']}](https://www.nhl.com/player/{p['playerId']})"
threeStars = data["game"].get("summary", {}).get("threeStars")
labels = ["First", "Second", "Third"]
%>\
${'##'} Decisions
%if data["game"]["liveData"]["decisions"].get("winner", {}).get("fullName"):
* Winner: ${playerLink(data["game"]["liveData"]["decisions"]["winner"])}
%endif
%if data["game"]["liveData"]["decisions"].get("loser", {}).get("fullName"):
* Loser: ${playerLink(data["game"]["liveData"]["decisions"]["loser"])}
%endif
%if data["game"]["liveData"]["decisions"].get("firstStar", {}).get("fullName"):
* First Star: ${playerLink(data["game"]["liveData"]["decisions"]["firstStar"])}
%endif
%if data["game"]["liveData"]["decisions"].get("secondStar", {}).get("fullName"):
* Second Star: ${playerLink(data["game"]["liveData"]["decisions"]["secondStar"])}
%endif
%if data["game"]["liveData"]["decisions"].get("thirdStar", {}).get("fullName"):
* Third Star: ${playerLink(data["game"]["liveData"]["decisions"]["thirdStar"])}
%endif
% if threeStars:
${'##'} Stars of the Game
##% if data["game"]["liveData"]["decisions"].get("winner", {}).get("fullName"):
##* Winner: ${playerLink(data["game"]["liveData"]["decisions"]["winner"])}
##% endif
##% if data["game"]["liveData"]["decisions"].get("loser", {}).get("fullName"):
##* Loser: ${playerLink(data["game"]["liveData"]["decisions"]["loser"])}
##% endif
% for p in threeStars:
* ${labels[p["star"] - 1]} Star: ${playerLink(p)} (${p["teamAbbrev"]})\
% if p.get("goals") is not None and p.get("assists") is not None:
Goals: ${p["goals"]}, Assists: ${p["assists"]}
% else:

% endif
% endfor
% endif
47 changes: 17 additions & 30 deletions bots/nhl_game_threads/templates/division_scoreboard.mako
Original file line number Diff line number Diff line change
@@ -1,43 +1,30 @@
<%
from datetime import datetime
import pytz
divGames = [
x
for x in data["todayOtherGames"]
if (
x["teams"]["away"]["team"]["id"] in data["otherDivisionTeamIds"]
or x["teams"]["home"]["team"]["id"] in data["otherDivisionTeamIds"]
) and data["myTeam"]["id"] not in [
x["teams"]["away"]["team"]["id"],
x["teams"]["home"]["team"]["id"],
]
]
def subLink(t):
return f"[{t['teamName']}]({data['teamSubs'].get(t['abbreviation'], '')})"
return f"[{t['name']}]({data['teamSubs'].get(t['abbrev'], '')})"
ordDict = {1:{1:'1st',2:'2nd',3:'3rd',4:'OT',5:'SO'},2:{1:'1st',2:'2nd',3:'3rd',4:'OT',5:'SO'},3:{1:'1st',2:'2nd',3:'3rd',4:'OT1',5:'OT2',6:'OT3',7:'OT4',8:'OT5'}}
def format_period(game):
return ordDict[game["gameType"]].get(game.get("period"), "")
%>\
% if len(divGames):
${'##'} ${data["myTeam"]["division"]["nameShort"]} Division Scoreboard
% for game in divGames:
% if len(data["todayOtherDivisionGames"]):
${'##'} ${data["myTeam"]["divisionName"]} Division Scoreboard
% for game in data["todayOtherDivisionGames"]:
<%
dt = datetime.strptime(game["gameDate"], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=pytz.utc)
dt = datetime.strptime(game["startTimeUTC"], "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=pytz.utc)
toTz = pytz.timezone(settings.get("Bot", {}).get("TEAM_TIMEZONE", "America/New_York"))
formattedGameTime = dt.astimezone(toTz).strftime("%I:%M %p")
#formattedPeriod = "" if game["gameState"] == "FUT" else game["period"] if game.get("period") and game["period"] <= 3 else (game.get("periodDescriptor", {}).get("otPeriods", "") + "OT") if game.get("periodDescriptor", {}).get("periodType") == "OT" else "SO" if game.get("periodDescriptor", {}).get("periodType") == "SO" else game["period"]
%>\
% if game["status"]["statusCode"] == "9":
${next((subLink(t) for t in data["allTeams"] if t["id"] == game["teams"]["away"]["team"]["id"]), "Unknown Team")} @ \
${next((subLink(t) for t in data["allTeams"] if t["id"] == game["teams"]["home"]["team"]["id"]), "Unknown Team")} - PPD
% elif game["status"]["abstractGameState"] == "Final":
${next((subLink(t) for t in data["allTeams"] if t["id"] == game["teams"]["away"]["team"]["id"]), "Unknown Team")} (${game["teams"]["away"]["score"]}) @ \
(${game["teams"]["home"]["score"]}) ${next((subLink(t) for t in data["allTeams"] if t["id"] == game["teams"]["home"]["team"]["id"]), "Unknown Team")} - Final
% elif game["status"]["abstractGameState"] == "Live":
${next((subLink(t) for t in data["allTeams"] if t["id"] == game["teams"]["away"]["team"]["id"]), "Unknown Team")} (${game["teams"]["away"]["score"]}) @ \
(${game["teams"]["home"]["score"]}) ${next((subLink(t) for t in data["allTeams"] if t["id"] == game["teams"]["home"]["team"]["id"]), "Unknown Team")} \
- ${game["linescore"]["currentPeriodOrdinal"]} ${game["linescore"]["currentPeriodTimeRemaining"]}
% if data["game"].get("gameScheduleState") in ["PPD", "SUSP", "CNCL"]:
${subLink(game["awayTeam"])} @ ${subLink(game["homeTeam"])} - ${data["game"].get("gameScheduleState")}
% elif game["gameState"] in ["FINAL", "OFF", "OVER"]:
${subLink(game["awayTeam"])} @ ${subLink(game["homeTeam"])} - Final
% elif game["gameState"] in ["LIVE", "CRIT"]:
${subLink(game["awayTeam"])} @ ${subLink(game["homeTeam"])} - ${format_period(game)} ${game.get("clock", {}).get("timeRemaining", "")}
% else:
${next((subLink(t) for t in data["allTeams"] if t["id"] == game["teams"]["away"]["team"]["id"]), "Unknown Team")} @ \
${next((subLink(t) for t in data["allTeams"] if t["id"] == game["teams"]["home"]["team"]["id"]), "Unknown Team")} \
- ${formattedGameTime}
${subLink(game["awayTeam"])} @ ${subLink(game["homeTeam"])} - ${formattedGameTime}
% endif

% endfor
% endif # if len(divGames)
% endif
Loading

0 comments on commit af780db

Please sign in to comment.