nhl-api-py 2.2.6__tar.gz → 2.3.1__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.3.1/PKG-INFO +370 -0
- nhl_api_py-2.3.1/README.md +344 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/_version.py +1 -1
- nhl_api_py-2.3.1/nhlpy/api/__init__.py +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/api/game_center.py +5 -2
- nhl_api_py-2.3.1/nhlpy/api/query/__init__.py +2 -0
- nhl_api_py-2.3.1/nhlpy/api/query/builder.py +50 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/__init__.py +12 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/decision.py +28 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/draft.py +27 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/experience.py +20 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/franchise.py +15 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/game_type.py +15 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/home_road.py +19 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/nationality.py +31 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/opponent.py +19 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/position.py +32 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/season.py +22 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/shoot_catch.py +19 -0
- nhl_api_py-2.3.1/nhlpy/api/query/filters/status.py +32 -0
- nhl_api_py-2.3.1/nhlpy/api/query/sorting/__init__.py +0 -0
- nhl_api_py-2.3.1/nhlpy/api/query/sorting/sorting_options.py +146 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/api/stats.py +21 -14
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/pyproject.toml +1 -1
- nhl_api_py-2.2.6/PKG-INFO +0 -195
- nhl_api_py-2.2.6/README.md +0 -169
- nhl_api_py-2.2.6/nhlpy/api/__init__.py +0 -30
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/__init__.py +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/api/helpers.py +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/api/misc.py +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/api/schedule.py +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/api/standings.py +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/api/teams.py +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/config.py +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/data/seasonal_information_manifest.json +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/data/team_stat_ids.json +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/data/teams_20232024.json +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/http_client.py +0 -0
- {nhl_api_py-2.2.6 → nhl_api_py-2.3.1}/nhlpy/nhl_client.py +0 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: nhl-api-py
|
|
3
|
+
Version: 2.3.1
|
|
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
|
+
Home-page: https://github.com/coreyjs/nhl-api-py
|
|
6
|
+
License: GPL-3.0-or-later
|
|
7
|
+
Keywords: nhl,api,wrapper,hockey,sports
|
|
8
|
+
Author: Corey Schaf
|
|
9
|
+
Author-email: cschaf@gmail.com
|
|
10
|
+
Requires-Python: >=3.9,<4.0
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Dist: httpx
|
|
24
|
+
Project-URL: Repository, https://github.com/coreyjs/nhl-api-py
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
[](https://badge.fury.io/py/nhl-api-py)
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
# NHL-API-PY
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## About
|
|
34
|
+
|
|
35
|
+
NHL-api-py is a Python package that provides a simple wrapper around the
|
|
36
|
+
NHL API, allowing you to easily access and retrieve NHL data in your Python
|
|
37
|
+
applications.
|
|
38
|
+
|
|
39
|
+
Note: This is ~~very early~~ maturing, I created this to help me with some machine learning
|
|
40
|
+
projects around the NHL and the NHL data sets. Special thanks to https://github.com/erunion/sport-api-specifications/tree/master/nhl and https://gitlab.com/dword4/nhlapi/-/blob/master/stats-api.md.
|
|
41
|
+
|
|
42
|
+
### Developer Note: This is being updated with the new, also undocumented, NHL API.
|
|
43
|
+
|
|
44
|
+
More endpoints will be flushed out and completed as they
|
|
45
|
+
are discovered. If you find any, open a ticket or post in the discussions tab. I would love to hear more.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
## Contact
|
|
50
|
+
Im available on [Bluesky](https://bsky.app/profile/coreyjs.dev) for any questions or just general chats about enhancements.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Usage
|
|
57
|
+
```python
|
|
58
|
+
from nhlpy import NHLClient
|
|
59
|
+
|
|
60
|
+
client = NHLClient()
|
|
61
|
+
# OR
|
|
62
|
+
client = NHLClient(verbose=True) # a tad more logging such as the URL being called
|
|
63
|
+
```
|
|
64
|
+
---
|
|
65
|
+
## Stats with QueryBuilder
|
|
66
|
+
|
|
67
|
+
The skater stats endpoint can be accessed using the new query builder. It should make
|
|
68
|
+
creating and understanding the queries a bit easier. Filters are being added as I go, and will match up
|
|
69
|
+
to what the NHL API will allow.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
### Sorting
|
|
74
|
+
The sorting is a list of dictionaries similar to below. You can supply your own, otherwise it will
|
|
75
|
+
default to the default sort properties that the stat dashboard uses. All sorting defaults are found
|
|
76
|
+
in the `nhl-api-py/nhlpy/api/query/sorting/sorting_options.py` file.
|
|
77
|
+
|
|
78
|
+
<details>
|
|
79
|
+
<summary>Default Sorting</summary>
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
skater_summary_default_sorting = [
|
|
83
|
+
{"property": "points", "direction": "DESC"},
|
|
84
|
+
{"property": "gamesPlayed", "direction": "ASC"},
|
|
85
|
+
{"property": "playerId", "direction": "ASC"},
|
|
86
|
+
]
|
|
87
|
+
```
|
|
88
|
+
</details>
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### Report Types
|
|
93
|
+
The following report types are available. These are used to build the request url. So `/summary`, `/bios`, etc.
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
summary
|
|
97
|
+
bios
|
|
98
|
+
faceoffpercentages
|
|
99
|
+
faceoffwins
|
|
100
|
+
goalsForAgainst
|
|
101
|
+
realtime
|
|
102
|
+
penalties
|
|
103
|
+
penaltykill
|
|
104
|
+
penaltyShots
|
|
105
|
+
powerplay
|
|
106
|
+
puckPossessions
|
|
107
|
+
summaryshooting
|
|
108
|
+
percentages
|
|
109
|
+
scoringRates
|
|
110
|
+
scoringpergame
|
|
111
|
+
shootout
|
|
112
|
+
shottype
|
|
113
|
+
timeonice
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Available Filters
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from nhlpy.api.query.filters.franchise import FranchiseQuery
|
|
120
|
+
from nhlpy.api.query.filters.shoot_catch import ShootCatchesQuery
|
|
121
|
+
from nhlpy.api.query.filters.draft import DraftQuery
|
|
122
|
+
from nhlpy.api.query.filters.season import SeasonQuery
|
|
123
|
+
from nhlpy.api.query.filters.game_type import GameTypeQuery
|
|
124
|
+
from nhlpy.api.query.filters.position import PositionQuery, PositionTypes
|
|
125
|
+
from nhlpy.api.query.filters.status import StatusQuery
|
|
126
|
+
from nhlpy.api.query.filters.opponent import OpponentQuery
|
|
127
|
+
from nhlpy.api.query.filters.home_road import HomeRoadQuery
|
|
128
|
+
from nhlpy.api.query.filters.experience import ExperienceQuery
|
|
129
|
+
from nhlpy.api.query.filters.decision import DecisionQuery
|
|
130
|
+
|
|
131
|
+
filters = [
|
|
132
|
+
GameTypeQuery(game_type="2"),
|
|
133
|
+
DraftQuery(year="2020", draft_round="2"),
|
|
134
|
+
SeasonQuery(season_start="20202021", season_end="20232024"),
|
|
135
|
+
PositionQuery(position=PositionTypes.ALL_FORWARDS),
|
|
136
|
+
ShootCatchesQuery(shoot_catch="L"),
|
|
137
|
+
HomeRoadQuery(home_road="H"),
|
|
138
|
+
FranchiseQuery(franchise_id="1"),
|
|
139
|
+
StatusQuery(is_active=True) #for active players OR for HOF players StatusQuery(is_hall_of_fame=True),
|
|
140
|
+
OpponentQuery(opponent_franchise_id="2"),
|
|
141
|
+
ExperienceQuery(is_rookie=True), # for rookies || ExperienceQuery(is_rookie=False) #for veteran
|
|
142
|
+
DecisionQuery(decision="W") # OR DecisionQuery(decision="L") OR DecisionQuery(decision="O")
|
|
143
|
+
]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
### Example
|
|
148
|
+
```python
|
|
149
|
+
from nhlpy.api.query.builder import QueryBuilder, QueryContext
|
|
150
|
+
from nhlpy.nhl_client import NHLClient
|
|
151
|
+
from nhlpy.api.query.filters.draft import DraftQuery
|
|
152
|
+
from nhlpy.api.query.filters.season import SeasonQuery
|
|
153
|
+
from nhlpy.api.query.filters.game_type import GameTypeQuery
|
|
154
|
+
from nhlpy.api.query.filters.position import PositionQuery, PositionTypes
|
|
155
|
+
|
|
156
|
+
client = NHLClient(verbose=True)
|
|
157
|
+
|
|
158
|
+
filters = [
|
|
159
|
+
GameTypeQuery(game_type="2"),
|
|
160
|
+
DraftQuery(year="2020", draft_round="2"),
|
|
161
|
+
SeasonQuery(season_start="20202021", season_end="20232024"),
|
|
162
|
+
PositionQuery(position=PositionTypes.ALL_FORWARDS)
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
query_builder = QueryBuilder()
|
|
166
|
+
query_context: QueryContext = query_builder.build(filters=filters)
|
|
167
|
+
|
|
168
|
+
data = client.stats.skater_stats_with_query_context(
|
|
169
|
+
report_type='summary',
|
|
170
|
+
query_context=query_context,
|
|
171
|
+
aggregate=True
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Granular Filtering
|
|
176
|
+
Each API request uses an additional query parameter called `factCayenneExp`. This defaults to `gamesPlayed>=1`
|
|
177
|
+
but can be overridden by setting the `fact_query` parameter in the `QueryContextObject` object. These can
|
|
178
|
+
be combined together with `and` to create a more complex query. It supports `>`, `<`, `>=`, `<=`. For example: `shootingPct>=0.01 and timeOnIcePerGame>=60 and faceoffWinPct>=0.01 and shots>=1`
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
This should support the following filters:
|
|
182
|
+
|
|
183
|
+
- `gamesPlayed`
|
|
184
|
+
- `points`
|
|
185
|
+
- `goals`
|
|
186
|
+
- `pointsPerGame`
|
|
187
|
+
- `penaltyMinutes`
|
|
188
|
+
- `plusMinus`
|
|
189
|
+
- `ppGoals` # power play goals
|
|
190
|
+
- `evGoals` # even strength goals
|
|
191
|
+
- `pointsPerGame`
|
|
192
|
+
- `penaltyMinutes`
|
|
193
|
+
- `evPoints` # even strength points
|
|
194
|
+
- `ppPoints` # power play points
|
|
195
|
+
- `gameWinningGoals`
|
|
196
|
+
- `otGoals`
|
|
197
|
+
- `shPoints` # short handed points
|
|
198
|
+
- `shGoals` # short handed goals
|
|
199
|
+
- `shootingPct`
|
|
200
|
+
- `timeOnIcePerGame`
|
|
201
|
+
- `faceoffWinPct`
|
|
202
|
+
- `shots`
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
.....
|
|
206
|
+
query_builder = QueryBuilder()
|
|
207
|
+
query_context: QueryContext = query_builder.build(filters=filters)
|
|
208
|
+
|
|
209
|
+
query_context.fact_query = "gamesPlayed>=1 and goals>=10" # defaults to gamesPlayed>=1
|
|
210
|
+
|
|
211
|
+
data = client.stats.skater_stats_with_query_context(
|
|
212
|
+
report_type='summary',
|
|
213
|
+
query_context=query_context,
|
|
214
|
+
aggregate=True
|
|
215
|
+
)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
### Invalid Query / Errors
|
|
220
|
+
|
|
221
|
+
The `QueryContext` object will hold the result of the built query with the supplied queries.
|
|
222
|
+
In the event of an invalid query (bad data, wrong option, etc), the `QueryContext` object will
|
|
223
|
+
hold all the errors that were encountered during the build process. This should help in debugging.
|
|
224
|
+
|
|
225
|
+
You can quickly check the `QueryContext` object for errors by calling `query_context.is_valid()`. Any "invalid" filters
|
|
226
|
+
will be removed from the output query, but anything that is still valid will be included.
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
...
|
|
230
|
+
query_context: QueryContext = query_builder.build(filters=filters)
|
|
231
|
+
query_context.is_valid() # False if any of the filters fails its validation check
|
|
232
|
+
query_context.errors
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Additional Stats Endpoints (In development)
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
|
|
241
|
+
client.stats.club_stats_season(team_abbr="BUF") # kinda weird endpoint.
|
|
242
|
+
|
|
243
|
+
client.stats.player_career_stats(player_id="8478402")
|
|
244
|
+
|
|
245
|
+
# Team Summary Stats.
|
|
246
|
+
# These have lots of available parameters. You can also tap into the apache cayenne expressions to build custom
|
|
247
|
+
# queries, if you have that knowledge.
|
|
248
|
+
client.stats.team_summary(start_season="20202021", end_season="20212022", game_type_id=2)
|
|
249
|
+
client.stats.team_summary(start_season="20202021", end_season="20212022")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# Skater Summary Stats.
|
|
253
|
+
# Queries for skaters for year ranges, filterable down by franchise.
|
|
254
|
+
client.stats.skater_stats_summary(start_season="20232024", end_season="20232024")
|
|
255
|
+
client.stats.skater_stats_summary(franchise_id=10, start_season="20232024", end_season="20232024")
|
|
256
|
+
```
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
## Schedule Endpoints
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
client.schedule.get_schedule(date="2021-01-13")
|
|
264
|
+
client.schedule.get_schedule()
|
|
265
|
+
|
|
266
|
+
client.schedule.get_schedule_by_team_by_month(team_abbr="BUF")
|
|
267
|
+
client.schedule.get_schedule_by_team_by_month(team_abbr="BUF", month="2021-01")
|
|
268
|
+
|
|
269
|
+
client.schedule.get_schedule_by_team_by_week(team_abbr="BUF")
|
|
270
|
+
client.schedule.get_schedule_by_team_by_week(team_abbr="BUF", date="2024-01-01")
|
|
271
|
+
|
|
272
|
+
client.schedule.get_season_schedule(team_abbr="BUF", season="20212022")
|
|
273
|
+
|
|
274
|
+
client.schedule.schedule_calendar(date="2023-11-23")
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Standings Endpoints
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
client.standings.get_standings()
|
|
283
|
+
client.standings.get_standings(date="2021-01-13")
|
|
284
|
+
client.standings.get_standings(season="202222023")
|
|
285
|
+
|
|
286
|
+
# standings manifest. This returns a ton of information for every season ever it seems like
|
|
287
|
+
# This calls the API for this info, I also cache this in /data/seasonal_information_manifest.json
|
|
288
|
+
# for less API calls since this only changes yearly.
|
|
289
|
+
client.standings.season_standing_manifest()
|
|
290
|
+
```
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Teams Endpoints
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
client.teams.teams_info() # returns id + abbrevation + name of all teams
|
|
297
|
+
|
|
298
|
+
client.teams.team_stats_summary(lang="en") # I honestly dont know. This is missing teams and has teams long abandoned.
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Game Center
|
|
304
|
+
```python
|
|
305
|
+
client.game_center.boxscore(game_id="2023020280")
|
|
306
|
+
|
|
307
|
+
client.game_center.play_by_play(game_id="2023020280")
|
|
308
|
+
|
|
309
|
+
client.game_center.landing(game_id="2023020280")
|
|
310
|
+
|
|
311
|
+
client.game_center.score_now()
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Misc Endpoints
|
|
317
|
+
```python
|
|
318
|
+
client.misc.glossary()
|
|
319
|
+
|
|
320
|
+
client.misc.config()
|
|
321
|
+
|
|
322
|
+
client.misc.countries()
|
|
323
|
+
|
|
324
|
+
client.misc.season_specific_rules_and_info()
|
|
325
|
+
|
|
326
|
+
client.misc.draft_year_and_rounds()
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
## Insomnia Rest Client Export
|
|
331
|
+
|
|
332
|
+
[Insomnia Rest Client](https://insomnia.rest) is a great tool for testing
|
|
333
|
+
|
|
334
|
+
nhl_api-{ver}.json in the root folder is an export of the endpoints I have
|
|
335
|
+
been working through using the Insomnia Rest Client. You can import this directly
|
|
336
|
+
into the client and use it to test the endpoints. I will be updating this as I go
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
- - -
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
## Developers
|
|
343
|
+
|
|
344
|
+
1) Install [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer)
|
|
345
|
+
|
|
346
|
+
`curl -sSL https://install.python-poetry.org | python3 -`
|
|
347
|
+
|
|
348
|
+
or using pipx
|
|
349
|
+
|
|
350
|
+
`pipx install poetry`
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
2) `poetry install --with dev`
|
|
354
|
+
|
|
355
|
+
3) `poetry shell`
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
### Build Pipeline
|
|
359
|
+
The build pipeline will run `black`, `ruff`, and `pytest`. Please make sure these are passing before submitting a PR.
|
|
360
|
+
|
|
361
|
+
```python
|
|
362
|
+
|
|
363
|
+
$ poetry shell
|
|
364
|
+
|
|
365
|
+
# You can then run the following
|
|
366
|
+
$ pytest
|
|
367
|
+
$ ruff .
|
|
368
|
+
$ black .
|
|
369
|
+
|
|
370
|
+
```
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
[](https://badge.fury.io/py/nhl-api-py)
|
|
2
|
+

|
|
3
|
+
|
|
4
|
+
# NHL-API-PY
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## About
|
|
8
|
+
|
|
9
|
+
NHL-api-py is a Python package that provides a simple wrapper around the
|
|
10
|
+
NHL API, allowing you to easily access and retrieve NHL data in your Python
|
|
11
|
+
applications.
|
|
12
|
+
|
|
13
|
+
Note: This is ~~very early~~ maturing, I created this to help me with some machine learning
|
|
14
|
+
projects around the NHL and the NHL data sets. Special thanks to https://github.com/erunion/sport-api-specifications/tree/master/nhl and https://gitlab.com/dword4/nhlapi/-/blob/master/stats-api.md.
|
|
15
|
+
|
|
16
|
+
### Developer Note: This is being updated with the new, also undocumented, NHL API.
|
|
17
|
+
|
|
18
|
+
More endpoints will be flushed out and completed as they
|
|
19
|
+
are discovered. If you find any, open a ticket or post in the discussions tab. I would love to hear more.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
## Contact
|
|
24
|
+
Im available on [Bluesky](https://bsky.app/profile/coreyjs.dev) for any questions or just general chats about enhancements.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Usage
|
|
31
|
+
```python
|
|
32
|
+
from nhlpy import NHLClient
|
|
33
|
+
|
|
34
|
+
client = NHLClient()
|
|
35
|
+
# OR
|
|
36
|
+
client = NHLClient(verbose=True) # a tad more logging such as the URL being called
|
|
37
|
+
```
|
|
38
|
+
---
|
|
39
|
+
## Stats with QueryBuilder
|
|
40
|
+
|
|
41
|
+
The skater stats endpoint can be accessed using the new query builder. It should make
|
|
42
|
+
creating and understanding the queries a bit easier. Filters are being added as I go, and will match up
|
|
43
|
+
to what the NHL API will allow.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### Sorting
|
|
48
|
+
The sorting is a list of dictionaries similar to below. You can supply your own, otherwise it will
|
|
49
|
+
default to the default sort properties that the stat dashboard uses. All sorting defaults are found
|
|
50
|
+
in the `nhl-api-py/nhlpy/api/query/sorting/sorting_options.py` file.
|
|
51
|
+
|
|
52
|
+
<details>
|
|
53
|
+
<summary>Default Sorting</summary>
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
skater_summary_default_sorting = [
|
|
57
|
+
{"property": "points", "direction": "DESC"},
|
|
58
|
+
{"property": "gamesPlayed", "direction": "ASC"},
|
|
59
|
+
{"property": "playerId", "direction": "ASC"},
|
|
60
|
+
]
|
|
61
|
+
```
|
|
62
|
+
</details>
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### Report Types
|
|
67
|
+
The following report types are available. These are used to build the request url. So `/summary`, `/bios`, etc.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
summary
|
|
71
|
+
bios
|
|
72
|
+
faceoffpercentages
|
|
73
|
+
faceoffwins
|
|
74
|
+
goalsForAgainst
|
|
75
|
+
realtime
|
|
76
|
+
penalties
|
|
77
|
+
penaltykill
|
|
78
|
+
penaltyShots
|
|
79
|
+
powerplay
|
|
80
|
+
puckPossessions
|
|
81
|
+
summaryshooting
|
|
82
|
+
percentages
|
|
83
|
+
scoringRates
|
|
84
|
+
scoringpergame
|
|
85
|
+
shootout
|
|
86
|
+
shottype
|
|
87
|
+
timeonice
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Available Filters
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from nhlpy.api.query.filters.franchise import FranchiseQuery
|
|
94
|
+
from nhlpy.api.query.filters.shoot_catch import ShootCatchesQuery
|
|
95
|
+
from nhlpy.api.query.filters.draft import DraftQuery
|
|
96
|
+
from nhlpy.api.query.filters.season import SeasonQuery
|
|
97
|
+
from nhlpy.api.query.filters.game_type import GameTypeQuery
|
|
98
|
+
from nhlpy.api.query.filters.position import PositionQuery, PositionTypes
|
|
99
|
+
from nhlpy.api.query.filters.status import StatusQuery
|
|
100
|
+
from nhlpy.api.query.filters.opponent import OpponentQuery
|
|
101
|
+
from nhlpy.api.query.filters.home_road import HomeRoadQuery
|
|
102
|
+
from nhlpy.api.query.filters.experience import ExperienceQuery
|
|
103
|
+
from nhlpy.api.query.filters.decision import DecisionQuery
|
|
104
|
+
|
|
105
|
+
filters = [
|
|
106
|
+
GameTypeQuery(game_type="2"),
|
|
107
|
+
DraftQuery(year="2020", draft_round="2"),
|
|
108
|
+
SeasonQuery(season_start="20202021", season_end="20232024"),
|
|
109
|
+
PositionQuery(position=PositionTypes.ALL_FORWARDS),
|
|
110
|
+
ShootCatchesQuery(shoot_catch="L"),
|
|
111
|
+
HomeRoadQuery(home_road="H"),
|
|
112
|
+
FranchiseQuery(franchise_id="1"),
|
|
113
|
+
StatusQuery(is_active=True) #for active players OR for HOF players StatusQuery(is_hall_of_fame=True),
|
|
114
|
+
OpponentQuery(opponent_franchise_id="2"),
|
|
115
|
+
ExperienceQuery(is_rookie=True), # for rookies || ExperienceQuery(is_rookie=False) #for veteran
|
|
116
|
+
DecisionQuery(decision="W") # OR DecisionQuery(decision="L") OR DecisionQuery(decision="O")
|
|
117
|
+
]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
### Example
|
|
122
|
+
```python
|
|
123
|
+
from nhlpy.api.query.builder import QueryBuilder, QueryContext
|
|
124
|
+
from nhlpy.nhl_client import NHLClient
|
|
125
|
+
from nhlpy.api.query.filters.draft import DraftQuery
|
|
126
|
+
from nhlpy.api.query.filters.season import SeasonQuery
|
|
127
|
+
from nhlpy.api.query.filters.game_type import GameTypeQuery
|
|
128
|
+
from nhlpy.api.query.filters.position import PositionQuery, PositionTypes
|
|
129
|
+
|
|
130
|
+
client = NHLClient(verbose=True)
|
|
131
|
+
|
|
132
|
+
filters = [
|
|
133
|
+
GameTypeQuery(game_type="2"),
|
|
134
|
+
DraftQuery(year="2020", draft_round="2"),
|
|
135
|
+
SeasonQuery(season_start="20202021", season_end="20232024"),
|
|
136
|
+
PositionQuery(position=PositionTypes.ALL_FORWARDS)
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
query_builder = QueryBuilder()
|
|
140
|
+
query_context: QueryContext = query_builder.build(filters=filters)
|
|
141
|
+
|
|
142
|
+
data = client.stats.skater_stats_with_query_context(
|
|
143
|
+
report_type='summary',
|
|
144
|
+
query_context=query_context,
|
|
145
|
+
aggregate=True
|
|
146
|
+
)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Granular Filtering
|
|
150
|
+
Each API request uses an additional query parameter called `factCayenneExp`. This defaults to `gamesPlayed>=1`
|
|
151
|
+
but can be overridden by setting the `fact_query` parameter in the `QueryContextObject` object. These can
|
|
152
|
+
be combined together with `and` to create a more complex query. It supports `>`, `<`, `>=`, `<=`. For example: `shootingPct>=0.01 and timeOnIcePerGame>=60 and faceoffWinPct>=0.01 and shots>=1`
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
This should support the following filters:
|
|
156
|
+
|
|
157
|
+
- `gamesPlayed`
|
|
158
|
+
- `points`
|
|
159
|
+
- `goals`
|
|
160
|
+
- `pointsPerGame`
|
|
161
|
+
- `penaltyMinutes`
|
|
162
|
+
- `plusMinus`
|
|
163
|
+
- `ppGoals` # power play goals
|
|
164
|
+
- `evGoals` # even strength goals
|
|
165
|
+
- `pointsPerGame`
|
|
166
|
+
- `penaltyMinutes`
|
|
167
|
+
- `evPoints` # even strength points
|
|
168
|
+
- `ppPoints` # power play points
|
|
169
|
+
- `gameWinningGoals`
|
|
170
|
+
- `otGoals`
|
|
171
|
+
- `shPoints` # short handed points
|
|
172
|
+
- `shGoals` # short handed goals
|
|
173
|
+
- `shootingPct`
|
|
174
|
+
- `timeOnIcePerGame`
|
|
175
|
+
- `faceoffWinPct`
|
|
176
|
+
- `shots`
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
.....
|
|
180
|
+
query_builder = QueryBuilder()
|
|
181
|
+
query_context: QueryContext = query_builder.build(filters=filters)
|
|
182
|
+
|
|
183
|
+
query_context.fact_query = "gamesPlayed>=1 and goals>=10" # defaults to gamesPlayed>=1
|
|
184
|
+
|
|
185
|
+
data = client.stats.skater_stats_with_query_context(
|
|
186
|
+
report_type='summary',
|
|
187
|
+
query_context=query_context,
|
|
188
|
+
aggregate=True
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
### Invalid Query / Errors
|
|
194
|
+
|
|
195
|
+
The `QueryContext` object will hold the result of the built query with the supplied queries.
|
|
196
|
+
In the event of an invalid query (bad data, wrong option, etc), the `QueryContext` object will
|
|
197
|
+
hold all the errors that were encountered during the build process. This should help in debugging.
|
|
198
|
+
|
|
199
|
+
You can quickly check the `QueryContext` object for errors by calling `query_context.is_valid()`. Any "invalid" filters
|
|
200
|
+
will be removed from the output query, but anything that is still valid will be included.
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
...
|
|
204
|
+
query_context: QueryContext = query_builder.build(filters=filters)
|
|
205
|
+
query_context.is_valid() # False if any of the filters fails its validation check
|
|
206
|
+
query_context.errors
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Additional Stats Endpoints (In development)
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
|
|
215
|
+
client.stats.club_stats_season(team_abbr="BUF") # kinda weird endpoint.
|
|
216
|
+
|
|
217
|
+
client.stats.player_career_stats(player_id="8478402")
|
|
218
|
+
|
|
219
|
+
# Team Summary Stats.
|
|
220
|
+
# These have lots of available parameters. You can also tap into the apache cayenne expressions to build custom
|
|
221
|
+
# queries, if you have that knowledge.
|
|
222
|
+
client.stats.team_summary(start_season="20202021", end_season="20212022", game_type_id=2)
|
|
223
|
+
client.stats.team_summary(start_season="20202021", end_season="20212022")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# Skater Summary Stats.
|
|
227
|
+
# Queries for skaters for year ranges, filterable down by franchise.
|
|
228
|
+
client.stats.skater_stats_summary(start_season="20232024", end_season="20232024")
|
|
229
|
+
client.stats.skater_stats_summary(franchise_id=10, start_season="20232024", end_season="20232024")
|
|
230
|
+
```
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
## Schedule Endpoints
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
client.schedule.get_schedule(date="2021-01-13")
|
|
238
|
+
client.schedule.get_schedule()
|
|
239
|
+
|
|
240
|
+
client.schedule.get_schedule_by_team_by_month(team_abbr="BUF")
|
|
241
|
+
client.schedule.get_schedule_by_team_by_month(team_abbr="BUF", month="2021-01")
|
|
242
|
+
|
|
243
|
+
client.schedule.get_schedule_by_team_by_week(team_abbr="BUF")
|
|
244
|
+
client.schedule.get_schedule_by_team_by_week(team_abbr="BUF", date="2024-01-01")
|
|
245
|
+
|
|
246
|
+
client.schedule.get_season_schedule(team_abbr="BUF", season="20212022")
|
|
247
|
+
|
|
248
|
+
client.schedule.schedule_calendar(date="2023-11-23")
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Standings Endpoints
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
client.standings.get_standings()
|
|
257
|
+
client.standings.get_standings(date="2021-01-13")
|
|
258
|
+
client.standings.get_standings(season="202222023")
|
|
259
|
+
|
|
260
|
+
# standings manifest. This returns a ton of information for every season ever it seems like
|
|
261
|
+
# This calls the API for this info, I also cache this in /data/seasonal_information_manifest.json
|
|
262
|
+
# for less API calls since this only changes yearly.
|
|
263
|
+
client.standings.season_standing_manifest()
|
|
264
|
+
```
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Teams Endpoints
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
client.teams.teams_info() # returns id + abbrevation + name of all teams
|
|
271
|
+
|
|
272
|
+
client.teams.team_stats_summary(lang="en") # I honestly dont know. This is missing teams and has teams long abandoned.
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Game Center
|
|
278
|
+
```python
|
|
279
|
+
client.game_center.boxscore(game_id="2023020280")
|
|
280
|
+
|
|
281
|
+
client.game_center.play_by_play(game_id="2023020280")
|
|
282
|
+
|
|
283
|
+
client.game_center.landing(game_id="2023020280")
|
|
284
|
+
|
|
285
|
+
client.game_center.score_now()
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Misc Endpoints
|
|
291
|
+
```python
|
|
292
|
+
client.misc.glossary()
|
|
293
|
+
|
|
294
|
+
client.misc.config()
|
|
295
|
+
|
|
296
|
+
client.misc.countries()
|
|
297
|
+
|
|
298
|
+
client.misc.season_specific_rules_and_info()
|
|
299
|
+
|
|
300
|
+
client.misc.draft_year_and_rounds()
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
## Insomnia Rest Client Export
|
|
305
|
+
|
|
306
|
+
[Insomnia Rest Client](https://insomnia.rest) is a great tool for testing
|
|
307
|
+
|
|
308
|
+
nhl_api-{ver}.json in the root folder is an export of the endpoints I have
|
|
309
|
+
been working through using the Insomnia Rest Client. You can import this directly
|
|
310
|
+
into the client and use it to test the endpoints. I will be updating this as I go
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
- - -
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
## Developers
|
|
317
|
+
|
|
318
|
+
1) Install [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer)
|
|
319
|
+
|
|
320
|
+
`curl -sSL https://install.python-poetry.org | python3 -`
|
|
321
|
+
|
|
322
|
+
or using pipx
|
|
323
|
+
|
|
324
|
+
`pipx install poetry`
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
2) `poetry install --with dev`
|
|
328
|
+
|
|
329
|
+
3) `poetry shell`
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
### Build Pipeline
|
|
333
|
+
The build pipeline will run `black`, `ruff`, and `pytest`. Please make sure these are passing before submitting a PR.
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
|
|
337
|
+
$ poetry shell
|
|
338
|
+
|
|
339
|
+
# You can then run the following
|
|
340
|
+
$ pytest
|
|
341
|
+
$ ruff .
|
|
342
|
+
$ black .
|
|
343
|
+
|
|
344
|
+
```
|