Skip to content

Commit

Permalink
Merge pull request #5 from coreyjs/enhance/4
Browse files Browse the repository at this point in the history
Enhance/4
  • Loading branch information
coreyjs committed Jun 16, 2023
2 parents 70dcd3d + 950b487 commit ce748a3
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 37 deletions.
12 changes: 8 additions & 4 deletions nhlpy/api/games.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Union, List
from nhlpy.api import BaseNHLAPIClient


class Games(BaseNHLAPIClient):
"""
This class is used to access the NHL API for game data.
Expand Down Expand Up @@ -58,17 +59,20 @@ def get_game_live_feed(self, game_id: Union[str, int]) -> dict:
"""
return self._get(resource=f"game/{game_id}/feed/live").json()

def get_game_live_feed_diff_after_timestamp(self, game_id: Union[str, int], timestamp: str) -> dict:
def get_game_live_feed_diff_after_timestamp(
self, game_id: Union[str, int], timestamp: str
) -> dict:
"""
Returns the difference in the live feed game data, from since the given timestamp: param.
:param game_id:
:param timestamp:
:return:
"""
warnings.warn("This endpoint is still experimental and may not work as expected")
warnings.warn(
"This endpoint is still experimental and may not work as expected"
)
return self._get(resource=f"game/{game_id}/feed/live/diffPath").json()


def get_game_boxscore(self, game_id: Union[str, int]) -> List[dict]:
"""
Less detail than get_game_live_feed() but still a large response, contains lots of information about
Expand Down Expand Up @@ -101,4 +105,4 @@ def get_game_content(self, game_id: Union[str, int]) -> dict:
:param game_id:
:return:
"""
return self._get(resource=f"game/{game_id}/content").json()
return self._get(resource=f"game/{game_id}/content").json()
102 changes: 101 additions & 1 deletion nhlpy/api/helpers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,54 @@
from typing import List
import warnings
from typing import List, Optional

from nhlpy.api.standings import Standings
from nhlpy.api.schedule import Schedule
from nhlpy.api.games import Games


def _parse_team_specific_game_data(
game_item: dict, team_side: str, game_boxscore_data: dict
) -> None:
"""
Parser helper method
:param team_side:
:param game_boxscore_data:
:return:
"""
game_item[f"{team_side}_pim"] = game_boxscore_data[team_side]["teamStats"][
"teamSkaterStats"
]["pim"]
game_item[f"{team_side}_shots"] = game_boxscore_data[team_side]["teamStats"][
"teamSkaterStats"
]["shots"]
game_item[f"{team_side}_pp_percent"] = float(
game_boxscore_data[team_side]["teamStats"]["teamSkaterStats"][
"powerPlayPercentage"
]
)
game_item[f"{team_side}_pp_goals"] = game_boxscore_data[team_side]["teamStats"][
"teamSkaterStats"
]["powerPlayGoals"]
game_item[f"{team_side}_pp_opps"] = game_boxscore_data[team_side]["teamStats"][
"teamSkaterStats"
]["powerPlayOpportunities"]
game_item[f"{team_side}_fo_win_percent"] = float(
game_boxscore_data[team_side]["teamStats"]["teamSkaterStats"][
"faceOffWinPercentage"
]
)
game_item[f"{team_side}_shots_blocked"] = game_boxscore_data[team_side][
"teamStats"
]["teamSkaterStats"]["blocked"]
game_item[f"{team_side}_shots_takeaways"] = game_boxscore_data[team_side][
"teamStats"
]["teamSkaterStats"]["takeaways"]
game_item[f"{team_side}_shots_giveaways"] = game_boxscore_data[team_side][
"teamStats"
]["teamSkaterStats"]["giveaways"]
game_item[f"{team_side}_shots_hits"] = game_boxscore_data[team_side]["teamStats"][
"teamSkaterStats"
]["hits"]


class Helpers:
Expand Down Expand Up @@ -61,3 +109,55 @@ def league_standings(self, season: str, py_exp_ex: float = 2.37) -> List[dict]:
team["expected_wins"] = team["py_expectation"] * team["games_played"]
teams.append(team)
return teams

