From f554c8e49bda1b09b959a1d71f9e78326399e24b Mon Sep 17 00:00:00 2001 From: Cris Lom <36197748+Lombardoc4@users.noreply.github.com> Date: Wed, 6 Dec 2023 21:33:52 -0500 Subject: [PATCH 1/8] Update interface.py Testing fork --- fastf1/ergast/interface.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fastf1/ergast/interface.py b/fastf1/ergast/interface.py index aa0a17286..67cb9e314 100644 --- a/fastf1/ergast/interface.py +++ b/fastf1/ergast/interface.py @@ -778,8 +778,7 @@ def get_driver_info( 'fastest_rank': fastest_rank, 'status': status} - return self._build_default_result(endpoint='drivers', - table='DriverTable', + return self._build_default_result(table='DriverTable', category=API.Drivers, subcategory=None, result_type=result_type, @@ -845,8 +844,7 @@ def get_constructor_info( 'fastest_rank': fastest_rank, 'status': status} - return self._build_default_result(endpoint='constructors', - table='ConstructorTable', + return self._build_default_result(table='ConstructorTable', category=API.Constructors, subcategory=None, result_type=result_type, From bb389a6343075ab01877c2eac76102f9d378b4d1 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Fri, 8 Dec 2023 15:49:37 -0500 Subject: [PATCH 2/8] if endpoint selector includes more than season and/or year dont append final endpoint val --- fastf1/ergast/interface.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/fastf1/ergast/interface.py b/fastf1/ergast/interface.py index 67cb9e314..936220a77 100644 --- a/fastf1/ergast/interface.py +++ b/fastf1/ergast/interface.py @@ -434,6 +434,7 @@ def _build_url( standings_position: Optional[int] = None ) -> str: selectors = list() + baseJson = True if season is not None: selectors.append(f"/{season}") @@ -441,23 +442,31 @@ def _build_url( selectors.append(f"/{round}") if circuit is not None: selectors.append(f"/circuits/{circuit}") + baseJson = not baseJson if constructor is not None: selectors.append(f"/constructors/{constructor}") + baseJson = not baseJson if driver is not None: + baseJson = not baseJson selectors.append(f"/drivers/{driver}") if grid_position is not None: + baseJson = not baseJson selectors.append(f"/grid/{grid_position}") if results_position is not None: + baseJson = not baseJson selectors.append(f"/results/{results_position}") if fastest_rank is not None: + baseJson = not baseJson selectors.append(f"/fastest/{fastest_rank}") if status is not None: + baseJson = not baseJson selectors.append(f"/status/{status}") # some special cases: the endpoint may also be used as selector # therefore, if the specifier is defined, do not add the endpoint # string additionally if standings_position is not None: + baseJson = not baseJson if endpoint == 'driverStandings': selectors.append(f"/driverStandings/{standings_position}") endpoint = None @@ -466,18 +475,23 @@ def _build_url( endpoint = None if lap_number is not None: + baseJson = not baseJson selectors.append(f"/laps/{lap_number}") if endpoint == 'laps': endpoint = None if stop_number is not None: + baseJson = not baseJson selectors.append(f"/pitstops/{stop_number}") if endpoint == 'pitstops': endpoint = None - if endpoint is not None: + # This needs very special cases + # Exclusively for season & circuit only, not additional selectors + if endpoint is not None and baseJson: selectors.append(f"/{endpoint}") + return BASE_URL + "".join(selectors) + ".json" @classmethod @@ -778,7 +792,8 @@ def get_driver_info( 'fastest_rank': fastest_rank, 'status': status} - return self._build_default_result(table='DriverTable', + return self._build_default_result(endpoint='drivers', + table='DriverTable', category=API.Drivers, subcategory=None, result_type=result_type, @@ -844,7 +859,8 @@ def get_constructor_info( 'fastest_rank': fastest_rank, 'status': status} - return self._build_default_result(table='ConstructorTable', + return self._build_default_result(endpoint="constructors", + table='ConstructorTable', category=API.Constructors, subcategory=None, result_type=result_type, From b8ca79e939f1d705df5058d13c1eb5895a8d0f26 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Mon, 18 Dec 2023 11:15:56 -0500 Subject: [PATCH 3/8] mimicking laps endpoint behavior to omit endpoint value from url in status, driver, constructors, and circuit --- fastf1/ergast/interface.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/fastf1/ergast/interface.py b/fastf1/ergast/interface.py index 936220a77..194bfbf92 100644 --- a/fastf1/ergast/interface.py +++ b/fastf1/ergast/interface.py @@ -228,6 +228,7 @@ class ErgastRawResponse(ErgastResponseMixin, list): auto_cast: Determines if values are automatically cast to the most appropriate data type from their original string representation """ + def __init__(self, *, query_result, category, auto_cast, **kwargs): if auto_cast: query_result = self._prepare_response(query_result, category) @@ -351,6 +352,7 @@ class ErgastMultiResponse(ErgastResponseMixin): auto_cast: Flag that enables or disables automatic casting from the original string representation to the most suitable data type. """ + def __init__(self, *args, response_description: dict, response_data: list, @@ -409,6 +411,7 @@ class Ergast: 30 if not set. Maximum: 1000. See also "Response Paging" on https://ergast.com/mrd/. """ + def __init__(self, result_type: Literal['raw', 'pandas'] = 'pandas', auto_cast: bool = True, @@ -434,7 +437,6 @@ def _build_url( standings_position: Optional[int] = None ) -> str: selectors = list() - baseJson = True if season is not None: selectors.append(f"/{season}") @@ -442,31 +444,27 @@ def _build_url( selectors.append(f"/{round}") if circuit is not None: selectors.append(f"/circuits/{circuit}") - baseJson = not baseJson + endpoint = None if constructor is not None: selectors.append(f"/constructors/{constructor}") - baseJson = not baseJson + endpoint = None if driver is not None: - baseJson = not baseJson selectors.append(f"/drivers/{driver}") + endpoint = None if grid_position is not None: - baseJson = not baseJson selectors.append(f"/grid/{grid_position}") if results_position is not None: - baseJson = not baseJson selectors.append(f"/results/{results_position}") if fastest_rank is not None: - baseJson = not baseJson selectors.append(f"/fastest/{fastest_rank}") if status is not None: - baseJson = not baseJson selectors.append(f"/status/{status}") + endpoint = None # some special cases: the endpoint may also be used as selector # therefore, if the specifier is defined, do not add the endpoint # string additionally if standings_position is not None: - baseJson = not baseJson if endpoint == 'driverStandings': selectors.append(f"/driverStandings/{standings_position}") endpoint = None @@ -475,23 +473,18 @@ def _build_url( endpoint = None if lap_number is not None: - baseJson = not baseJson selectors.append(f"/laps/{lap_number}") if endpoint == 'laps': endpoint = None if stop_number is not None: - baseJson = not baseJson selectors.append(f"/pitstops/{stop_number}") if endpoint == 'pitstops': endpoint = None - # This needs very special cases - # Exclusively for season & circuit only, not additional selectors - if endpoint is not None and baseJson: + if endpoint is not None: selectors.append(f"/{endpoint}") - return BASE_URL + "".join(selectors) + ".json" @classmethod From 7e843651fc5f3dd9c77613c65a5e4c3e9d2aabb6 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Mon, 18 Dec 2023 12:57:22 -0500 Subject: [PATCH 4/8] additional check for endpoint value to further scope removal in base url --- fastf1/ergast/interface.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fastf1/ergast/interface.py b/fastf1/ergast/interface.py index 194bfbf92..17be8c9a7 100644 --- a/fastf1/ergast/interface.py +++ b/fastf1/ergast/interface.py @@ -444,13 +444,16 @@ def _build_url( selectors.append(f"/{round}") if circuit is not None: selectors.append(f"/circuits/{circuit}") - endpoint = None + if endpoint == 'circuits': + endpoint = None if constructor is not None: selectors.append(f"/constructors/{constructor}") - endpoint = None + if endpoint == 'constructors': + endpoint = None if driver is not None: selectors.append(f"/drivers/{driver}") - endpoint = None + if endpoint == 'driver': + endpoint = None if grid_position is not None: selectors.append(f"/grid/{grid_position}") if results_position is not None: @@ -459,7 +462,8 @@ def _build_url( selectors.append(f"/fastest/{fastest_rank}") if status is not None: selectors.append(f"/status/{status}") - endpoint = None + if endpoint == 'status': + endpoint = None # some special cases: the endpoint may also be used as selector # therefore, if the specifier is defined, do not add the endpoint From b6bb5cb37a5ce4b2165145a1f36d95e2633d5b64 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Tue, 19 Dec 2023 10:00:23 -0500 Subject: [PATCH 5/8] appending base_url special cases with proper format --- fastf1/ergast/interface.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fastf1/ergast/interface.py b/fastf1/ergast/interface.py index 17be8c9a7..e9168bf91 100644 --- a/fastf1/ergast/interface.py +++ b/fastf1/ergast/interface.py @@ -442,32 +442,36 @@ def _build_url( selectors.append(f"/{season}") if round is not None: selectors.append(f"/{round}") + if grid_position is not None: + selectors.append(f"/grid/{grid_position}") + if results_position is not None: + selectors.append(f"/results/{results_position}") + if fastest_rank is not None: + selectors.append(f"/fastest/{fastest_rank}") + + # some special cases: the endpoint may also be used as selector + # therefore, if the specifier is defined, do not add the endpoint + # string additionally if circuit is not None: selectors.append(f"/circuits/{circuit}") if endpoint == 'circuits': endpoint = None + if constructor is not None: selectors.append(f"/constructors/{constructor}") if endpoint == 'constructors': endpoint = None + if driver is not None: selectors.append(f"/drivers/{driver}") if endpoint == 'driver': endpoint = None - if grid_position is not None: - selectors.append(f"/grid/{grid_position}") - if results_position is not None: - selectors.append(f"/results/{results_position}") - if fastest_rank is not None: - selectors.append(f"/fastest/{fastest_rank}") + if status is not None: selectors.append(f"/status/{status}") if endpoint == 'status': endpoint = None - # some special cases: the endpoint may also be used as selector - # therefore, if the specifier is defined, do not add the endpoint - # string additionally if standings_position is not None: if endpoint == 'driverStandings': selectors.append(f"/driverStandings/{standings_position}") From aa72579e49ab1e001a5c0b91dbf727fa5abd46a4 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Wed, 20 Dec 2023 15:07:07 -0500 Subject: [PATCH 6/8] specify endpoint is the final selector for the ergast url --- fastf1/ergast/interface.py | 52 +++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/fastf1/ergast/interface.py b/fastf1/ergast/interface.py index e9168bf91..3d2996292 100644 --- a/fastf1/ergast/interface.py +++ b/fastf1/ergast/interface.py @@ -438,8 +438,11 @@ def _build_url( ) -> str: selectors = list() + # ! Need to add endpoint if additional filters otherwise do not + # https://ergast.com/mrd/methods/schedule/ if season is not None: selectors.append(f"/{season}") + if round is not None: selectors.append(f"/{round}") if grid_position is not None: @@ -452,43 +455,52 @@ def _build_url( # some special cases: the endpoint may also be used as selector # therefore, if the specifier is defined, do not add the endpoint # string additionally - if circuit is not None: - selectors.append(f"/circuits/{circuit}") - if endpoint == 'circuits': - endpoint = None + if driver is not None: + if endpoint == 'driver': + endpoint = f"/drivers/{driver}" + else: + selectors.append(f"/drivers/{driver}") if constructor is not None: - selectors.append(f"/constructors/{constructor}") if endpoint == 'constructors': - endpoint = None + endpoint = f"/constructors/{constructor}" + else: + selectors.append(f"/constructors/{constructor}") - if driver is not None: - selectors.append(f"/drivers/{driver}") - if endpoint == 'driver': - endpoint = None + if circuit is not None: + if endpoint == 'circuits': + endpoint = f"/circuits/{circuit}" + else: + selectors.append(f"/circuits/{circuit}") if status is not None: - selectors.append(f"/status/{status}") if endpoint == 'status': - endpoint = None + endpoint = f"/status/{status}" + else: + selectors.append(f"/status/{status}") if standings_position is not None: if endpoint == 'driverStandings': - selectors.append(f"/driverStandings/{standings_position}") - endpoint = None + endpoint = f"/driverStandings/{standings_position}" elif endpoint == 'constructorStandings': - selectors.append(f"/constructorStandings/{standings_position}") - endpoint = None + endpoint = f"/constructorStandings/{standings_position}" if lap_number is not None: - selectors.append(f"/laps/{lap_number}") if endpoint == 'laps': - endpoint = None + endpoint = f"/laps/{lap_number}" + else: + selectors.append(f"/laps/{lap_number}") if stop_number is not None: - selectors.append(f"/pitstops/{stop_number}") if endpoint == 'pitstops': - endpoint = None + endpoint = f"/pitstops/{stop_number}" + else: + selectors.append(f"/pitstops/{stop_number}") + + # Special case for race_schedule + # If no additional filters besides required (season) exclude endpoint + if endpoint == 'races' and len(selectors) == 1: + endpoint = None if endpoint is not None: selectors.append(f"/{endpoint}") From df14659f9e474e6f6101e0f449a104544712bdd9 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Wed, 20 Dec 2023 15:20:00 -0500 Subject: [PATCH 7/8] omit race_schedule conditional --- fastf1/ergast/interface.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fastf1/ergast/interface.py b/fastf1/ergast/interface.py index 3d2996292..44124498c 100644 --- a/fastf1/ergast/interface.py +++ b/fastf1/ergast/interface.py @@ -438,11 +438,8 @@ def _build_url( ) -> str: selectors = list() - # ! Need to add endpoint if additional filters otherwise do not - # https://ergast.com/mrd/methods/schedule/ if season is not None: selectors.append(f"/{season}") - if round is not None: selectors.append(f"/{round}") if grid_position is not None: @@ -499,8 +496,8 @@ def _build_url( # Special case for race_schedule # If no additional filters besides required (season) exclude endpoint - if endpoint == 'races' and len(selectors) == 1: - endpoint = None + # if endpoint == 'races' and len(selectors) == 1: + # endpoint = None if endpoint is not None: selectors.append(f"/{endpoint}") From 893f39641894c0bc4074a79d426fc4589b986878 Mon Sep 17 00:00:00 2001 From: theOehrly <23384863+theOehrly@users.noreply.github.com> Date: Tue, 26 Dec 2023 15:40:52 +0100 Subject: [PATCH 8/8] minor fixes, add more tests --- fastf1/ergast/interface.py | 23 +++++------- fastf1/tests/test_ergast.py | 70 +++++++++++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/fastf1/ergast/interface.py b/fastf1/ergast/interface.py index 44124498c..674a68e9d 100644 --- a/fastf1/ergast/interface.py +++ b/fastf1/ergast/interface.py @@ -453,52 +453,47 @@ def _build_url( # therefore, if the specifier is defined, do not add the endpoint # string additionally if driver is not None: - if endpoint == 'driver': - endpoint = f"/drivers/{driver}" + if endpoint == 'drivers': + endpoint = f"drivers/{driver}" else: selectors.append(f"/drivers/{driver}") if constructor is not None: if endpoint == 'constructors': - endpoint = f"/constructors/{constructor}" + endpoint = f"constructors/{constructor}" else: selectors.append(f"/constructors/{constructor}") if circuit is not None: if endpoint == 'circuits': - endpoint = f"/circuits/{circuit}" + endpoint = f"circuits/{circuit}" else: selectors.append(f"/circuits/{circuit}") if status is not None: if endpoint == 'status': - endpoint = f"/status/{status}" + endpoint = f"status/{status}" else: selectors.append(f"/status/{status}") if standings_position is not None: if endpoint == 'driverStandings': - endpoint = f"/driverStandings/{standings_position}" + endpoint = f"driverStandings/{standings_position}" elif endpoint == 'constructorStandings': - endpoint = f"/constructorStandings/{standings_position}" + endpoint = f"constructorStandings/{standings_position}" if lap_number is not None: if endpoint == 'laps': - endpoint = f"/laps/{lap_number}" + endpoint = f"laps/{lap_number}" else: selectors.append(f"/laps/{lap_number}") if stop_number is not None: if endpoint == 'pitstops': - endpoint = f"/pitstops/{stop_number}" + endpoint = f"pitstops/{stop_number}" else: selectors.append(f"/pitstops/{stop_number}") - # Special case for race_schedule - # If no additional filters besides required (season) exclude endpoint - # if endpoint == 'races' and len(selectors) == 1: - # endpoint = None - if endpoint is not None: selectors.append(f"/{endpoint}") diff --git a/fastf1/tests/test_ergast.py b/fastf1/tests/test_ergast.py index d355835db..85619a7ad 100644 --- a/fastf1/tests/test_ergast.py +++ b/fastf1/tests/test_ergast.py @@ -313,30 +313,78 @@ def test_merge_dicts_of_lists(data, expected): @pytest.mark.parametrize( "endpoint, selectors, expected", ( - # "normal" behaviour for most endpoints + # "simple" behaviour ['seasons', {}, "https://ergast.com/api/f1/seasons.json"], - ['circuits', {'driver': 'alonso', 'constructor': 'alpine'}, - "https://ergast.com/api/f1/constructors/alpine/" - "drivers/alonso/circuits.json"], + ['races', {'season': 2022}, + "https://ergast.com/api/f1/2022/races.json"], + + ['results', {'season': 2022, 'round': '10'}, + "https://ergast.com/api/f1/2022/10/results.json"], + + ['sprint', {'season': 2022, 'round': '4'}, + "https://ergast.com/api/f1/2022/4/sprint.json"], + + ['qualifying', {'season': 2022, 'round': '10'}, + "https://ergast.com/api/f1/2022/10/qualifying.json"], + + # special cases where endpoint name matches selector and the endpoint + # gets extended with its selection + ['drivers', {}, + "https://ergast.com/api/f1/drivers.json"], + + ['drivers', {'driver': 'alonso'}, + "https://ergast.com/api/f1/drivers/alonso.json"], + + ['constructors', {}, + "https://ergast.com/api/f1/constructors.json"], + + ['constructors', {'constructor': 'ferrari'}, + "https://ergast.com/api/f1/constructors/ferrari.json"], + + ['circuits', {}, + "https://ergast.com/api/f1/circuits.json"], + + ['circuits', {'circuit': 'monza'}, + "https://ergast.com/api/f1/circuits/monza.json"], + + ['status', {}, + "https://ergast.com/api/f1/status.json"], + + ['status', {'status': '1'}, + "https://ergast.com/api/f1/status/1.json"], + + ['driverStandings', {'season': 2022}, + "https://ergast.com/api/f1/2022/driverStandings.json"], + + ['driverStandings', {'season': 2022, 'standings_position': '1'}, + "https://ergast.com/api/f1/2022/driverStandings/1.json"], + + ['constructorStandings', {'season': 2022}, + "https://ergast.com/api/f1/2022/constructorStandings.json"], + + ['constructorStandings', {'season': 2022, 'standings_position': '1'}, + "https://ergast.com/api/f1/2022/constructorStandings/1.json"], - # special case where endpoint name matches selector ['laps', {'season': 2022, 'round': 10}, "https://ergast.com/api/f1/2022/10/laps.json"], ['laps', {'season': 2022, 'round': 10, 'lap_number': 1}, "https://ergast.com/api/f1/2022/10/laps/1.json"], + ['pitstops', {'season': 2022, 'round': 10}, + "https://ergast.com/api/f1/2022/10/pitstops.json"], + + ['pitstops', {'season': 2022, 'round': 10, 'stop_number': '1'}, + "https://ergast.com/api/f1/2022/10/pitstops/1.json"], + # endpoint/selector combination in other request ['pitstops', {'season': 2022, 'round': 10, 'lap_number': 1}, "https://ergast.com/api/f1/2022/10/laps/1/pitstops.json"], - # combined selector standings_position - ['driverStandings', {'season': 2022, 'standings_position': 1}, - "https://ergast.com/api/f1/2022/driverStandings/1.json"], - - ['constructorStandings', {'season': 2022, 'standings_position': 3}, - "https://ergast.com/api/f1/2022/constructorStandings/3.json"] + ['circuits', {'driver': 'alonso', 'constructor': 'alpine'}, + "https://ergast.com/api/f1/drivers/alonso/" + "constructors/alpine/circuits.json"], ) ) def test_ergast_build_url(endpoint: str, selectors: dict, expected: str):