diff --git a/src/nba_api/stats/library/http.py b/src/nba_api/stats/library/http.py index 5a98136f..68f71ea5 100644 --- a/src/nba_api/stats/library/http.py +++ b/src/nba_api/stats/library/http.py @@ -26,6 +26,10 @@ class NBAStatsResponse(http.NBAResponse): """Response handler for NBA Stats API requests.""" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._endpoint = None + def get_normalized_dict(self): raw_data = self.get_dict() @@ -55,6 +59,26 @@ def get_normalized_dict(self): row[headers[i]] = raw_row[i] rows.append(row) data[name] = rows + elif self._endpoint is not None: + try: + from nba_api.stats.endpoints._parsers import get_parser_for_endpoint + + endpoint_parser = get_parser_for_endpoint(self._endpoint, raw_data) + data_sets = endpoint_parser.get_data_sets() + + for name, dataset in data_sets.items(): + headers = dataset["headers"] + row_data = dataset["data"] + + rows = [] + for raw_row in row_data: + row = {} + for i in range(len(headers)): + row[headers[i]] = raw_row[i] + rows.append(row) + data[name] = rows + except (KeyError, ImportError): + pass return data @@ -96,8 +120,10 @@ def get_headers_from_data_sets(self): def get_data_sets(self, endpoint=None): raw_dict = self.get_dict() + if endpoint is not None: + self._endpoint = endpoint + if endpoint is None: - # Process Tabular Json if "resultSets" in raw_dict: results = raw_dict["resultSets"] else: @@ -119,8 +145,6 @@ def get_data_sets(self, endpoint=None): for result_set in results } else: - # Process V3 endpoint with custom parser - # Lazy import to avoid circular dependency from nba_api.stats.endpoints._parsers import get_parser_for_endpoint endpoint_parser = get_parser_for_endpoint(endpoint, self.get_dict()) diff --git a/tests/unit/stats/endpoints/test_boxscoretraditionalv3_normalized.py b/tests/unit/stats/endpoints/test_boxscoretraditionalv3_normalized.py new file mode 100644 index 00000000..520927be --- /dev/null +++ b/tests/unit/stats/endpoints/test_boxscoretraditionalv3_normalized.py @@ -0,0 +1,144 @@ +import json +import pytest +from nba_api.stats.endpoints.boxscoretraditionalv3 import BoxScoreTraditionalV3 +from nba_api.stats.library.http import NBAStatsResponse +from .data.boxscoretraditionalv3 import BOXSCORETRADITIONALV3_SAMPLE + + +class MockResponse(NBAStatsResponse): + + def __init__(self, data): + super().__init__(json.dumps(data), 200, "http://mock.url") + self._mock_data = data + + def get_dict(self): + return self._mock_data + + +class TestBoxScoreTraditionalV3Normalized: + + def test_get_normalized_dict_returns_data(self): + endpoint = BoxScoreTraditionalV3(game_id="0022500165", get_request=False) + endpoint.nba_response = MockResponse(BOXSCORETRADITIONALV3_SAMPLE) + endpoint.load_response() + + result = endpoint.get_normalized_dict() + + assert isinstance(result, dict) + assert len(result) > 0, "get_normalized_dict() should not return empty dict" + + assert "PlayerStats" in result + assert "TeamStats" in result + assert "TeamStarterBenchStats" in result + + def test_get_normalized_dict_structure(self): + endpoint = BoxScoreTraditionalV3(game_id="0022500165", get_request=False) + endpoint.nba_response = MockResponse(BOXSCORETRADITIONALV3_SAMPLE) + endpoint.load_response() + + result = endpoint.get_normalized_dict() + + assert isinstance(result["PlayerStats"], list) + assert isinstance(result["TeamStats"], list) + assert isinstance(result["TeamStarterBenchStats"], list) + + if result["PlayerStats"]: + first_player = result["PlayerStats"][0] + assert isinstance(first_player, dict) + assert "gameId" in first_player + assert "personId" in first_player + assert "firstName" in first_player + assert "points" in first_player + + if result["TeamStats"]: + first_team = result["TeamStats"][0] + assert isinstance(first_team, dict) + assert "gameId" in first_team + assert "teamId" in first_team + assert "teamName" in first_team + assert "points" in first_team + + def test_get_normalized_json_returns_data(self): + endpoint = BoxScoreTraditionalV3(game_id="0022500165", get_request=False) + endpoint.nba_response = MockResponse(BOXSCORETRADITIONALV3_SAMPLE) + endpoint.load_response() + + result = endpoint.get_normalized_json() + + assert isinstance(result, str) + assert len(result) > 2, "get_normalized_json() should not return empty JSON" + assert result != "{}", "get_normalized_json() should not return empty object" + + parsed = json.loads(result) + assert isinstance(parsed, dict) + assert len(parsed) > 0 + + def test_get_normalized_json_is_valid_json(self): + endpoint = BoxScoreTraditionalV3(game_id="0022500165", get_request=False) + endpoint.nba_response = MockResponse(BOXSCORETRADITIONALV3_SAMPLE) + endpoint.load_response() + + json_str = endpoint.get_normalized_json() + parsed = json.loads(json_str) + + dict_result = endpoint.get_normalized_dict() + assert parsed == dict_result + + def test_get_normalized_dict_player_data_correctness(self): + endpoint = BoxScoreTraditionalV3(game_id="0022500165", get_request=False) + endpoint.nba_response = MockResponse(BOXSCORETRADITIONALV3_SAMPLE) + endpoint.load_response() + + result = endpoint.get_normalized_dict() + player_stats = result["PlayerStats"] + + assert len(player_stats) == 2 + + tatum = player_stats[0] + assert tatum["gameId"] == "0022500165" + assert tatum["teamId"] == 1610612738 + assert tatum["personId"] == 1630162 + assert tatum["firstName"] == "Jayson" + assert tatum["familyName"] == "Tatum" + assert tatum["points"] == 32 + assert tatum["plusMinusPoints"] == 12 + + def test_get_normalized_dict_team_data_correctness(self): + endpoint = BoxScoreTraditionalV3(game_id="0022500165", get_request=False) + endpoint.nba_response = MockResponse(BOXSCORETRADITIONALV3_SAMPLE) + endpoint.load_response() + + result = endpoint.get_normalized_dict() + team_stats = result["TeamStats"] + + assert len(team_stats) == 2 + + celtics = team_stats[0] + assert celtics["gameId"] == "0022500165" + assert celtics["teamId"] == 1610612738 + assert celtics["teamCity"] == "Boston" + assert celtics["teamName"] == "Celtics" + assert celtics["points"] == 122 + assert celtics["plusMinusPoints"] == 14 + + warriors = team_stats[1] + assert warriors["teamId"] == 1610612744 + assert warriors["teamCity"] == "Golden State" + assert warriors["points"] == 108 + assert warriors["plusMinusPoints"] == -14 + + def test_regression_issue_602(self): + endpoint = BoxScoreTraditionalV3(game_id="0022500165", get_request=False) + endpoint.nba_response = MockResponse(BOXSCORETRADITIONALV3_SAMPLE) + endpoint.load_response() + + normalized_dict = endpoint.get_normalized_dict() + assert normalized_dict != {}, "Issue #602: get_normalized_dict() returns empty dict" + assert len(normalized_dict) > 0, "Issue #602: get_normalized_dict() has no data" + + normalized_json = endpoint.get_normalized_json() + assert normalized_json != "{}", "Issue #602: get_normalized_json() returns empty JSON" + assert len(normalized_json) > 2, "Issue #602: get_normalized_json() has no data" + + parsed = json.loads(normalized_json) + assert len(parsed) > 0, "Issue #602: Parsed JSON is empty"