nhl-api-py 2.1.2__tar.gz → 2.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/PKG-INFO +52 -2
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/README.md +49 -0
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/_version.py +1 -1
- nhl_api_py-2.2.5/nhlpy/api/misc.py +45 -0
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/api/schedule.py +5 -3
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/api/standings.py +2 -2
- nhl_api_py-2.2.5/nhlpy/api/stats.py +202 -0
- nhl_api_py-2.2.5/nhlpy/api/teams.py +56 -0
- nhl_api_py-2.2.5/nhlpy/data/team_stat_ids.json +239 -0
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/http_client.py +3 -2
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/nhl_client.py +3 -1
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/pyproject.toml +2 -2
- nhl_api_py-2.1.2/nhlpy/api/teams.py +0 -30
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/__init__.py +0 -0
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/api/__init__.py +0 -0
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/api/game_center.py +0 -0
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/api/helpers.py +0 -0
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/config.py +0 -0
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/data/seasonal_information_manifest.json +0 -0
- {nhl_api_py-2.1.2 → nhl_api_py-2.2.5}/nhlpy/data/teams_20232024.json +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nhl-api-py
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.5
|
|
4
4
|
Summary: 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.
|
|
5
5
|
Home-page: https://github.com/coreyjs/nhl-api-py
|
|
6
6
|
License: GPL-3.0-or-later
|
|
7
7
|
Keywords: nhl,api,wrapper,hockey,sports
|
|
8
8
|
Author: Corey Schaf
|
|
9
9
|
Author-email: cschaf@gmail.com
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.9,<4.0
|
|
11
11
|
Classifier: Development Status :: 3 - Alpha
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -55,6 +56,41 @@ client = NHLClient(verbose=True) # a tad more logging such as the URL being call
|
|
|
55
56
|
```
|
|
56
57
|
|
|
57
58
|
|
|
59
|
+
### Stats Endpoints (In development)
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
|
|
63
|
+
client.club_stats_season(team_abbr="BUF") # kinda weird endpoint.
|
|
64
|
+
|
|
65
|
+
client.player_career_stats(player_id=8478402)
|
|
66
|
+
|
|
67
|
+
# Team Summary Stats.
|
|
68
|
+
# These have lots of available parameters. You can also tap into the apache cayenne expressions to build custom
|
|
69
|
+
# queries, if you have that knowledge.
|
|
70
|
+
client.stats.team_summary(start_season="20202021", end_season="20212022", game_type_id=2)
|
|
71
|
+
client.stats.team_summary(start_season="20202021", end_season="20212022")
|
|
72
|
+
|
|
73
|
+
###
|
|
74
|
+
# Skater Summary Stats.
|
|
75
|
+
# Queries for skaters for year ranges, filterable down by franchise.
|
|
76
|
+
client.stats.skater_stats_summary(start_season="20232024", end_season="20232024")
|
|
77
|
+
client.stats.skater_stats_summary(franchise_id=10, start_season="20232024", end_season="20232024")
|
|
78
|
+
|
|
79
|
+
# skater_stats_summary_by_expression is more advanced method. It allows for more direct manipulation of the query and
|
|
80
|
+
# the cayenne expression clauses.
|
|
81
|
+
sort_expr = [
|
|
82
|
+
{"property": "points", "direction": "DESC"},
|
|
83
|
+
{"property": "gamesPlayed", "direction": "ASC"},
|
|
84
|
+
{"property": "playerId", "direction": "ASC"},
|
|
85
|
+
]
|
|
86
|
+
expr = "gameTypeId=2 and seasonId<=20232024 and seasonId>=20222023"
|
|
87
|
+
client.stats.skater_stats_summary_by_expression(default_cayenne_exp=expr, sort_expr=sort_expr)
|
|
88
|
+
|
|
89
|
+
###
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
|
|
58
94
|
### Schedule Endpoints
|
|
59
95
|
|
|
60
96
|
```python
|
|
@@ -65,6 +101,7 @@ client.schedule.get_schedule_by_team_by_month(team_abbr="BUF")
|
|
|
65
101
|
client.schedule.get_schedule_by_team_by_month(team_abbr="BUF", month="2021-01")
|
|
66
102
|
|
|
67
103
|
client.schedule.get_schedule_by_team_by_week(team_abbr="BUF")
|
|
104
|
+
client.schedule.get_schedule_by_team_by_week(team_abbr="BUF", date="2024-01-01")
|
|
68
105
|
|
|
69
106
|
client.schedule.get_season_schedule(team_abbr="BUF", season="20212022")
|
|
70
107
|
|
|
@@ -105,6 +142,19 @@ client.game_center.score_now()
|
|
|
105
142
|
```
|
|
106
143
|
|
|
107
144
|
|
|
145
|
+
### Misc Endpoints
|
|
146
|
+
```python
|
|
147
|
+
client.misc.glossary()
|
|
148
|
+
|
|
149
|
+
client.misc.config()
|
|
150
|
+
|
|
151
|
+
client.misc.countries()
|
|
152
|
+
|
|
153
|
+
client.misc.season_specific_rules_and_info()
|
|
154
|
+
|
|
155
|
+
client.misc.draft_year_and_rounds()
|
|
156
|
+
```
|
|
157
|
+
|
|
108
158
|
---
|
|
109
159
|
### Insomnia Rest Client Export
|
|
110
160
|
|
|
@@ -30,6 +30,41 @@ client = NHLClient(verbose=True) # a tad more logging such as the URL being call
|
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
### Stats Endpoints (In development)
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
|
|
37
|
+
client.club_stats_season(team_abbr="BUF") # kinda weird endpoint.
|
|
38
|
+
|
|
39
|
+
client.player_career_stats(player_id=8478402)
|
|
40
|
+
|
|
41
|
+
# Team Summary Stats.
|
|
42
|
+
# These have lots of available parameters. You can also tap into the apache cayenne expressions to build custom
|
|
43
|
+
# queries, if you have that knowledge.
|
|
44
|
+
client.stats.team_summary(start_season="20202021", end_season="20212022", game_type_id=2)
|
|
45
|
+
client.stats.team_summary(start_season="20202021", end_season="20212022")
|
|
46
|
+
|
|
47
|
+
###
|
|
48
|
+
# Skater Summary Stats.
|
|
49
|
+
# Queries for skaters for year ranges, filterable down by franchise.
|
|
50
|
+
client.stats.skater_stats_summary(start_season="20232024", end_season="20232024")
|
|
51
|
+
client.stats.skater_stats_summary(franchise_id=10, start_season="20232024", end_season="20232024")
|
|
52
|
+
|
|
53
|
+
# skater_stats_summary_by_expression is more advanced method. It allows for more direct manipulation of the query and
|
|
54
|
+
# the cayenne expression clauses.
|
|
55
|
+
sort_expr = [
|
|
56
|
+
{"property": "points", "direction": "DESC"},
|
|
57
|
+
{"property": "gamesPlayed", "direction": "ASC"},
|
|
58
|
+
{"property": "playerId", "direction": "ASC"},
|
|
59
|
+
]
|
|
60
|
+
expr = "gameTypeId=2 and seasonId<=20232024 and seasonId>=20222023"
|
|
61
|
+
client.stats.skater_stats_summary_by_expression(default_cayenne_exp=expr, sort_expr=sort_expr)
|
|
62
|
+
|
|
63
|
+
###
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
|
|
33
68
|
### Schedule Endpoints
|
|
34
69
|
|
|
35
70
|
```python
|
|
@@ -40,6 +75,7 @@ client.schedule.get_schedule_by_team_by_month(team_abbr="BUF")
|
|
|
40
75
|
client.schedule.get_schedule_by_team_by_month(team_abbr="BUF", month="2021-01")
|
|
41
76
|
|
|
42
77
|
client.schedule.get_schedule_by_team_by_week(team_abbr="BUF")
|
|
78
|
+
client.schedule.get_schedule_by_team_by_week(team_abbr="BUF", date="2024-01-01")
|
|
43
79
|
|
|
44
80
|
client.schedule.get_season_schedule(team_abbr="BUF", season="20212022")
|
|
45
81
|
|
|
@@ -80,6 +116,19 @@ client.game_center.score_now()
|
|
|
80
116
|
```
|
|
81
117
|
|
|
82
118
|
|
|
119
|
+
### Misc Endpoints
|
|
120
|
+
```python
|
|
121
|
+
client.misc.glossary()
|
|
122
|
+
|
|
123
|
+
client.misc.config()
|
|
124
|
+
|
|
125
|
+
client.misc.countries()
|
|
126
|
+
|
|
127
|
+
client.misc.season_specific_rules_and_info()
|
|
128
|
+
|
|
129
|
+
client.misc.draft_year_and_rounds()
|
|
130
|
+
```
|
|
131
|
+
|
|
83
132
|
---
|
|
84
133
|
### Insomnia Rest Client Export
|
|
85
134
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from nhlpy.http_client import HttpClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Misc:
|
|
7
|
+
def __init__(self, http_client: HttpClient) -> None:
|
|
8
|
+
self.client = http_client
|
|
9
|
+
|
|
10
|
+
def glossary(self) -> List[dict]:
|
|
11
|
+
"""
|
|
12
|
+
Get the glossary for the NHL API.
|
|
13
|
+
:return: dict
|
|
14
|
+
"""
|
|
15
|
+
return self.client.get_by_url(full_resource="https://api.nhle.com/stats/rest/en/glossary?sort=fullName").json()[
|
|
16
|
+
"data"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
def config(self) -> dict:
|
|
20
|
+
"""
|
|
21
|
+
Seems to be various options for the filters?.
|
|
22
|
+
:return: dict
|
|
23
|
+
"""
|
|
24
|
+
return self.client.get_by_url(full_resource="https://api.nhle.com/stats/rest/en/config").json()
|
|
25
|
+
|
|
26
|
+
def countries(self) -> List[dict]:
|
|
27
|
+
"""
|
|
28
|
+
Get the countries for the NHL API.
|
|
29
|
+
:return: dict
|
|
30
|
+
"""
|
|
31
|
+
return self.client.get_by_url(full_resource="https://api.nhle.com/stats/rest/en/country").json()["data"]
|
|
32
|
+
|
|
33
|
+
def season_specific_rules_and_info(self) -> List[dict]:
|
|
34
|
+
"""
|
|
35
|
+
Get the season specific rules and info for the NHL API.
|
|
36
|
+
:return: dict
|
|
37
|
+
"""
|
|
38
|
+
return self.client.get_by_url(full_resource="https://api.nhle.com/stats/rest/en/season").json()["data"]
|
|
39
|
+
|
|
40
|
+
def draft_year_and_rounds(self) -> List[dict]:
|
|
41
|
+
"""
|
|
42
|
+
Get the draft year and rounds for the NHL API. This only has "id", "draftYear" and "rounds count".
|
|
43
|
+
:return: dict
|
|
44
|
+
"""
|
|
45
|
+
return self.client.get_by_url(full_resource="https://api.nhle.com/stats/rest/en/draft").json()["data"]
|
|
@@ -29,13 +29,15 @@ class Schedule:
|
|
|
29
29
|
resource = f"club-schedule/{team_abbr}/month/{month if month else 'now'}"
|
|
30
30
|
return self.client.get(resource=resource).json()["games"]
|
|
31
31
|
|
|
32
|
-
def get_schedule_by_team_by_week(self, team_abbr: str) -> List[dict]:
|
|
32
|
+
def get_schedule_by_team_by_week(self, team_abbr: str, date: Optional[str] = None) -> List[dict]:
|
|
33
33
|
"""
|
|
34
|
-
This returns the schedule for the team (team_abbr) for the current week.
|
|
34
|
+
This returns the schedule for the team (team_abbr) for the week set or the current week if no week is specified.
|
|
35
|
+
|
|
36
|
+
:param date: Optional date, in which to search the week of. In format YYYY-MM-DD. Example "2024-02-10".
|
|
35
37
|
:param team_abbr: The 3 letter abbreviation of the team. BUF, TOR, etc
|
|
36
38
|
:return:
|
|
37
39
|
"""
|
|
38
|
-
resource = f"club-schedule/{team_abbr}/week/now"
|
|
40
|
+
resource = f"club-schedule/{team_abbr}/week/{date if date else 'now'}"
|
|
39
41
|
return self.client.get(resource=resource).json()["games"]
|
|
40
42
|
|
|
41
43
|
def get_season_schedule(self, team_abbr: str, season: str) -> dict:
|
|
@@ -24,8 +24,8 @@ class Standings:
|
|
|
24
24
|
# load json from data/seasonal_information_manifest.json
|
|
25
25
|
import json
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
data_resource = importlib.resources.files("nhlpy") / "data"
|
|
28
|
+
seasons = json.loads((data_resource / "seasonal_information_manifest.json").read_text())["seasons"]
|
|
29
29
|
else:
|
|
30
30
|
seasons = self.season_standing_manifest()
|
|
31
31
|
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import urllib.parse
|
|
2
|
+
import json
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from nhlpy.http_client import HttpClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Stats:
|
|
10
|
+
def __init__(self, http_client: HttpClient):
|
|
11
|
+
self.client = http_client
|
|
12
|
+
|
|
13
|
+
def club_stats_season(self, team_abbr: str) -> dict:
|
|
14
|
+
"""
|
|
15
|
+
This seems to return gameTypes for every season the team was in existence. Maybe its useful?
|
|
16
|
+
:param team_abbr: The 3 letter abbreviation of the team. BUF, TOR, etc
|
|
17
|
+
:return: dict
|
|
18
|
+
"""
|
|
19
|
+
return self.client.get(resource=f"club-stats-season/{team_abbr}").json()
|
|
20
|
+
|
|
21
|
+
def player_career_stats(self, player_id: int) -> dict:
|
|
22
|
+
"""
|
|
23
|
+
This returns the career stats for a player as well as player information.
|
|
24
|
+
|
|
25
|
+
example: https://api-web.nhle.com/v1/player/8481528/landing
|
|
26
|
+
|
|
27
|
+
:param player_id: The player_id for the player you want the stats for.
|
|
28
|
+
:return: dict
|
|
29
|
+
"""
|
|
30
|
+
return self.client.get(resource=f"player/{player_id}/landing").json()
|
|
31
|
+
|
|
32
|
+
def team_summary(
|
|
33
|
+
self,
|
|
34
|
+
start_season: str,
|
|
35
|
+
end_season: str,
|
|
36
|
+
game_type_id: int = 2,
|
|
37
|
+
is_game: bool = False,
|
|
38
|
+
is_aggregate: bool = False,
|
|
39
|
+
sort_expr: List[dict] = None,
|
|
40
|
+
start: int = 0,
|
|
41
|
+
limit: int = 50,
|
|
42
|
+
fact_cayenne_exp: str = "gamesPlayed>1",
|
|
43
|
+
default_cayenne_exp: str = None,
|
|
44
|
+
) -> List[dict]:
|
|
45
|
+
"""
|
|
46
|
+
Example: c.stats.team_summary(start_season="20202021", end_season="20212022", game_type_id=2)
|
|
47
|
+
c.stats.team_summary(start_season="20202021", end_season="20212022")
|
|
48
|
+
:param start_season: Season id, in format 20202021, 20212022, etc, that will be the start of the range. This
|
|
49
|
+
allows for searching over multiple season. If you just want one season, set start_season and end_season.
|
|
50
|
+
:param end_season: Season id for the end range.
|
|
51
|
+
:param game_type_id: 2 is for regular season, 3 is for playoffs. I think 1 is for preseason.
|
|
52
|
+
:param is_game: (dev notes: not sure what this is, its part of the api call)
|
|
53
|
+
:param is_aggregate:
|
|
54
|
+
:param sort_expr: A list of key/value pairs of properties and their sort direction. For Example this defaults
|
|
55
|
+
to [
|
|
56
|
+
{"property":"points","direction":"DESC"},
|
|
57
|
+
{"property":"wins","direction":"DESC"},
|
|
58
|
+
{"property":"teamId","direction":"ASC"}
|
|
59
|
+
]
|
|
60
|
+
:param start:
|
|
61
|
+
:param limit:
|
|
62
|
+
:param fact_cayenne_exp: An expression used by apache cayenne. This defaults to 'gamesPlayed>=1'.
|
|
63
|
+
You can experiment at will, this was taken from the nhl.com website.
|
|
64
|
+
:param default_cayenne_exp: Similar to above, I believe this provides a filter for the data returned.
|
|
65
|
+
This will look something like: "gameTypeId=2 and seasonId<=20232024 and seasonId>=20232024".
|
|
66
|
+
|
|
67
|
+
If this value is supplied, it will override a generated one, giving the user ability to experiment.
|
|
68
|
+
:return:
|
|
69
|
+
"""
|
|
70
|
+
q_params = {
|
|
71
|
+
"isAggregate": is_aggregate,
|
|
72
|
+
"isGame": is_game,
|
|
73
|
+
"start": start,
|
|
74
|
+
"limit": limit,
|
|
75
|
+
"factCayenneExp": fact_cayenne_exp,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if not sort_expr:
|
|
79
|
+
sort_expr = [
|
|
80
|
+
{"property": "points", "direction": "DESC"},
|
|
81
|
+
{"property": "wins", "direction": "DESC"},
|
|
82
|
+
{"property": "teamId", "direction": "ASC"},
|
|
83
|
+
]
|
|
84
|
+
q_params["sort"] = urllib.parse.quote(json.dumps(sort_expr))
|
|
85
|
+
|
|
86
|
+
if not default_cayenne_exp:
|
|
87
|
+
default_cayenne_exp = f"gameTypeId={game_type_id} and seasonId<={end_season} and seasonId>={start_season}"
|
|
88
|
+
q_params["cayenneExp"] = default_cayenne_exp
|
|
89
|
+
|
|
90
|
+
return self.client.get_by_url("https://api.nhle.com/stats/rest/en/team/summary", query_params=q_params).json()[
|
|
91
|
+
"data"
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
def skater_stats_summary(
|
|
95
|
+
self,
|
|
96
|
+
start_season: str,
|
|
97
|
+
end_season: str,
|
|
98
|
+
franchise_id: int = None,
|
|
99
|
+
game_type_id: int = 2,
|
|
100
|
+
aggregate: bool = False,
|
|
101
|
+
sort_expr: List[dict] = None,
|
|
102
|
+
start: int = 0,
|
|
103
|
+
limit: int = 70,
|
|
104
|
+
fact_cayenne_exp: str = "gamesPlayed>=1",
|
|
105
|
+
default_cayenne_exp: str = None,
|
|
106
|
+
) -> List[dict]:
|
|
107
|
+
"""
|
|
108
|
+
Example: c.stats.skater_stats_summary(start_season="20232024", end_season="20232024")
|
|
109
|
+
c.stats.skater_stats_summary(franchise_id=10, start_season="20232024", end_season="20232024")
|
|
110
|
+
:param start_season: Season id, in format 20202021, 20212022, etc, that will be the start of the range.
|
|
111
|
+
:param end_season: Season id for the end range.
|
|
112
|
+
:param franchise_id: The ID of the franchise. Not to be confused with team_id found on other endpoints.
|
|
113
|
+
This seems to be specific to the /stats apis.
|
|
114
|
+
:param game_type_id: 2 is for regular season, 3 is for playoffs. I think 1 is for preseason.
|
|
115
|
+
:param aggregate: If doing multiple years, you can choose to aggreate the date per player, or have separate
|
|
116
|
+
entries for each one.
|
|
117
|
+
:param sort_expr: Default sorting expresions. Provided as an array of key/value pairs. For example:
|
|
118
|
+
[
|
|
119
|
+
{"property": "points", "direction": "DESC"},
|
|
120
|
+
{"property": "gamesPlayed", "direction": "ASC"},
|
|
121
|
+
{"property": "playerId", "direction": "ASC"}
|
|
122
|
+
]
|
|
123
|
+
:param start: Possibly start of the retrived data, based on limit.
|
|
124
|
+
:param limit: How many to return.
|
|
125
|
+
:param fact_cayenne_exp: An anchor expression almost, default criteria. Only players with more than 1 game
|
|
126
|
+
played. I default this to gamesPlayed>=1, which is what the nhl.com site uses. But you can play with it.
|
|
127
|
+
:param default_cayenne_exp:
|
|
128
|
+
:return:
|
|
129
|
+
"""
|
|
130
|
+
q_params = {
|
|
131
|
+
"isAggregate": aggregate,
|
|
132
|
+
"isGame": False,
|
|
133
|
+
"start": start,
|
|
134
|
+
"limit": limit,
|
|
135
|
+
"factCayenneExp": fact_cayenne_exp,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if not sort_expr:
|
|
139
|
+
sort_expr = [
|
|
140
|
+
{"property": "points", "direction": "DESC"},
|
|
141
|
+
{"property": "gamesPlayed", "direction": "ASC"},
|
|
142
|
+
{"property": "playerId", "direction": "ASC"},
|
|
143
|
+
]
|
|
144
|
+
q_params["sort"] = urllib.parse.quote(json.dumps(sort_expr))
|
|
145
|
+
|
|
146
|
+
if not default_cayenne_exp:
|
|
147
|
+
default_cayenne_exp = f"gameTypeId={game_type_id} and seasonId<={end_season} and seasonId>={start_season}"
|
|
148
|
+
if franchise_id:
|
|
149
|
+
default_cayenne_exp = f"franchiseId={franchise_id} and {default_cayenne_exp}"
|
|
150
|
+
q_params["cayenneExp"] = default_cayenne_exp
|
|
151
|
+
|
|
152
|
+
return self.client.get_by_url("https://api.nhle.com/stats/rest/en/skater/summary", query_params=q_params).json()[
|
|
153
|
+
"data"
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
def skater_stats_summary_by_expression(
|
|
157
|
+
self,
|
|
158
|
+
cayenne_exp: str,
|
|
159
|
+
sort_expr: List[dict],
|
|
160
|
+
aggregate: bool = False,
|
|
161
|
+
start: int = 0,
|
|
162
|
+
limit: int = 70,
|
|
163
|
+
fact_cayenne_exp: str = "gamesPlayed>=1",
|
|
164
|
+
) -> dict:
|
|
165
|
+
"""
|
|
166
|
+
A more bare bones / raw version of skater_stats_summary. This allows for more flexibility in the query params.
|
|
167
|
+
You must supply your own cayenne expressions and sort expressions.
|
|
168
|
+
|
|
169
|
+
example:
|
|
170
|
+
sort_expr = [
|
|
171
|
+
{"property": "points", "direction": "DESC"},
|
|
172
|
+
{"property": "gamesPlayed", "direction": "ASC"},
|
|
173
|
+
{"property": "playerId", "direction": "ASC"}
|
|
174
|
+
]
|
|
175
|
+
cayenne_exp = "gameTypeId=2 and seasonId<=20232024 and seasonId>=20232024"
|
|
176
|
+
client.stats.skater_stats_summary_by_expression(cayenne_exp=expr, sort_expr=sort_expr)
|
|
177
|
+
|
|
178
|
+
:param aggregate: bool - If doing multiple years, you can choose to aggreate the date per player,
|
|
179
|
+
or have separate entries for each one.
|
|
180
|
+
:param sort_expr: A list of key/value pairs for sort criteria. As used in skater_stats_summary(), this is
|
|
181
|
+
in the format:
|
|
182
|
+
[
|
|
183
|
+
{"property": "points", "direction": "DESC"},
|
|
184
|
+
{"property": "gamesPlayed", "direction": "ASC"},
|
|
185
|
+
{"property": "playerId", "direction": "ASC"}
|
|
186
|
+
]
|
|
187
|
+
:param start:
|
|
188
|
+
:param limit:
|
|
189
|
+
:param fact_cayenne_exp:
|
|
190
|
+
:param default_cayenne_exp:
|
|
191
|
+
:return:
|
|
192
|
+
"""
|
|
193
|
+
q_params = {
|
|
194
|
+
"isAggregate": aggregate,
|
|
195
|
+
"isGame": False,
|
|
196
|
+
"start": start,
|
|
197
|
+
"limit": limit,
|
|
198
|
+
"factCayenneExp": fact_cayenne_exp,
|
|
199
|
+
}
|
|
200
|
+
q_params["sort"] = urllib.parse.quote(json.dumps(sort_expr))
|
|
201
|
+
q_params["cayenneExp"] = cayenne_exp
|
|
202
|
+
return self.client.get_by_url("https://api.nhle.com/stats/rest/en/skater/summary", query_params=q_params).json()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import importlib.resources
|
|
3
|
+
from warnings import warn
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from nhlpy.http_client import HttpClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Teams:
|
|
10
|
+
def __init__(self, http_client: HttpClient) -> None:
|
|
11
|
+
self.client = http_client
|
|
12
|
+
self.base_url = "https://api.nhle.com"
|
|
13
|
+
self.api_ver = "/stats/rest/"
|
|
14
|
+
|
|
15
|
+
# todo deprecate this
|
|
16
|
+
def team_stats_summary(self, lang="en") -> List[dict]:
|
|
17
|
+
"""
|
|
18
|
+
I really don't know what this endpoint does. It returns a list of dicts with team stats.
|
|
19
|
+
:param lang: Language param. 'en' for English, 'fr' for French
|
|
20
|
+
:return:
|
|
21
|
+
"""
|
|
22
|
+
warn(
|
|
23
|
+
"This endpoint will be deprecated in the future. Use stats.team_summary() instead.",
|
|
24
|
+
DeprecationWarning,
|
|
25
|
+
stacklevel=2,
|
|
26
|
+
)
|
|
27
|
+
return self.client.get_by_url(full_resource=f"{self.base_url}{self.api_ver}{lang}/team/summary").json()["data"]
|
|
28
|
+
|
|
29
|
+
def teams_info(self) -> dict:
|
|
30
|
+
"""
|
|
31
|
+
Returns a list of dicts with all the teams info. This is loaded via a harccoded json file.
|
|
32
|
+
:return: dict
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# with importlib.resources.files("nhlpy.data", "teams_20232024.json") as f:
|
|
36
|
+
# return json.load(f)['teams']
|
|
37
|
+
|
|
38
|
+
data_resource = importlib.resources.files("nhlpy") / "data"
|
|
39
|
+
teams_info = json.loads((data_resource / "teams_20232024.json").read_text())["teams"]
|
|
40
|
+
return teams_info
|
|
41
|
+
|
|
42
|
+
def roster(self, team_abbr: str, season: str) -> dict:
|
|
43
|
+
"""
|
|
44
|
+
Returns the roster for the given team and season.
|
|
45
|
+
:param team_abbr: Team abbreviation. BUF, TOR, etc
|
|
46
|
+
:param season: Season in format YYYYYYYY. 20202021, 20212022, etc
|
|
47
|
+
:return:
|
|
48
|
+
"""
|
|
49
|
+
return self.client.get(resource=f"roster/{team_abbr}/{season}").json()
|
|
50
|
+
|
|
51
|
+
def all_franchises(self) -> List[dict]:
|
|
52
|
+
"""
|
|
53
|
+
Returns a list of all past and current NHL franchises. This includes ones long forgotten.
|
|
54
|
+
:return:
|
|
55
|
+
"""
|
|
56
|
+
return self.client.get_by_url(full_resource="https://api.nhle.com/stats/rest/en/franchise").json()["data"]
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
{
|
|
2
|
+
"data": [
|
|
3
|
+
{
|
|
4
|
+
"id": 1,
|
|
5
|
+
"fullName": "Montréal Canadiens",
|
|
6
|
+
"teamCommonName": "Canadiens",
|
|
7
|
+
"teamPlaceName": "Montréal"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": 2,
|
|
11
|
+
"fullName": "Montreal Wanderers",
|
|
12
|
+
"teamCommonName": "Wanderers",
|
|
13
|
+
"teamPlaceName": "Montreal"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": 3,
|
|
17
|
+
"fullName": "St. Louis Eagles",
|
|
18
|
+
"teamCommonName": "Eagles",
|
|
19
|
+
"teamPlaceName": "St. Louis"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": 4,
|
|
23
|
+
"fullName": "Hamilton Tigers",
|
|
24
|
+
"teamCommonName": "Tigers",
|
|
25
|
+
"teamPlaceName": "Hamilton"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": 5,
|
|
29
|
+
"fullName": "Toronto Maple Leafs",
|
|
30
|
+
"teamCommonName": "Maple Leafs",
|
|
31
|
+
"teamPlaceName": "Toronto"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": 6,
|
|
35
|
+
"fullName": "Boston Bruins",
|
|
36
|
+
"teamCommonName": "Bruins",
|
|
37
|
+
"teamPlaceName": "Boston"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": 7,
|
|
41
|
+
"fullName": "Montreal Maroons",
|
|
42
|
+
"teamCommonName": "Maroons",
|
|
43
|
+
"teamPlaceName": "Montreal"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": 8,
|
|
47
|
+
"fullName": "Brooklyn Americans",
|
|
48
|
+
"teamCommonName": "Americans",
|
|
49
|
+
"teamPlaceName": "Brooklyn"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": 9,
|
|
53
|
+
"fullName": "Philadelphia Quakers",
|
|
54
|
+
"teamCommonName": "Quakers",
|
|
55
|
+
"teamPlaceName": "Philadelphia"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": 10,
|
|
59
|
+
"fullName": "New York Rangers",
|
|
60
|
+
"teamCommonName": "Rangers",
|
|
61
|
+
"teamPlaceName": "New York"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"id": 11,
|
|
65
|
+
"fullName": "Chicago Blackhawks",
|
|
66
|
+
"teamCommonName": "Blackhawks",
|
|
67
|
+
"teamPlaceName": "Chicago"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": 12,
|
|
71
|
+
"fullName": "Detroit Red Wings",
|
|
72
|
+
"teamCommonName": "Red Wings",
|
|
73
|
+
"teamPlaceName": "Detroit"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"id": 13,
|
|
77
|
+
"fullName": "Cleveland Barons",
|
|
78
|
+
"teamCommonName": "Barons",
|
|
79
|
+
"teamPlaceName": "Cleveland"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": 14,
|
|
83
|
+
"fullName": "Los Angeles Kings",
|
|
84
|
+
"teamCommonName": "Kings",
|
|
85
|
+
"teamPlaceName": "Los Angeles"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"id": 15,
|
|
89
|
+
"fullName": "Dallas Stars",
|
|
90
|
+
"teamCommonName": "Stars",
|
|
91
|
+
"teamPlaceName": "Dallas"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": 16,
|
|
95
|
+
"fullName": "Philadelphia Flyers",
|
|
96
|
+
"teamCommonName": "Flyers",
|
|
97
|
+
"teamPlaceName": "Philadelphia"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": 17,
|
|
101
|
+
"fullName": "Pittsburgh Penguins",
|
|
102
|
+
"teamCommonName": "Penguins",
|
|
103
|
+
"teamPlaceName": "Pittsburgh"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"id": 18,
|
|
107
|
+
"fullName": "St. Louis Blues",
|
|
108
|
+
"teamCommonName": "Blues",
|
|
109
|
+
"teamPlaceName": "St. Louis"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": 19,
|
|
113
|
+
"fullName": "Buffalo Sabres",
|
|
114
|
+
"teamCommonName": "Sabres",
|
|
115
|
+
"teamPlaceName": "Buffalo"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"id": 20,
|
|
119
|
+
"fullName": "Vancouver Canucks",
|
|
120
|
+
"teamCommonName": "Canucks",
|
|
121
|
+
"teamPlaceName": "Vancouver"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"id": 21,
|
|
125
|
+
"fullName": "Calgary Flames",
|
|
126
|
+
"teamCommonName": "Flames",
|
|
127
|
+
"teamPlaceName": "Calgary"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"id": 22,
|
|
131
|
+
"fullName": "New York Islanders",
|
|
132
|
+
"teamCommonName": "Islanders",
|
|
133
|
+
"teamPlaceName": "New York"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"id": 23,
|
|
137
|
+
"fullName": "New Jersey Devils",
|
|
138
|
+
"teamCommonName": "Devils",
|
|
139
|
+
"teamPlaceName": "New Jersey"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": 24,
|
|
143
|
+
"fullName": "Washington Capitals",
|
|
144
|
+
"teamCommonName": "Capitals",
|
|
145
|
+
"teamPlaceName": "Washington"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"id": 25,
|
|
149
|
+
"fullName": "Edmonton Oilers",
|
|
150
|
+
"teamCommonName": "Oilers",
|
|
151
|
+
"teamPlaceName": "Edmonton"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"id": 26,
|
|
155
|
+
"fullName": "Carolina Hurricanes",
|
|
156
|
+
"teamCommonName": "Hurricanes",
|
|
157
|
+
"teamPlaceName": "Carolina"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"id": 27,
|
|
161
|
+
"fullName": "Colorado Avalanche",
|
|
162
|
+
"teamCommonName": "Avalanche",
|
|
163
|
+
"teamPlaceName": "Colorado"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"id": 28,
|
|
167
|
+
"fullName": "Arizona Coyotes",
|
|
168
|
+
"teamCommonName": "Coyotes",
|
|
169
|
+
"teamPlaceName": "Arizona"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"id": 29,
|
|
173
|
+
"fullName": "San Jose Sharks",
|
|
174
|
+
"teamCommonName": "Sharks",
|
|
175
|
+
"teamPlaceName": "San Jose"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"id": 30,
|
|
179
|
+
"fullName": "Ottawa Senators",
|
|
180
|
+
"teamCommonName": "Senators",
|
|
181
|
+
"teamPlaceName": "Ottawa"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"id": 31,
|
|
185
|
+
"fullName": "Tampa Bay Lightning",
|
|
186
|
+
"teamCommonName": "Lightning",
|
|
187
|
+
"teamPlaceName": "Tampa Bay"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"id": 32,
|
|
191
|
+
"fullName": "Anaheim Ducks",
|
|
192
|
+
"teamCommonName": "Ducks",
|
|
193
|
+
"teamPlaceName": "Anaheim"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"id": 33,
|
|
197
|
+
"fullName": "Florida Panthers",
|
|
198
|
+
"teamCommonName": "Panthers",
|
|
199
|
+
"teamPlaceName": "Florida"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"id": 34,
|
|
203
|
+
"fullName": "Nashville Predators",
|
|
204
|
+
"teamCommonName": "Predators",
|
|
205
|
+
"teamPlaceName": "Nashville"
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"id": 35,
|
|
209
|
+
"fullName": "Winnipeg Jets",
|
|
210
|
+
"teamCommonName": "Jets",
|
|
211
|
+
"teamPlaceName": "Winnipeg"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"id": 36,
|
|
215
|
+
"fullName": "Columbus Blue Jackets",
|
|
216
|
+
"teamCommonName": "Blue Jackets",
|
|
217
|
+
"teamPlaceName": "Columbus"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"id": 37,
|
|
221
|
+
"fullName": "Minnesota Wild",
|
|
222
|
+
"teamCommonName": "Wild",
|
|
223
|
+
"teamPlaceName": "Minnesota"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"id": 38,
|
|
227
|
+
"fullName": "Vegas Golden Knights",
|
|
228
|
+
"teamCommonName": "Golden Knights",
|
|
229
|
+
"teamPlaceName": "Vegas"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"id": 39,
|
|
233
|
+
"fullName": "Seattle Kraken",
|
|
234
|
+
"teamCommonName": "Kraken",
|
|
235
|
+
"teamPlaceName": "Seattle"
|
|
236
|
+
}
|
|
237
|
+
],
|
|
238
|
+
"total": 39
|
|
239
|
+
}
|
|
@@ -23,13 +23,14 @@ class HttpClient:
|
|
|
23
23
|
|
|
24
24
|
return r
|
|
25
25
|
|
|
26
|
-
def get_by_url(self, full_resource: str) -> httpx.request:
|
|
26
|
+
def get_by_url(self, full_resource: str, query_params: dict = None) -> httpx.request:
|
|
27
27
|
"""
|
|
28
28
|
Private method to make a get request to any HTTP resource. This wraps the lib httpx functionality.
|
|
29
|
+
:param query_params:
|
|
29
30
|
:param full_resource: The full resource to get.
|
|
30
31
|
:return:
|
|
31
32
|
"""
|
|
32
|
-
r: httpx.request = httpx.get(url=full_resource, follow_redirects=True)
|
|
33
|
+
r: httpx.request = httpx.get(url=full_resource, params=query_params, follow_redirects=True)
|
|
33
34
|
|
|
34
35
|
if self._config.verbose:
|
|
35
36
|
logging.info(f"API URL: {r.url}")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from nhlpy.api import teams, standings, schedule, game_center
|
|
1
|
+
from nhlpy.api import teams, standings, schedule, game_center, stats, misc
|
|
2
2
|
from nhlpy.http_client import HttpClient
|
|
3
3
|
from nhlpy.config import ClientConfig
|
|
4
4
|
|
|
@@ -24,3 +24,5 @@ class NHLClient:
|
|
|
24
24
|
self.standings = standings.Standings(http_client=self._http_client)
|
|
25
25
|
self.schedule = schedule.Schedule(http_client=self._http_client)
|
|
26
26
|
self.game_center = game_center.GameCenter(http_client=self._http_client)
|
|
27
|
+
self.stats = stats.Stats(http_client=self._http_client)
|
|
28
|
+
self.misc = misc.Misc(http_client=self._http_client)
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "nhl-api-py"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.2.5"
|
|
8
8
|
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."
|
|
9
9
|
authors = ["Corey Schaf <cschaf@gmail.com>"]
|
|
10
10
|
readme = "README.md"
|
|
@@ -30,7 +30,7 @@ include = [
|
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[tool.poetry.dependencies]
|
|
33
|
-
python = "^3.
|
|
33
|
+
python = "^3.9"
|
|
34
34
|
httpx = "*"
|
|
35
35
|
|
|
36
36
|
[tool.poetry.group.dev.dependencies]
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import importlib.resources
|
|
3
|
-
|
|
4
|
-
from typing import List
|
|
5
|
-
|
|
6
|
-
from nhlpy.http_client import HttpClient
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Teams:
|
|
10
|
-
def __init__(self, http_client: HttpClient) -> None:
|
|
11
|
-
self.client = http_client
|
|
12
|
-
self.base_url = "https://api.nhle.com"
|
|
13
|
-
self.api_ver = "/stats/rest/"
|
|
14
|
-
|
|
15
|
-
def team_stats_summary(self, lang="en") -> List[dict]:
|
|
16
|
-
"""
|
|
17
|
-
I really dont know what this endpoint does. It returns a list of dicts with team stats.
|
|
18
|
-
:param lang: Language param. 'en' for English, 'fr' for French
|
|
19
|
-
:return:
|
|
20
|
-
"""
|
|
21
|
-
return self.client.get_by_url(full_resource=f"{self.base_url}{self.api_ver}{lang}/team/summary").json()["data"]
|
|
22
|
-
|
|
23
|
-
def teams_info(self) -> dict:
|
|
24
|
-
"""
|
|
25
|
-
Returns a list of dicts with all the teams info. This is loaded via a harccoded json file.
|
|
26
|
-
:return: dict
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
with importlib.resources.open_text("nhlpy.data", "teams_20232024.json") as f:
|
|
30
|
-
return json.load(f)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|