1
1
import requests
2
- from typing import Dict , List , Any , Union
2
+ from typing import Dict , List , Union
3
3
from cachecontrol import CacheControl
4
4
from fuzzywuzzy import fuzz , process
5
5
import pandas as pd
@@ -12,6 +12,7 @@ class AmericanSoccerAnalysis:
12
12
API_VERSION = "v1"
13
13
BASE_URL = f"https://app.americansocceranalysis.com/api/{ API_VERSION } /"
14
14
LEAGUES = ["nwsl" , "mls" , "uslc" , "usl1" , "nasl" ]
15
+ MAX_API_LIMIT = 1000
15
16
16
17
def __init__ (self ) -> None :
17
18
"""Class constructor"""
@@ -103,27 +104,28 @@ def _convert_names_to_ids(
103
104
ids .append (self ._convert_name_to_id (type , n ))
104
105
return ids
105
106
106
- def _check_leagues (self , leagues : Union [str , List [str ]]):
107
+ def _check_leagues (self , leagues : Union [str , List [str ]]) -> None :
107
108
"""Validates the leagues parameter
108
109
109
110
:param leagues: league abbreviation or list of league abbreviations
110
111
"""
111
112
if isinstance (leagues , list ):
112
- if not all (l in leagues for l in self .LEAGUES ):
113
- print (
114
- f"Leagues are limited only to the following options: { self .LEAGUES .join (',' )} ."
115
- )
116
- exit ()
113
+ for l in leagues :
114
+ if l not in self .LEAGUES :
115
+ print (
116
+ f"Leagues are limited only to the following options: { self .LEAGUES } ."
117
+ )
118
+ raise SystemExit (1 )
117
119
else :
118
120
if leagues not in self .LEAGUES :
119
121
print (
120
- f"Leagues are limited only to the following options: { self .LEAGUES . join ( ',' ) } ."
122
+ f"Leagues are limited only to the following options: { self .LEAGUES } ."
121
123
)
122
- exit ( )
124
+ raise SystemExit ( 1 )
123
125
124
126
def _check_ids_names (
125
127
self , ids : Union [str , List [str ]], names : Union [str , List [str ]]
126
- ):
128
+ ) -> None :
127
129
"""Makes sure only ids or names are passed to a function and verifies
128
130
they are the right data type.
129
131
@@ -132,17 +134,17 @@ def _check_ids_names(
132
134
"""
133
135
if ids and names :
134
136
print ("Please specify only IDs or names, not both." )
135
- exit ( )
137
+ raise SystemExit ( 1 )
136
138
137
139
if ids :
138
140
if not isinstance (ids , str ) and not isinstance (ids , list ):
139
141
print ("IDs must be passed as a string or list of strings." )
140
- exit ( )
142
+ raise SystemExit ( 1 )
141
143
142
144
if names :
143
145
if not isinstance (names , str ) and not isinstance (names , list ):
144
146
print ("Names must be passed as a string or list of names." )
145
- exit ( )
147
+ raise SystemExit ( 1 )
146
148
147
149
def _filter_entity (
148
150
self ,
@@ -151,7 +153,7 @@ def _filter_entity(
151
153
leagues : Union [str , List [str ]],
152
154
ids : Union [str , List [str ]] = None ,
153
155
names : Union [str , List [str ]] = None ,
154
- ) -> List [ Dict [ str , Any ]] :
156
+ ) -> pd . DataFrame :
155
157
"""Filters a dataframe based on the arguments given.
156
158
157
159
:param entity_all: a dataframe containing the complete set of data
@@ -180,20 +182,53 @@ def _filter_entity(
180
182
if converted_ids :
181
183
entity = entity [entity [f"{ entity_type } _id" ].isin (converted_ids )]
182
184
183
- return entity .to_json (orient = "records" )
185
+ return entity
186
+
187
+ def _execute_query (self , url : str , params : Dict [str ,List [str ]]) -> pd .DataFrame :
188
+ """Executes a query while handling the max number of responses from the API
189
+
190
+ :param url: the API endpoint to call
191
+ :param params: URL query strings
192
+ :returns: Dataframe
193
+ """
194
+ temp_response = self ._single_request (url , params )
195
+ response = temp_response
196
+
197
+ if (isinstance (response , pd .DataFrame )):
198
+ offset = self .MAX_API_LIMIT
199
+
200
+ while (len (temp_response ) == self .MAX_API_LIMIT ):
201
+ params ["offset" ] = offset
202
+ temp_response = self ._execute_query (url , params )
203
+ response = response .append (temp_response )
204
+ offset = offset + self .MAX_API_LIMIT
205
+
206
+ return response
207
+
208
+ def _single_request (self , url : str , params : Dict [str , List [str ]]) -> pd .DataFrame :
209
+ """Handles single call to the API
210
+
211
+ :param url: the API endpoint to call
212
+ :param params: URL query strings
213
+ :returns: Dataframe
214
+ """
215
+ response = self .session .get (url = url , params = params )
216
+ response .raise_for_status ()
217
+ resp_df = pd .read_json (json .dumps (response .json ()))
218
+ return resp_df
184
219
185
220
def get_stadia (
186
221
self ,
187
222
leagues : Union [str , List [str ]],
188
223
ids : Union [str , List [str ]] = None ,
189
224
names : Union [str , List [str ]] = None ,
190
- ) -> List [ Dict [ str , Any ]] :
225
+ ) -> pd . DataFrame :
191
226
"""Get information associated with stadia
192
227
193
228
:param leagues: league abbreviation or a list of league abbreviations
194
229
:param ids: a single stadium id or a list of stadia ids (optional)
195
230
:param names: a single stadium name or a list of stadia names (optional)
196
- :returns: list of dictionaries
231
+ :returns: Dataframe
197
232
"""
198
233
stadia = self ._filter_entity (self .stadia , "stadium" , leagues , ids , names )
199
234
return stadia
@@ -203,13 +238,13 @@ def get_referees(
203
238
leagues : Union [str , List [str ]],
204
239
ids : Union [str , List [str ]] = None ,
205
240
names : Union [str , List [str ]] = None ,
206
- ) -> List [ Dict [ str , Any ]] :
241
+ ) -> pd . DataFrame :
207
242
"""Get information associated with referees
208
243
209
244
:param leagues: league abbreviation or a list of league abbreviations
210
245
:param ids: a single referee id or a list of referee ids (optional)
211
246
:param names: a single referee name or a list of referee names (optional)
212
- :returns: list of dictionaries
247
+ :returns: Dataframe
213
248
"""
214
249
referees = self ._filter_entity (self .referees , "referee" , leagues , ids , names )
215
250
return referees
@@ -219,13 +254,13 @@ def get_managers(
219
254
leagues : Union [str , List [str ]],
220
255
ids : Union [str , List [str ]] = None ,
221
256
names : Union [str , List [str ]] = None ,
222
- ) -> List [ Dict [ str , Any ]] :
257
+ ) -> pd . DataFrame :
223
258
"""Get information associated with managers
224
259
225
260
:param leagues: league abbreviation or a list of league abbreviations
226
261
:param ids: a single referee id or a list of referee ids (optional)
227
262
:param names: a single referee name or a list of referee names (optional)
228
- :returns: list of dictionaries
263
+ :returns: Dataframe
229
264
"""
230
265
managers = self ._filter_entity (self .managers , "manager" , leagues , ids , names )
231
266
return managers
@@ -235,13 +270,13 @@ def get_teams(
235
270
leagues : Union [str , List [str ]],
236
271
ids : Union [str , List [str ]] = None ,
237
272
names : Union [str , List [str ]] = None ,
238
- ) -> List [ Dict [ str , Any ]] :
273
+ ) -> pd . DataFrame :
239
274
"""Get information associated with teams
240
275
241
276
:param leagues: league abbreviation or a list of league abbreviations
242
277
:param ids: a single team id or a list of team ids (optional)
243
278
:param names: a single team name or a list of team names (optional)
244
- :returns: list of dictionaries
279
+ :returns: Dataframe
245
280
"""
246
281
teams = self ._filter_entity (self .teams , "team" , leagues , ids , names )
247
282
return teams
@@ -251,13 +286,63 @@ def get_players(
251
286
leagues : Union [str , List [str ]],
252
287
ids : Union [str , List [str ]] = None ,
253
288
names : Union [str , List [str ]] = None ,
254
- ) -> List [ Dict [ str , Any ]] :
289
+ ) -> pd . DataFrame :
255
290
"""Get information associated with players
256
291
257
292
:param league: league abbreviation or a list of league abbreviations
258
293
:param ids: a single player id or a list of player ids (optional)
259
294
:param names: a single player name or a list of player names (optional)
260
- :returns: list of dictionaries
295
+ :returns: Dataframe
261
296
"""
262
297
players = self ._filter_entity (self .players , "player" , leagues , ids , names )
263
298
return players
299
+
300
+ def get_games (
301
+ self ,
302
+ leagues : Union [str , List [str ]],
303
+ game_ids : Union [str , List [str ]] = None ,
304
+ team_ids : Union [str , List [str ]] = None ,
305
+ team_names : Union [str , List [str ]] = None ,
306
+ seasons : Union [str , List [str ]] = None ,
307
+ stages : Union [str , List [str ]] = None ,
308
+ ) -> pd .DataFrame :
309
+ """Get information related to games
310
+
311
+ :param leagues: league abbreviation or a list of league abbreviations
312
+ :param game_ids: a single game id or a list of game ids
313
+ :param team_ids: a single team id or a list of team ids
314
+ :param team_names: a single team name or a list of team names
315
+ :param seasons: a single year of a league season or a list of years
316
+ :param stages: a single stage of competition in which a game took place or list of stages
317
+ :returns: Dataframe
318
+ """
319
+ self ._check_leagues (leagues )
320
+ self ._check_ids_names (team_ids , team_names )
321
+
322
+ query = {}
323
+
324
+ if game_ids :
325
+ query ["game_id" ] = game_ids
326
+ if team_names :
327
+ query ["team_id" ] = self ._convert_names_to_ids ("team" ,team_names )
328
+ if team_ids :
329
+ query ["team_id" ] = team_ids
330
+ if seasons :
331
+ query ["season_name" ] = seasons
332
+ if stages :
333
+ query ["stage_name" ] = stages
334
+
335
+ games = pd .DataFrame ([])
336
+ if isinstance (leagues , str ):
337
+ games_url = f"{ self .base_url } { leagues } /games"
338
+ response = self ._execute_query (games_url , query )
339
+
340
+ games = games .append (response )
341
+ elif isinstance (leagues , list ):
342
+ for league in leagues :
343
+ games_url = f"{ self .base_url } { league } /games"
344
+ response = self ._execute_query (games_url , query )
345
+
346
+ games = games .append (response )
347
+
348
+ return games .sort_values (by = ["date_time_utc" ], ascending = False )
0 commit comments