From 11fb695d19dffec84aae283620e7cec1893f2b34 Mon Sep 17 00:00:00 2001 From: Edward Amor Date: Tue, 29 Oct 2019 17:22:20 -0400 Subject: [PATCH 01/14] fix: add requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +requests From f8d654e6c9d80daa80e9cccc29402320a209997f Mon Sep 17 00:00:00 2001 From: Edward Amor Date: Tue, 29 Oct 2019 17:24:35 -0400 Subject: [PATCH 02/14] fix: add requirements-dev.txt --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements-dev.txt diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..e079f8a --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1 @@ +pytest From 256496f24882a9e60b209548c3cb62cefefe89b4 Mon Sep 17 00:00:00 2001 From: Edward Amor Date: Tue, 29 Oct 2019 17:26:02 -0400 Subject: [PATCH 03/14] fix: update requirements-dev.txt --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index e079f8a..1213649 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1 +1,2 @@ pytest +pytest-mock From 8e2779cd1ebf6ab89c02b2d6aeff318bcfbc725d Mon Sep 17 00:00:00 2001 From: Edward Amor Date: Tue, 29 Oct 2019 17:47:13 -0400 Subject: [PATCH 04/14] fix(tests): add return value test for get function The return value of the get function should always be a dictionary. toddrob99/MLB-StatsAPI#22 --- tests/__init__.py | 0 tests/test_get.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/test_get.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_get.py b/tests/test_get.py new file mode 100644 index 0000000..f37f21c --- /dev/null +++ b/tests/test_get.py @@ -0,0 +1,33 @@ +import statsapi + + +def fake_dict(): + return { + "foo": { + "url": "www.foo.com", + "path_params": { + "ver": { + "type": "str", + "default": "v1", + "leading_slash": False, + "trailing_slash": False, + "required": True, + } + }, + "query_params": ["bar"], + "required_params": [[]], + } + } + + +def test_get_returns_dictionary(mocker): + # mock the ENDPOINTS dictionary + mocker.patch.dict("statsapi.ENDPOINTS", fake_dict(), clear=True) + # mock the requests object + mock_req = mocker.patch("statsapi.requests", autospec=True) + # mock the status code to always be 200 + mock_req.get.return_value.status_code = 200 + + result = statsapi.get("foo", {"bar": "baz"}) + # assert that result is the same as the return value from calling the json method of a response object + assert result == mock_req.get.return_value.json.return_value From 8bcf1830a51b997c0eea0c08e215bae4dc7d7624 Mon Sep 17 00:00:00 2001 From: Edward Amor Date: Tue, 29 Oct 2019 18:11:00 -0400 Subject: [PATCH 05/14] fix(tests): add url construction test to get func The get function should request the properly formatted url. toddrob99/MLB-StatsAPI#22 --- tests/test_get.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_get.py b/tests/test_get.py index f37f21c..ce37773 100644 --- a/tests/test_get.py +++ b/tests/test_get.py @@ -31,3 +31,17 @@ def test_get_returns_dictionary(mocker): result = statsapi.get("foo", {"bar": "baz"}) # assert that result is the same as the return value from calling the json method of a response object assert result == mock_req.get.return_value.json.return_value + + +def test_get_calls_correct_url(mocker): + # mock the ENDPOINTS dictionary + mocker.patch.dict("statsapi.ENDPOINTS", fake_dict(), clear=True) + # mock the requests object + mock_req = mocker.patch("statsapi.requests", autospec=True) + + try: + statsapi.get("foo", {"bar": "baz"}) + except ValueError: + pass + + mock_req.get.assert_called_with("www.foo.com?bar=baz") From 563cfd01fbb0d56e217bf3926b1985468020dfb8 Mon Sep 17 00:00:00 2001 From: Edward Amor Date: Tue, 29 Oct 2019 18:11:00 -0400 Subject: [PATCH 06/14] fix(tests): add url construction test for get func The get function should request the properly formatted url. toddrob99/MLB-StatsAPI#22 --- tests/test_get.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_get.py b/tests/test_get.py index f37f21c..ce37773 100644 --- a/tests/test_get.py +++ b/tests/test_get.py @@ -31,3 +31,17 @@ def test_get_returns_dictionary(mocker): result = statsapi.get("foo", {"bar": "baz"}) # assert that result is the same as the return value from calling the json method of a response object assert result == mock_req.get.return_value.json.return_value + + +def test_get_calls_correct_url(mocker): + # mock the ENDPOINTS dictionary + mocker.patch.dict("statsapi.ENDPOINTS", fake_dict(), clear=True) + # mock the requests object + mock_req = mocker.patch("statsapi.requests", autospec=True) + + try: + statsapi.get("foo", {"bar": "baz"}) + except ValueError: + pass + + mock_req.get.assert_called_with("www.foo.com?bar=baz") From d64b17a715247797d117c0d22db23690ff025ceb Mon Sep 17 00:00:00 2001 From: Edward Amor Date: Tue, 29 Oct 2019 18:28:13 -0400 Subject: [PATCH 07/14] fix(test): add error test for get function The get function should raise some errors for certain situations. - Missing required parameters - Missing path parameters - Bad status code returned toddrob99/MLB-StatsAPI#22 --- tests/test_get.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_get.py b/tests/test_get.py index ce37773..58aeb3a 100644 --- a/tests/test_get.py +++ b/tests/test_get.py @@ -1,4 +1,5 @@ import statsapi +import pytest def fake_dict(): @@ -45,3 +46,23 @@ def test_get_calls_correct_url(mocker): pass mock_req.get.assert_called_with("www.foo.com?bar=baz") + + +def test_get_raises_errors(mocker): + # mock the ENDPOINTS dictionary + mocker.patch.dict("statsapi.ENDPOINTS", fake_dict(), clear=True) + # mock the requests object + mock_req = mocker.patch("statsapi.requests", autospec=True) + # mock the status code to always be 200 + mock_req.get.return_value.status_code = 0 + + # bad status code + with pytest.raises(ValueError): + statsapi.get("foo", {"bar": "baz"}) + + # invalid endpoint + with pytest.raises(ValueError): + statsapi.get("bar", {"foo": "baz"}) + + # need to add test for path requirement not met + # need to add test for required params From f8b4b029c160dd05ea30994fa66fb13d5c8ca42c Mon Sep 17 00:00:00 2001 From: todd Date: Tue, 5 Nov 2019 21:15:21 -0500 Subject: [PATCH 08/14] Fix endpoint config, honor leading/trailing slash FIX: Leading & trailing slashes not honored in endpoint config #28 FIX: Update required params for team_roster endpoint #29 ENHANCE: Minor code formatting updates --- statsapi/__init__.py | 52 ++++++++++++++++++++++++++++++++++++++++--- statsapi/endpoints.py | 2 +- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/statsapi/__init__.py b/statsapi/__init__.py index c19a936..b319f5c 100644 --- a/statsapi/__init__.py +++ b/statsapi/__init__.py @@ -111,6 +111,7 @@ def schedule( if end_date and not start_date: date = end_date end_date = None + if start_date and not end_date: date = start_date start_date = None @@ -388,6 +389,7 @@ def boxscore( "ops": "", } ) + while len(awayBatters) < len(homeBatters): awayBatters.append( { @@ -412,6 +414,7 @@ def boxscore( for i in range(0, len(awayBatters)): if i == 0 or i == len(awayBatters) - 1: boxscore += "-" * rowLen + " | " + "-" * rowLen + "\n" + boxscore += "{namefield:<40} {ab:^3} {r:^3} {h:^3} {rbi:^3} {bb:^3} {k:^3} {lob:^3} {avg:^4} {ops:^5} | ".format( **awayBatters[i] ) @@ -427,6 +430,7 @@ def boxscore( while len(awayBattingNotes) > len(homeBattingNotes): homeBattingNotes.update({len(homeBattingNotes): ""}) + while len(awayBattingNotes) < len(homeBattingNotes): awayBattingNotes.update({len(awayBattingNotes): ""}) @@ -459,11 +463,12 @@ def boxscore( else: lines.append(check) check = " " + word + if len(check): lines.append(check) + for i in range(0, len(lines)): awayBoxInfo.update({len(awayBoxInfo): lines[i]}) - else: awayBoxInfo.update( {len(awayBoxInfo): x["label"] + ": " + x.get("value", "")} @@ -487,6 +492,7 @@ def boxscore( check = " " + word if len(check): lines.append(check) + for i in range(0, len(lines)): homeBoxInfo.update({len(homeBoxInfo): lines[i]}) else: @@ -496,12 +502,14 @@ def boxscore( if len(awayBoxInfo) and infoType == "BATTING": awayBoxInfo.update({len(awayBoxInfo): " "}) + if len(homeBoxInfo) and infoType == "BATTING": homeBoxInfo.update({len(homeBoxInfo): " "}) if len(awayBoxInfo) > 0: while len(awayBoxInfo) > len(homeBoxInfo): homeBoxInfo.update({len(homeBoxInfo): ""}) + while len(awayBoxInfo) < len(homeBoxInfo): awayBoxInfo.update({len(awayBoxInfo): ""}) @@ -532,6 +540,7 @@ def boxscore( "era": "", } ) + while len(awayPitchers) < len(homePitchers): awayPitchers.append( { @@ -555,6 +564,7 @@ def boxscore( for i in range(0, len(awayPitchers)): if i == 0 or i == len(awayPitchers) - 1: boxscore += "-" * rowLen + " | " + "-" * rowLen + "\n" + boxscore += "{namefield:<43} {ip:^4} {h:^3} {r:^3} {er:^3} {bb:^3} {k:^3} {hr:^3} {era:^6} | ".format( **awayPitchers[i] ) @@ -591,11 +601,12 @@ def boxscore( else: lines.append(check) check = " " + word + if len(check): lines.append(check) + for i in range(0, len(lines)): gameBoxInfo.update({len(gameBoxInfo): lines[i]}) - else: gameBoxInfo.update( { @@ -637,6 +648,7 @@ def boxscore_data(gamePk, timecode=None): } if timecode: params.update({"timecode": timecode}) + r = get("game", params) boxData.update({"gameId": r["gameData"]["game"]["id"]}) @@ -926,6 +938,7 @@ def boxscore_data(gamePk, timecode=None): awayBattingNotes = {} for n in boxData["away"]["note"]: awayBattingNotes.update({len(awayBattingNotes): n["label"] + "-" + n["value"]}) + homeBattingNotes = {} for n in boxData["home"]["note"]: homeBattingNotes.update({len(homeBattingNotes): n["label"] + "-" + n["value"]}) @@ -1207,6 +1220,7 @@ def linescore(gamePk, timecode=None): } if timecode: params.update({"timecode": timecode}) + r = get("game", params) header_name = r["gameData"]["status"]["abstractGameState"] @@ -1281,6 +1295,7 @@ def linescore(gamePk, timecode=None): linescore += ("{:^2}" * (len(k[1]) - 3)).format(*k[1]) linescore += ("{:^4}" * 3).format(*k[1][-3:]) linescore += "\n" + if len(linescore) > 1: linescore = linescore[:-1] # strip the extra line break @@ -1448,6 +1463,7 @@ def game_highlight_data(gamePk): r["dates"][0]["games"][0]["content"]["highlights"]["highlights"]["items"] ): return "" + items = r["dates"][0]["games"][0]["content"]["highlights"]["highlights"]["items"] unorderedHighlights = {} @@ -1523,6 +1539,7 @@ def game_pace(season=datetime.now().year, sportId=1): for k in s.keys(): if k in ["season", "sport"]: continue + if k == "prPortalCalculatedFields": for x in s[k].keys(): pace += "{}: {}\n".format(x, s[k][x]) @@ -1538,6 +1555,7 @@ def game_pace_data(season=datetime.now().year, sportId=1): params = {} if season: params.update({"season": season}) + if sportId: params.update({"sportId": sportId}) @@ -1610,9 +1628,11 @@ def player_stats(personId, group="[hitting,pitching,fielding]", type="season"): stats += player["first_name"] if player["nickname"]: stats += ' "{nickname}"'.format(**player) + stats += " {last_name}, {position} ({mlb_debut:.4}-".format(**player) if not player["active"]: stats += "{last_played:.4}".format(**player) + stats += ")\n\n" for x in player["stats"]: @@ -1625,11 +1645,13 @@ def player_stats(personId, group="[hitting,pitching,fielding]", type="season"): ) if x["stats"].get("position"): stats += " ({})".format(x["stats"]["position"]["abbreviation"]) + stats += "\n" for y in x["stats"].keys(): if y == "position": continue stats += "{}: {}\n".format(y, x["stats"][y]) + stats += "\n" return stats @@ -1772,6 +1794,7 @@ def lookup_team(lookup_value, activeStatus="Y", season=datetime.now().year, spor if str(lookup_value).lower() in str(v).lower(): teams.append(team) break + return teams @@ -1985,22 +2008,30 @@ def league_leader_data( params = {"leaderCategories": leaderCategories, "sportId": sportId, "limit": limit} if season: params.update({"season": season}) + if statType: params.update({"statType": statType}) + if not season and not statType: params.update( {"season": datetime.now().year} ) # default season to current year if no season or statType provided + if statGroup: if statGroup == "batting": statGroup = "hitting" + params.update({"statGroup": statGroup}) + if gameTypes: params.update({"leaderGameTypes": gameTypes}) + if leagueId: params.update({"leagueId": leagueId}) + if playerPool: params.update({"playerPool": playerPool}) + params.update( { "fields": "leagueLeaders,leaders,rank,value,team,name,league,name,person,fullName" @@ -2122,13 +2153,16 @@ def standings_data( params = {"leagueId": leagueId} if date: params.update({"date": date}) + if not season: if date: season = date[-4:] else: season = datetime.now().year + if not standingsTypes: standingsTypes = "regularSeason" + params.update({"season": season, "standingsTypes": standingsTypes}) params.update( { @@ -2157,6 +2191,7 @@ def standings_data( } } ) + team = { "name": x["team"]["name"], "div_rank": x["divisionRank"], @@ -2216,6 +2251,7 @@ def roster(teamId, rosterType=None, season=datetime.now().year, date=None): """ if not rosterType: rosterType = "active" + params = {"rosterType": rosterType, "season": season, "teamId": teamId} if date: params.update({"date": date}) @@ -2347,6 +2383,7 @@ def get(endpoint, params, force=False): ep = ENDPOINTS.get(endpoint) if not ep: raise ValueError("Invalid endpoint (" + str(endpoint) + ").") + url = ep["url"] logger.debug("URL: {}".format(url)) @@ -2384,8 +2421,14 @@ def get(endpoint, params, force=False): # Replace path parameters with their values for k, v in path_params.items(): logger.debug("Replacing {%s}" % k) - url = url.replace("{" + k + "}", v) + url = url.replace( + "{" + k + "}", + ("/" if ep["path_params"][k]["leading_slash"] else "") + + v + + ("/" if ep["path_params"][k]["trailing_slash"] else "") + ) logger.debug("URL: {}".format(url)) + while url.find("{") != -1 and url.find("}") > url.find("{"): param = url[url.find("{") + 1 : url.find("}")] if ep.get("path_params", {}).get(param, {}).get("required"): @@ -2411,6 +2454,7 @@ def get(endpoint, params, force=False): else: logger.debug("Removing optional param {%s}" % param) url = url.replace("{" + param + "}", "") + logger.debug("URL: {}".format(url)) # Add query parameters to the URL if len(query_params) > 0: @@ -2431,11 +2475,13 @@ def get(endpoint, params, force=False): if len(missing_params) == 0: satisfied = True break + if not satisfied and not force: if ep.get("note"): note = "\n--Endpoint note: " + ep.get("note") else: note = "" + raise ValueError( "Missing required parameter(s): " + ", ".join(missing_params) diff --git a/statsapi/endpoints.py b/statsapi/endpoints.py index 5c7cdec..38e2da1 100644 --- a/statsapi/endpoints.py +++ b/statsapi/endpoints.py @@ -1246,7 +1246,7 @@ }, }, "query_params": ["rosterType", "season", "date", "hydrate", "fields"], - "required_params": [["rosterType", "season"]], + "required_params": [[]], }, "venue": { "url": BASE_URL + "{ver}/venues", From 08cdd9f5e5afc61661565a28163a5682f7c5cfa1 Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 8 Nov 2019 20:46:33 -0500 Subject: [PATCH 09/14] Add .vscode to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9225091..42614f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /logs +.vscode # Byte-compiled / optimized / DLL files __pycache__/ From 11539a64b4b95b79abea3aedf8954e55aa061fbf Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 8 Nov 2019 20:52:50 -0500 Subject: [PATCH 10/14] v0.1.5 --- statsapi/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/statsapi/version.py b/statsapi/version.py index 3d4e598..fe5b52b 100644 --- a/statsapi/version.py +++ b/statsapi/version.py @@ -1,3 +1,3 @@ #!/usr/bin/env python -VERSION = "0.1.4" +VERSION = "0.1.5" From 437c0e46d6d9e300e03f45d1629af79b9f8aa61c Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 8 Nov 2019 20:58:27 -0500 Subject: [PATCH 11/14] Code formatting and remove unused variable --- statsapi/__init__.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/statsapi/__init__.py b/statsapi/__init__.py index b319f5c..141c61c 100644 --- a/statsapi/__init__.py +++ b/statsapi/__init__.py @@ -143,9 +143,7 @@ def schedule( games = [] if r.get("totalItems") == 0: - return ( - games - ) # TODO: ValueError('No games to parse from schedule object.') instead? + return games # TODO: ValueError('No games to parse from schedule object.') instead? else: for date in r.get("dates"): for game in date.get("games"): @@ -2110,7 +2108,7 @@ def standings( standings = "" - for div_id, div in divisions.items(): + for div in divisions.values(): standings += div["div_name"] + "\n" if include_wildcard: standings += "{:^4} {:<21} {:^3} {:^3} {:^4} {:^4} {:^7} {:^5} {:^4}\n".format( @@ -2425,7 +2423,7 @@ def get(endpoint, params, force=False): "{" + k + "}", ("/" if ep["path_params"][k]["leading_slash"] else "") + v - + ("/" if ep["path_params"][k]["trailing_slash"] else "") + + ("/" if ep["path_params"][k]["trailing_slash"] else ""), ) logger.debug("URL: {}".format(url)) From 6716b088eaaa0241762163cac5b3205cb1a8d429 Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 8 Nov 2019 21:21:40 -0500 Subject: [PATCH 12/14] Code formatting and remove unused module import --- setup.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index d927e5b..c165c94 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,9 @@ import setuptools -import os +from statsapi import version with open("README.md", "r") as fh: long_description = fh.read() -from statsapi import version - setuptools.setup( name="MLB-StatsAPI", version=version.VERSION, @@ -16,7 +14,7 @@ long_description_content_type="text/markdown", url="https://github.com/toddrob99/MLB-StatsAPI", packages=setuptools.find_packages(), - install_requires=['requests'], + install_requires=["requests"], classifiers=[ "Programming Language :: Python", "Programming Language :: Python :: 2.7", From 45e6954535311c16055e4c1f1c21aa446c70f510 Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 8 Nov 2019 21:25:31 -0500 Subject: [PATCH 13/14] Update documentation for v0.1.5 --- docs/endpoints.html | 2 +- docs/index.html | 116 ++++++++++++++++++++++++++++++++++++++------ docs/version.html | 2 +- 3 files changed, 104 insertions(+), 16 deletions(-) diff --git a/docs/endpoints.html b/docs/endpoints.html index 968e3a7..d967c35 100644 --- a/docs/endpoints.html +++ b/docs/endpoints.html @@ -1272,7 +1272,7 @@