def get_all_game_results(
self,
season: str,
detailed_game_data: bool = False,
game_type: str = "R",
team_ids: Optional[List[int]] = None,
) -> List[dict]:
"""
:param season:
:param detailed_game_data: If True, will return the full game data for each game. If False, will only return simple game data.
:param game_type:
:param team_ids:
:return:
"""
warnings.warn(
"This endpoint will query the schedule API to get the games, and then sequentially query the boxscore API"
" for each game. This is a slow endpoint, do not call this while in a loop, or multiple times in succession"
)
games = []
game_dates = Schedule().get_schedule(
season=season, game_type=game_type, team_ids=team_ids
)["dates"]
for d in game_dates:
date = d["date"]
for game in d["games"]:
game_data = {
"date": date,
"home_score": game["teams"]["home"]["score"],
"away_score": game["teams"]["away"]["score"],
"game_id": game["gamePk"],
"game_type": game["gameType"],
"away_id": game["teams"]["away"]["team"]["id"],
"away_name": game["teams"]["away"]["team"]["name"],
"home_id": game["teams"]["home"]["team"]["id"],
"home_name": game["teams"]["home"]["team"]["name"],
}
games.append(game_data)

if detailed_game_data:
game_client = Games()
for game in games:
data = game_client.get_game_boxscore(game_id=game["game_id"])["teams"]
_parse_team_specific_game_data(
game_item=game, team_side="away", game_boxscore_data=data
)
_parse_team_specific_game_data(
game_item=game, team_side="home", game_boxscore_data=data
)

return games
4 changes: 2 additions & 2 deletions nhlpy/api/players.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def get_player(self, person_id: str) -> dict:
:param person_id:
:return:
"""
return self._get(resource=f"people/{person_id}").json()
return self._get(resource=f"people/{person_id}").json()["people"]

def get_player_stats(
self, person_id: str, season: str = None, stat_type: str = "statsSingleSeason"
Expand All @@ -32,7 +32,7 @@ def get_player_stats(
query = f"stats={stat_type}" if stat_type else ""
return self._get(
resource=f"people/{person_id}/stats?season={season}&{query}"
).json()
).json()["stats"]

def get_player_stat_types(self) -> dict:
"""
Expand Down
28 changes: 20 additions & 8 deletions nhlpy/api/schedule.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
from typing import Optional
from typing import Optional, List
from nhlpy.api import BaseNHLAPIClient


class Schedule(BaseNHLAPIClient):
def get_schedule(self, season: Optional[str] = None) -> dict:
def get_schedule(
self, season: str, game_type: str = "R", team_ids: Optional[List[int]] = None
) -> dict:
"""
Returns a list of all games for the current season if no season is supplied. Otherwise returns the
schedule for the season defined in the season: param.
:param season: Season in format of 20202021
:return:
:param season: str - Season in format of 20202021
:param game_type: str - Game type, R (default) for regular season, P for playoffs, PR for preseason, A for all-star
:param team_ids: List[int] - List of team ids
example: c.schedule.get_schedule(season="20222023", team_ids=[7], game_type='PR')
:return: dict
"""
query = f"?season={season}" if season else ""
return self._get(resource=f"schedule{query}").json()
q: str = f"?season={season}"
team_q: str = (
f"&teamId={','.join(str(t) for t in team_ids)}" if team_ids else ""
)
type_q: str = f"&gameType={game_type}"

return self._get(resource=f"schedule{q}{type_q}{team_q}").json()
29 changes: 17 additions & 12 deletions nhlpy/api/standings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@


class Standings(BaseNHLAPIClient):
def get_standing_types(self) -> dict:
"""
Returns a list of standing types that can be used in get_standings_by_standing_type()
:return: dict of standing types
"""
return self._get(resource="standingsTypes").json()

