From ec7c02bb2cf4db20d8a2340f36a6171202c490a5 Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 3 May 2019 08:34:56 -0400 Subject: [PATCH 1/5] Add probable pitcher name & report to schedule() --- statsapi/__init__.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/statsapi/__init__.py b/statsapi/__init__.py index 9459260..f6d899b 100644 --- a/statsapi/__init__.py +++ b/statsapi/__init__.py @@ -41,8 +41,8 @@ def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sp 'game_date': date of game (YYYY-MM-DD) 'game_type': Preseason, Regular season, Postseason, etc. Look up possible values using the meta endpoint with type=gameTypes 'status': Scheduled, Warmup, In Progress, Final, etc. Look up possible values using the meta endpoint with type=gameStatus - 'away': team name for the away team (e.g. Philadelphia Phillies) - 'home': team name for the home team (e.g. Philadelphia Phillies) + 'away_name': team name for the away team (e.g. Philadelphia Phillies) + 'home_name': team name for the home team (e.g. Philadelphia Phillies) 'away_id': team id for the away team, e.g. 143. Use this to look up other info about a team using the team endpoint with teamId=143 'home_id': team id for the home team, e.g. 143. Use this to look up other info about a team using the team endpoint with teamId=143 'doubleheader': indicates if the game is part of a straight doubleheader (Y), a split doubleheader (S), or not part of a doubleheader @@ -54,6 +54,10 @@ def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sp 'winning_pitcher': full name of the winning pitcher, if the game is final and has a winner (not postponed/tied) 'losing_pitcher': full name of the losing pitcher, if the game is final and has a winner (not postponed/tied) 'save_pitcher': full name of the pitcher credited with a save, if the game is final and has a winner (not postponed/tied) + 'home_probable_pitcher': full name of the probable pitcher for the home team, if available + 'away_probable_pitcher': full name of the probable pitcher for the away team, if available + 'home_pitcher_note': pitching report for the home team probable pitcher, if available + 'away_pitcher_note': pitching report for the away team probable pitcher, if available 'summary': if the game is final, the summary will include " - () @ ()" if the game is not final, the summary will include " - @ ()" @@ -105,7 +109,7 @@ def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sp if opponent != '': params.update({'opponentId':str(opponent)}) - params.update({'sportId':str(sportId), 'hydrate':'decisions'}) + params.update({'sportId':str(sportId), 'hydrate':'decisions,probablePitcher(note)'}) r = get('schedule',params) @@ -121,12 +125,16 @@ def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sp 'game_date': date['date'], 'game_type': game['gameType'], 'status': game['status']['detailedState'], - 'away': game['teams']['away']['team']['name'], - 'home': game['teams']['home']['team']['name'], + 'away_name': game['teams']['away']['team']['name'], + 'home_name': game['teams']['home']['team']['name'], 'away_id': game['teams']['away']['team']['id'], 'home_id': game['teams']['home']['team']['id'], 'doubleheader': game['doubleHeader'], - 'game_num': game['gameNumber'] + 'game_num': game['gameNumber'], + 'home_probable_pitcher': game['teams']['home'].get('probablePitcher',{}).get('fullName',''), + 'away_probable_pitcher': game['teams']['away'].get('probablePitcher',{}).get('fullName',''), + 'home_pitcher_note': game['teams']['home'].get('probablePitcher').get('note',''), + 'away_pitcher_note': game['teams']['away'].get('probablePitcher').get('note','') } if game_info['status'] == 'Final': game_info.update({ From 260c9f80c3d53810a7d426f9ea963d41850e818e Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 3 May 2019 08:38:04 -0400 Subject: [PATCH 2/5] fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9a88a1..737a091 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ If you install manually, be sure to also install requests. * `statsapi.lookup_player()` - get a list of player data based on first, last, or full name, jersey number, current team Id, position, etc. -* `statsapi.lookup_team()` - get a lsit of teams info based on the team name, city, abbreviation, or file code +* `statsapi.lookup_team()` - get a list of teams' info based on the team name, city, abbreviation, or file code * `statsapi.schedule()` - retrieve a list of games on a given date/range and/or team/opponent From 9ba87978216c34632b147a14413a06c85145f938 Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 3 May 2019 08:39:56 -0400 Subject: [PATCH 3/5] Update headers with missing links --- README.md | 2 ++ statsapi/__init__.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 737a091..41eb245 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Python wrapper for MLB Stats API Created by Todd Roberts +https://pypi.org/project/MLB-StatsAPI/ + https://github.com/toddrob99/MLB-StatsAPI Documentation: https://toddrob99.github.io/MLB-StatsAPI/ diff --git a/statsapi/__init__.py b/statsapi/__init__.py index f6d899b..4ba1896 100644 --- a/statsapi/__init__.py +++ b/statsapi/__init__.py @@ -8,6 +8,8 @@ https://pypi.org/project/MLB-StatsAPI/ https://github.com/toddrob99/MLB-StatsAPI + +Documentation: https://toddrob99.github.io/MLB-StatsAPI/ """ import sys if sys.version_info.major < 3: From 0de509beb28ac76ba9a87e93e16f348b6171a05b Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 3 May 2019 09:44:01 -0400 Subject: [PATCH 4/5] remove test.py --- statsapi/test.py | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 statsapi/test.py diff --git a/statsapi/test.py b/statsapi/test.py deleted file mode 100644 index cf529d7..0000000 --- a/statsapi/test.py +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env python - From ec3c8a3bf0044b4ec50c63fd4aea50ed4844f9c1 Mon Sep 17 00:00:00 2001 From: todd Date: Sat, 4 May 2019 15:41:44 -0400 Subject: [PATCH 5/5] v0.0.7 * Added MLB copyright notice to README * Added ability to call schedule() for specific game_id(s) (comma separated string) * Added current inning to summary for schedule() when game is in progress, and status will now always be included * Added ability to get standings for a given division, and to exclude wildcard standings --- README.md | 4 +++ statsapi/__init__.py | 66 ++++++++++++++++++++++++++----------------- statsapi/endpoints.py | 3 +- statsapi/version.py | 2 +- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 41eb245..48ac27f 100644 --- a/README.md +++ b/README.md @@ -147,3 +147,7 @@ print( statsapi.player_stats(next(x['id'] for x in statsapi.get('sports_players' ``` print( statsapi.game_scoring_plays(567074) ) ``` + +## Copyright Notice + +This API wrapper interfaces with MLB's Stats API. Use of MLB data is subject to the notice posted at http://gdx.mlb.com/components/copyright.txt. diff --git a/statsapi/__init__.py b/statsapi/__init__.py index 4ba1896..7278833 100644 --- a/statsapi/__init__.py +++ b/statsapi/__init__.py @@ -33,9 +33,11 @@ import requests from datetime import datetime -def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sportId=1): +def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sportId=1, game_id=None): """Get list of games for a given date/range and/or team/opponent. + Include a game_id to get data for that game. + Output will be a list containing a dict for each game. Fields in the dict: 'game_id': unique MLB game id (primary key, or gamePk) @@ -60,8 +62,10 @@ def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sp 'away_probable_pitcher': full name of the probable pitcher for the away team, if available 'home_pitcher_note': pitching report for the home team probable pitcher, if available 'away_pitcher_note': pitching report for the away team probable pitcher, if available - 'summary': if the game is final, the summary will include " - () @ ()" - if the game is not final, the summary will include " - @ ()" + 'current_inning': current inning (applies best to in-progress games) + 'inning_state': state of current inning: top, middle, bottom, end (applies best to in-progress games) + 'summary': if the game is final or in progress, the summary will include " - () @ () ()" + if the game has not started yet, the summary will include " - @ ()" Example use: @@ -76,10 +80,10 @@ def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sp Output: - 2018-07-09 - Philadelphia Phillies (3) @ New York Mets (4) - 2018-07-09 - Philadelphia Phillies (3) @ New York Mets (1) - 2018-07-10 - Philadelphia Phillies (7) @ New York Mets (3) - 2018-07-11 - Philadelphia Phillies (0) @ New York Mets (3) + 2018-07-09 - Philadelphia Phillies (3) @ New York Mets (4) (Final) + 2018-07-09 - Philadelphia Phillies (3) @ New York Mets (1) (Final) + 2018-07-10 - Philadelphia Phillies (7) @ New York Mets (3) (Final) + 2018-07-11 - Philadelphia Phillies (0) @ New York Mets (3) (Final) Print a list of decisions for those games: @@ -111,7 +115,9 @@ def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sp if opponent != '': params.update({'opponentId':str(opponent)}) - params.update({'sportId':str(sportId), 'hydrate':'decisions,probablePitcher(note)'}) + if game_id: params.update({'gamePks':game_id}) + + params.update({'sportId':str(sportId), 'hydrate':'decisions,probablePitcher(note),linescore'}) r = get('schedule',params) @@ -136,17 +142,15 @@ def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sp 'home_probable_pitcher': game['teams']['home'].get('probablePitcher',{}).get('fullName',''), 'away_probable_pitcher': game['teams']['away'].get('probablePitcher',{}).get('fullName',''), 'home_pitcher_note': game['teams']['home'].get('probablePitcher').get('note',''), - 'away_pitcher_note': game['teams']['away'].get('probablePitcher').get('note','') + 'away_pitcher_note': game['teams']['away'].get('probablePitcher').get('note',''), + 'away_score': game['teams']['away'].get('score','0'), + 'home_score': game['teams']['home'].get('score','0'), + 'current_inning': game['linescore'].get('currentInning',''), + 'inning_state': game['linescore'].get('inningState','') } - if game_info['status'] == 'Final': - game_info.update({ - 'away_score': game['teams']['away']['score'], - 'home_score': game['teams']['home']['score'] - }) + if game_info['status'] in ['Final','Game Over']: if game.get('isTie'): game_info.update({ - 'away_score': game['teams']['away']['score'], - 'home_score': game['teams']['home']['score'], 'winning_team': 'Tie', 'losing_Team': 'Tie' }) @@ -158,8 +162,12 @@ def schedule(date=None, start_date=None, end_date=None, team='', opponent='', sp 'losing_pitcher': game['decisions'].get('loser',{}).get('fullName',''), 'save_pitcher': game['decisions'].get('save',{}).get('fullName') }) - summary = date['date'] + ' - ' + game['teams']['away']['team']['name'] + ' (' + str(game['teams']['away']['score']) + ') @ ' + game['teams']['home']['team']['name'] + ' (' + str(game['teams']['home']['score']) + ')' + summary = date['date'] + ' - ' + game['teams']['away']['team']['name'] + ' (' + str(game['teams']['away']['score']) + ') @ ' + game['teams']['home']['team']['name'] + ' (' + str(game['teams']['home']['score']) + ') (' + game['status']['detailedState'] + ')' game_info.update({'summary': summary}) + elif game_info['status'] == 'In Progress': + game_info.update({ + 'summary': date['date'] + ' - ' + game['teams']['away']['team']['name'] + ' (' + str(game['teams']['away']['score']) + ') @ ' + game['teams']['home']['team']['name'] + ' (' + str(game['teams']['home']['score']) + ') (' + game['linescore']['inningState'] + ' of the ' + game['linescore']['currentInningOrdinal'] + ')' + }) else: summary = date['date'] + ' - ' + game['teams']['away']['team']['name'] + ' @ ' + game['teams']['home']['team']['name'] + ' (' + game['status']['detailedState'] + ')' game_info.update({'summary': summary}) @@ -1118,9 +1126,12 @@ def league_leaders(leaderCategories,season=None,limit=10,statGroup=None,leagueId return leaders -def standings(leagueId=None,season=None,standingsTypes=None,date=None): - """Get formatted standings for a given league and season. - Will include division and wildcard standings +def standings(leagueId='103,104',division='all',include_wildcard=True,season=None,standingsTypes=None,date=None): + """Get formatted standings for a given league/division and season. + + Using both leagueId and divisionId is fine, as long as the division belongs to the specified league + + Return value will be a formatted table including division and wildcard standings, unless include_wildcard=False Format for date = 'MM/DD/YYYY', e.g. '04/24/2019' @@ -1158,7 +1169,6 @@ def standings(leagueId=None,season=None,standingsTypes=None,date=None): 5 Washington Nationals 59 101 31.5 E 13 29.5 E """ - if not leagueId: leagueId = '103,104' params = {'leagueId':leagueId} if date: params.update({'date':date}) if not season: @@ -1168,7 +1178,6 @@ def standings(leagueId=None,season=None,standingsTypes=None,date=None): season = datetime.now().year if not standingsTypes: standingsTypes = 'regularSeason' params.update({'season':season,'standingsTypes':standingsTypes}) - if leagueId: params.update({'leagueId':leagueId}) params.update({'hydrate':'team(division)','fields':'records,standingsType,teamRecords,team,name,division,id,nameShort,abbreviation,divisionRank,gamesBack,wildCardRank,wildCardGamesBack,wildCardEliminationNumber,divisionGamesBack,clinched,eliminationNumber,winningPercentage,type,wins,losses'}) r = get('standings',params) @@ -1177,7 +1186,7 @@ def standings(leagueId=None,season=None,standingsTypes=None,date=None): divisions = {} for y in r['records']: - for x in y['teamRecords']: + for x in (x for x in y['teamRecords'] if division.lower()=='all' or division.lower()==x['team']['division']['abbreviation'].lower()): if x['team']['division']['id'] not in divisions.keys(): divisions.update({x['team']['division']['id']:{'div_name':x['team']['division']['name'],'teams':[]}}) team = { @@ -1195,9 +1204,14 @@ def standings(leagueId=None,season=None,standingsTypes=None,date=None): for div_id,div in divisions.items(): standings += div['div_name'] + '\n' - standings += '{:^4} {:<21} {:^3} {:^3} {:^4} {:^4} {:^7} {:^5} {:^4}\n'.format(*['Rank','Team','W','L','GB','(E#)','WC Rank','WC GB','(E#)']) - for t in div['teams']: - standings += '{div_rank:^4} {name:<21} {w:^3} {l:^3} {gb:^4} {elim_num:^4} {wc_rank:^7} {wc_gb:^5} {wc_elim_num:^4}\n'.format(**t) + if include_wildcard: + standings += '{:^4} {:<21} {:^3} {:^3} {:^4} {:^4} {:^7} {:^5} {:^4}\n'.format(*['Rank','Team','W','L','GB','(E#)','WC Rank','WC GB','(E#)']) + for t in div['teams']: + standings += '{div_rank:^4} {name:<21} {w:^3} {l:^3} {gb:^4} {elim_num:^4} {wc_rank:^7} {wc_gb:^5} {wc_elim_num:^4}\n'.format(**t) + else: + standings += '{:^4} {:<21} {:^3} {:^3} {:^4} {:^4}\n'.format(*['Rank','Team','W','L','GB','(E#)']) + for t in div['teams']: + standings += '{div_rank:^4} {name:<21} {w:^3} {l:^3} {gb:^4} {elim_num:^4}\n'.format(**t) standings += '\n' return standings diff --git a/statsapi/endpoints.py b/statsapi/endpoints.py index 2be2cbd..373b082 100644 --- a/statsapi/endpoints.py +++ b/statsapi/endpoints.py @@ -234,7 +234,8 @@ } }, 'query_params': ['timecode','fields'], - 'required_params': [[]] + 'required_params': [[]], + 'note': 'If you only want the current win probability for each team, try the game_contextMetrics endpoint instad.' }, 'game_boxscore': { 'url': BASE_URL + '{ver}/game/{gamePk}/boxscore', diff --git a/statsapi/version.py b/statsapi/version.py index 428cd5c..4c99ccc 100644 --- a/statsapi/version.py +++ b/statsapi/version.py @@ -1,3 +1,3 @@ #!/usr/bin/env python -VERSION = '0.0.6' +VERSION = '0.0.7'