Module statsapi.endpoints

}, }, "query_params": ["rosterType", "season", "date", "hydrate", "fields"], - "required_params": [["rosterType", "season"]], + "required_params": [[]], }, "venue": { "url": BASE_URL + "{ver}/venues", diff --git a/docs/index.html b/docs/index.html index 4e822c6..9648afd 100644 --- a/docs/index.html +++ b/docs/index.html @@ -143,6 +143,7 @@

MLB-StatsAPI

if end_date and not start_date: date = end_date end_date = None + if start_date and not end_date: date = start_date start_date = None @@ -174,9 +175,7 @@

MLB-StatsAPI

games = [] if r.get("totalItems") == 0: - return ( - games - ) # TODO: ValueError('No games to parse from schedule object.') instead? + return games # TODO: ValueError('No games to parse from schedule object.') instead? else: for date in r.get("dates"): for game in date.get("games"): @@ -420,6 +419,7 @@

MLB-StatsAPI

"ops": "", } ) + while len(awayBatters) < len(homeBatters): awayBatters.append( { @@ -444,6 +444,7 @@

MLB-StatsAPI

for i in range(0, len(awayBatters)): if i == 0 or i == len(awayBatters) - 1: boxscore += "-" * rowLen + " | " + "-" * rowLen + "\n" + boxscore += "{namefield:<40} {ab:^3} {r:^3} {h:^3} {rbi:^3} {bb:^3} {k:^3} {lob:^3} {avg:^4} {ops:^5} | ".format( **awayBatters[i] ) @@ -459,6 +460,7 @@

MLB-StatsAPI

while len(awayBattingNotes) > len(homeBattingNotes): homeBattingNotes.update({len(homeBattingNotes): ""}) + while len(awayBattingNotes) < len(homeBattingNotes): awayBattingNotes.update({len(awayBattingNotes): ""}) @@ -491,11 +493,12 @@

MLB-StatsAPI

else: lines.append(check) check = " " + word + if len(check): lines.append(check) + for i in range(0, len(lines)): awayBoxInfo.update({len(awayBoxInfo): lines[i]}) - else: awayBoxInfo.update( {len(awayBoxInfo): x["label"] + ": " + x.get("value", "")} @@ -519,6 +522,7 @@

MLB-StatsAPI

check = " " + word if len(check): lines.append(check) + for i in range(0, len(lines)): homeBoxInfo.update({len(homeBoxInfo): lines[i]}) else: @@ -528,12 +532,14 @@

MLB-StatsAPI

if len(awayBoxInfo) and infoType == "BATTING": awayBoxInfo.update({len(awayBoxInfo): " "}) + if len(homeBoxInfo) and infoType == "BATTING": homeBoxInfo.update({len(homeBoxInfo): " "}) if len(awayBoxInfo) > 0: while len(awayBoxInfo) > len(homeBoxInfo): homeBoxInfo.update({len(homeBoxInfo): ""}) + while len(awayBoxInfo) < len(homeBoxInfo): awayBoxInfo.update({len(awayBoxInfo): ""}) @@ -564,6 +570,7 @@

MLB-StatsAPI

"era": "", } ) + while len(awayPitchers) < len(homePitchers): awayPitchers.append( { @@ -587,6 +594,7 @@

MLB-StatsAPI

for i in range(0, len(awayPitchers)): if i == 0 or i == len(awayPitchers) - 1: boxscore += "-" * rowLen + " | " + "-" * rowLen + "\n" + boxscore += "{namefield:<43} {ip:^4} {h:^3} {r:^3} {er:^3} {bb:^3} {k:^3} {hr:^3} {era:^6} | ".format( **awayPitchers[i] ) @@ -623,11 +631,12 @@

MLB-StatsAPI

else: lines.append(check) check = " " + word + if len(check): lines.append(check) + for i in range(0, len(lines)): gameBoxInfo.update({len(gameBoxInfo): lines[i]}) - else: gameBoxInfo.update( { @@ -669,6 +678,7 @@

MLB-StatsAPI

} if timecode: params.update({"timecode": timecode}) + r = get("game", params) boxData.update({"gameId": r["gameData"]["game"]["id"]}) @@ -958,6 +968,7 @@

MLB-StatsAPI

awayBattingNotes = {} for n in boxData["away"]["note"]: awayBattingNotes.update({len(awayBattingNotes): n["label"] + "-" + n["value"]}) + homeBattingNotes = {} for n in boxData["home"]["note"]: homeBattingNotes.update({len(homeBattingNotes): n["label"] + "-" + n["value"]}) @@ -1239,6 +1250,7 @@

MLB-StatsAPI

} if timecode: params.update({"timecode": timecode}) + r = get("game", params) header_name = r["gameData"]["status"]["abstractGameState"] @@ -1313,6 +1325,7 @@

MLB-StatsAPI

linescore += ("{:^2}" * (len(k[1]) - 3)).format(*k[1]) linescore += ("{:^4}" * 3).format(*k[1][-3:]) linescore += "\n" + if len(linescore) > 1: linescore = linescore[:-1] # strip the extra line break @@ -1480,6 +1493,7 @@

MLB-StatsAPI

r["dates"][0]["games"][0]["content"]["highlights"]["highlights"]["items"] ): return "" + items = r["dates"][0]["games"][0]["content"]["highlights"]["highlights"]["items"] unorderedHighlights = {} @@ -1555,6 +1569,7 @@

MLB-StatsAPI

for k in s.keys(): if k in ["season", "sport"]: continue + if k == "prPortalCalculatedFields": for x in s[k].keys(): pace += "{}: {}\n".format(x, s[k][x]) @@ -1570,6 +1585,7 @@

MLB-StatsAPI

params = {} if season: params.update({"season": season}) + if sportId: params.update({"sportId": sportId}) @@ -1642,9 +1658,11 @@

MLB-StatsAPI

stats += player["first_name"] if player["nickname"]: stats += ' "{nickname}"'.format(**player) + stats += " {last_name}, {position} ({mlb_debut:.4}-".format(**player) if not player["active"]: stats += "{last_played:.4}".format(**player) + stats += ")\n\n" for x in player["stats"]: @@ -1657,11 +1675,13 @@

MLB-StatsAPI

) if x["stats"].get("position"): stats += " ({})".format(x["stats"]["position"]["abbreviation"]) + stats += "\n" for y in x["stats"].keys(): if y == "position": continue stats += "{}: {}\n".format(y, x["stats"][y]) + stats += "\n" return stats @@ -1804,6 +1824,7 @@

MLB-StatsAPI

if str(lookup_value).lower() in str(v).lower(): teams.append(team) break + return teams @@ -2017,22 +2038,30 @@

MLB-StatsAPI

params = {"leaderCategories": leaderCategories, "sportId": sportId, "limit": limit} if season: params.update({"season": season}) + if statType: params.update({"statType": statType}) + if not season and not statType: params.update( {"season": datetime.now().year} ) # default season to current year if no season or statType provided + if statGroup: if statGroup == "batting": statGroup = "hitting" + params.update({"statGroup": statGroup}) + if gameTypes: params.update({"leaderGameTypes": gameTypes}) + if leagueId: params.update({"leagueId": leagueId}) + if playerPool: params.update({"playerPool": playerPool}) + params.update( { "fields": "leagueLeaders,leaders,rank,value,team,name,league,name,person,fullName" @@ -2111,7 +2140,7 @@

MLB-StatsAPI

standings = "" - for div_id, div in divisions.items(): + for div in divisions.values(): standings += div["div_name"] + "\n" if include_wildcard: standings += "{:^4} {:<21} {:^3} {:^3} {:^4} {:^4} {:^7} {:^5} {:^4}\n".format( @@ -2154,13 +2183,16 @@

MLB-StatsAPI

params = {"leagueId": leagueId} if date: params.update({"date": date}) + if not season: if date: season = date[-4:] else: season = datetime.now().year + if not standingsTypes: standingsTypes = "regularSeason" + params.update({"season": season, "standingsTypes": standingsTypes}) params.update( { @@ -2189,6 +2221,7 @@

MLB-StatsAPI

} } ) + team = { "name": x["team"]["name"], "div_rank": x["divisionRank"], @@ -2248,6 +2281,7 @@

MLB-StatsAPI

""" if not rosterType: rosterType = "active" + params = {"rosterType": rosterType, "season": season, "teamId": teamId} if date: params.update({"date": date}) @@ -2379,6 +2413,7 @@

MLB-StatsAPI

ep = ENDPOINTS.get(endpoint) if not ep: raise ValueError("Invalid endpoint (" + str(endpoint) + ").") + url = ep["url"] logger.debug("URL: {}".format(url)) @@ -2416,8 +2451,14 @@

MLB-StatsAPI

# Replace path parameters with their values for k, v in path_params.items(): logger.debug("Replacing {%s}" % k) - url = url.replace("{" + k + "}", v) + url = url.replace( + "{" + k + "}", + ("/" if ep["path_params"][k]["leading_slash"] else "") + + v + + ("/" if ep["path_params"][k]["trailing_slash"] else ""), + ) logger.debug("URL: {}".format(url)) + while url.find("{") != -1 and url.find("}") > url.find("{"): param = url[url.find("{") + 1 : url.find("}")] if ep.get("path_params", {}).get(param, {}).get("required"): @@ -2443,6 +2484,7 @@

MLB-StatsAPI

else: logger.debug("Removing optional param {%s}" % param) url = url.replace("{" + param + "}", "") + logger.debug("URL: {}".format(url)) # Add query parameters to the URL if len(query_params) > 0: @@ -2463,11 +2505,13 @@

MLB-StatsAPI

if len(missing_params) == 0: satisfied = True break + if not satisfied and not force: if ep.get("note"): note = "\n--Endpoint note: " + ep.get("note") else: note = "" + raise ValueError( "Missing required parameter(s): " + ", ".join(missing_params) @@ -3245,6 +3289,7 @@

Functions

"ops": "", } ) + while len(awayBatters) < len(homeBatters): awayBatters.append( { @@ -3269,6 +3314,7 @@

Functions

for i in range(0, len(awayBatters)): if i == 0 or i == len(awayBatters) - 1: boxscore += "-" * rowLen + " | " + "-" * rowLen + "\n" + boxscore += "{namefield:<40} {ab:^3} {r:^3} {h:^3} {rbi:^3} {bb:^3} {k:^3} {lob:^3} {avg:^4} {ops:^5} | ".format( **awayBatters[i] ) @@ -3284,6 +3330,7 @@

Functions

while len(awayBattingNotes) > len(homeBattingNotes): homeBattingNotes.update({len(homeBattingNotes): ""}) + while len(awayBattingNotes) < len(homeBattingNotes): awayBattingNotes.update({len(awayBattingNotes): ""}) @@ -3316,11 +3363,12 @@

Functions

else: lines.append(check) check = " " + word + if len(check): lines.append(check) + for i in range(0, len(lines)): awayBoxInfo.update({len(awayBoxInfo): lines[i]}) - else: awayBoxInfo.update( {len(awayBoxInfo): x["label"] + ": " + x.get("value", "")} @@ -3344,6 +3392,7 @@

Functions

check = " " + word if len(check): lines.append(check) + for i in range(0, len(lines)): homeBoxInfo.update({len(homeBoxInfo): lines[i]}) else: @@ -3353,12 +3402,14 @@

Functions

if len(awayBoxInfo) and infoType == "BATTING": awayBoxInfo.update({len(awayBoxInfo): " "}) + if len(homeBoxInfo) and infoType == "BATTING": homeBoxInfo.update({len(homeBoxInfo): " "}) if len(awayBoxInfo) > 0: while len(awayBoxInfo) > len(homeBoxInfo): homeBoxInfo.update({len(homeBoxInfo): ""}) + while len(awayBoxInfo) < len(homeBoxInfo): awayBoxInfo.update({len(awayBoxInfo): ""}) @@ -3389,6 +3440,7 @@

Functions

"era": "", } ) + while len(awayPitchers) < len(homePitchers): awayPitchers.append( { @@ -3412,6 +3464,7 @@

Functions

for i in range(0, len(awayPitchers)): if i == 0 or i == len(awayPitchers) - 1: boxscore += "-" * rowLen + " | " + "-" * rowLen + "\n" + boxscore += "{namefield:<43} {ip:^4} {h:^3} {r:^3} {er:^3} {bb:^3} {k:^3} {hr:^3} {era:^6} | ".format( **awayPitchers[i] ) @@ -3448,11 +3501,12 @@

Functions

else: lines.append(check) check = " " + word + if len(check): lines.append(check) + for i in range(0, len(lines)): gameBoxInfo.update({len(gameBoxInfo): lines[i]}) - else: gameBoxInfo.update( { @@ -3511,6 +3565,7 @@

Functions

} if timecode: params.update({"timecode": timecode}) + r = get("game", params) boxData.update({"gameId": r["gameData"]["game"]["id"]}) @@ -3800,6 +3855,7 @@

Functions

awayBattingNotes = {} for n in boxData["away"]["note"]: awayBattingNotes.update({len(awayBattingNotes): n["label"] + "-" + n["value"]}) + homeBattingNotes = {} for n in boxData["home"]["note"]: homeBattingNotes.update({len(homeBattingNotes): n["label"] + "-" + n["value"]}) @@ -4071,6 +4127,7 @@

Functions

r["dates"][0]["games"][0]["content"]["highlights"]["highlights"]["items"] ): return "" + items = r["dates"][0]["games"][0]["content"]["highlights"]["highlights"]["items"] unorderedHighlights = {} @@ -4264,6 +4321,7 @@

Functions

for k in s.keys(): if k in ["season", "sport"]: continue + if k == "prPortalCalculatedFields": for x in s[k].keys(): pace += "{}: {}\n".format(x, s[k][x]) @@ -4288,6 +4346,7 @@

Functions

params = {} if season: params.update({"season": season}) + if sportId: params.update({"sportId": sportId}) @@ -4449,6 +4508,7 @@

Functions

ep = ENDPOINTS.get(endpoint) if not ep: raise ValueError("Invalid endpoint (" + str(endpoint) + ").") + url = ep["url"] logger.debug("URL: {}".format(url)) @@ -4486,8 +4546,14 @@

Functions

# Replace path parameters with their values for k, v in path_params.items(): logger.debug("Replacing {%s}" % k) - url = url.replace("{" + k + "}", v) + url = url.replace( + "{" + k + "}", + ("/" if ep["path_params"][k]["leading_slash"] else "") + + v + + ("/" if ep["path_params"][k]["trailing_slash"] else ""), + ) logger.debug("URL: {}".format(url)) + while url.find("{") != -1 and url.find("}") > url.find("{"): param = url[url.find("{") + 1 : url.find("}")] if ep.get("path_params", {}).get(param, {}).get("required"): @@ -4513,6 +4579,7 @@

Functions

else: logger.debug("Removing optional param {%s}" % param) url = url.replace("{" + param + "}", "") + logger.debug("URL: {}".format(url)) # Add query parameters to the URL if len(query_params) > 0: @@ -4533,11 +4600,13 @@

Functions

if len(missing_params) == 0: satisfied = True break + if not satisfied and not force: if ep.get("note"): note = "\n--Endpoint note: " + ep.get("note") else: note = "" + raise ValueError( "Missing required parameter(s): " + ", ".join(missing_params) @@ -4634,22 +4703,30 @@

Functions

params = {"leaderCategories": leaderCategories, "sportId": sportId, "limit": limit} if season: params.update({"season": season}) + if statType: params.update({"statType": statType}) + if not season and not statType: params.update( {"season": datetime.now().year} ) # default season to current year if no season or statType provided + if statGroup: if statGroup == "batting": statGroup = "hitting" + params.update({"statGroup": statGroup}) + if gameTypes: params.update({"leaderGameTypes": gameTypes}) + if leagueId: params.update({"leagueId": leagueId}) + if playerPool: params.update({"playerPool": playerPool}) + params.update( { "fields": "leagueLeaders,leaders,rank,value,team,name,league,name,person,fullName" @@ -5044,6 +5121,7 @@

Functions

} if timecode: params.update({"timecode": timecode}) + r = get("game", params) header_name = r["gameData"]["status"]["abstractGameState"] @@ -5118,6 +5196,7 @@

Functions

linescore += ("{:^2}" * (len(k[1]) - 3)).format(*k[1]) linescore += ("{:^4}" * 3).format(*k[1][-3:]) linescore += "\n" + if len(linescore) > 1: linescore = linescore[:-1] # strip the extra line break @@ -5261,6 +5340,7 @@

Functions

if str(lookup_value).lower() in str(v).lower(): teams.append(team) break + return teams @@ -5578,9 +5658,11 @@

Functions

stats += player["first_name"] if player["nickname"]: stats += ' "{nickname}"'.format(**player) + stats += " {last_name}, {position} ({mlb_debut:.4}-".format(**player) if not player["active"]: stats += "{last_played:.4}".format(**player) + stats += ")\n\n" for x in player["stats"]: @@ -5593,11 +5675,13 @@

Functions

) if x["stats"].get("position"): stats += " ({})".format(x["stats"]["position"]["abbreviation"]) + stats += "\n" for y in x["stats"].keys(): if y == "position": continue stats += "{}: {}\n".format(y, x["stats"][y]) + stats += "\n" return stats @@ -5736,6 +5820,7 @@

56 """ if not rosterType: rosterType = "active" + params = {"rosterType": rosterType, "season": season, "teamId": teamId} if date: params.update({"date": date}) @@ -5887,6 +5972,7 @@

56 if end_date and not start_date: date = end_date end_date = None + if start_date and not end_date: date = start_date start_date = None @@ -5918,9 +6004,7 @@

56 games = [] if r.get("totalItems") == 0: - return ( - games - ) # TODO: ValueError('No games to parse from schedule object.') instead? + return games # TODO: ValueError('No games to parse from schedule object.') instead? else: for date in r.get("dates"): for game in date.get("games"): @@ -6262,7 +6346,7 @@

56 standings = "" - for div_id, div in divisions.items(): + for div in divisions.values(): standings += div["div_name"] + "\n" if include_wildcard: standings += "{:^4} {:<21} {:^3} {:^3} {:^4} {:^4} {:^7} {:^5} {:^4}\n".format( @@ -6317,13 +6401,16 @@

56 params = {"leagueId": leagueId} if date: params.update({"date": date}) + if not season: if date: season = date[-4:] else: season = datetime.now().year + if not standingsTypes: standingsTypes = "regularSeason" + params.update({"season": season, "standingsTypes": standingsTypes}) params.update( { @@ -6352,6 +6439,7 @@

56 } } ) + team = { "name": x["team"]["name"], "div_rank": x["divisionRank"], diff --git a/docs/version.html b/docs/version.html index 1d9eb8c..3273472 100644 --- a/docs/version.html +++ b/docs/version.html @@ -26,7 +26,7 @@

Module statsapi.version

#!/usr/bin/env python
 
-VERSION = "0.1.4"
+VERSION = "0.1.5"
From 6f72aa40d3f8787a3403e1b2455904be6b96cbbc Mon Sep 17 00:00:00 2001 From: todd Date: Fri, 8 Nov 2019 21:33:16 -0500 Subject: [PATCH 14/14] Code standardization --- tests/test_get.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_get.py b/tests/test_get.py index 58aeb3a..b660e51 100644 --- a/tests/test_get.py +++ b/tests/test_get.py @@ -64,5 +64,5 @@ def test_get_raises_errors(mocker): with pytest.raises(ValueError): statsapi.get("bar", {"foo": "baz"}) - # need to add test for path requirement not met - # need to add test for required params + # TODO: add test for path requirement not met + # TODO: add test for required params