def get_standings(self, season: str = None, detailed_record: bool = False) -> dict:
"""
Gets the standings for the season supplied via season: param.
Expand All @@ -11,16 +18,11 @@ def get_standings(self, season: str = None, detailed_record: bool = False) -> di
head-to-head records against divisions and conferences.
:return: dict
"""
modifier = f"season={season}&" if season else ""
detailed = "expand=standings.record&" if detailed_record else ""
return self._get(resource=f"standings?{modifier}{detailed}").json()
modifier: str = f"season={season}&" if season else ""
detailed: str = "expand=standings.record&" if detailed_record else ""

def get_standing_types(self) -> dict:
"""
Returns a list of standing types that can be used in get_standings_by_standing_type()
:return: dict of standing types
"""
return self._get(resource="standingsTypes").json()
response: dict = self._get(resource=f"standings?{modifier}{detailed}").json()
return response["records"]

def get_standings_by_standing_type(
self, season: str, standing_type: str, detailed_records: bool = False
Expand All @@ -34,6 +36,9 @@ def get_standings_by_standing_type(
postseason, byDivision, byConference, byLeague
:return: dict
"""
query = f"season={season}&"
detailed = "expand=standings.record&" if detailed_records else ""
return self._get(resource=f"standings/{standing_type}?{query}{detailed}").json()
query: str = f"season={season}&"
detailed: str = "expand=standings.record&" if detailed_records else ""
response: dict = self._get(
resource=f"standings/{standing_type}?{query}{detailed}"
).json()
return response["records"]
21 changes: 13 additions & 8 deletions nhlpy/api/teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ def all(self) -> dict:
Returns a list of all teams.
:return: dict
"""
return self._get(resource="/teams").json()
response: dict = self._get(resource="/teams").json()
return response["teams"]

def get_by_id(
self,
Expand All @@ -20,47 +21,51 @@ def get_by_id(
:param roster: bool, Should include the roster for the team
:return: dict
"""
query = ""
query: str = ""
if roster:
query += "?expand=team.roster"
return self._get(resource=f"teams/{id}{query}").json()
return self._get(resource=f"teams/{id}{query}").json()["teams"]

def get_team_next_game(self, id: int) -> dict:
"""
Returns the next game for the team with the id supplied.
:param id: int, NHL team id
:return: dict
"""
return self._get(resource=f"teams/{id}?expand=team.schedule.next").json()
return self._get(resource=f"teams/{id}?expand=team.schedule.next").json()[
"teams"
]

def get_team_previous_game(self, id: int) -> dict:
"""
Returns the previous game for the team with the id supplied.
:param id: int, NHL team id
:return: dict
"""
return self._get(resource=f"teams/{id}?expand=team.schedule.previous").json()
return self._get(resource=f"teams/{id}?expand=team.schedule.previous").json()[
"teams"
]

def get_team_with_stats(self, id: int) -> dict:
"""
Returns the team with stats for the team with the id supplied.
:param id: int, NHL team id
:return: dict
"""
return self._get(resource=f"teams/{id}?expand=team.stats").json()
return self._get(resource=f"teams/{id}?expand=team.stats").json()["teams"]

def get_team_roster(self, id: int) -> dict:
"""
Returns the roster for the team with the id supplied.
:param id: int, NHL team id
:return: dict
"""
return self._get(resource=f"teams/{id}/roster").json()
return self._get(resource=f"teams/{id}/roster").json()["roster"]

def get_team_stats(self, id: int) -> dict:
"""
Returns the stats for the team with the id supplied.
:param id:
:return: dict
"""
return self._get(resource=f"teams/{id}/stats").json()
return self._get(resource=f"teams/{id}/stats").json()["stats"]
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "nhl-api-py"
version = "0.3.0"
description = "NHL API Wrapper. For standings, team stats, outcomes and player information."
version = "0.4.1"
description = "NHL API. For standings, team stats, outcomes, player information. Contains each individual API endpoint as well as convience methods for easy data loading in Pandas or any ML applications."
authors = ["Corey Schaf <[email protected]>"]
readme = "README.md"
packages = [{include = "nhlpy"}]
Expand Down

0 comments on commit ce748a3

Please sign in to comment.