From 6b035af3a21d5adf724ab6c06ed7d644c85c0c80 Mon Sep 17 00:00:00 2001 From: Anthony Volk Date: Mon, 6 Jul 2026 18:27:36 +0200 Subject: [PATCH 1/5] Deprecate district breakdown dataset selectors --- policyengine_api/openapi_spec.yaml | 3 ++- policyengine_api/routes/economy_routes.py | 17 ++++------------- policyengine_api/services/economy_service.py | 13 ++++++++----- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/policyengine_api/openapi_spec.yaml b/policyengine_api/openapi_spec.yaml index c8daa82ae..0799c8f82 100644 --- a/policyengine_api/openapi_spec.yaml +++ b/policyengine_api/openapi_spec.yaml @@ -888,7 +888,8 @@ paths: type: string - name: include_district_breakdowns in: query - description: Whether to include congressional district breakdowns for US national simulations. + description: Deprecated no-op. Congressional district results are returned automatically for US national and state-level simulations. + deprecated: true required: false schema: type: boolean diff --git a/policyengine_api/routes/economy_routes.py b/policyengine_api/routes/economy_routes.py index d8e9aab53..8507cfe3e 100644 --- a/policyengine_api/routes/economy_routes.py +++ b/policyengine_api/routes/economy_routes.py @@ -53,13 +53,8 @@ def get_economic_impact(country_id: str, policy_id: int, baseline_policy_id: int dataset = options.pop("dataset", "default") time_period = options.pop("time_period") - # Handle district breakdowns - only for US national simulations - include_district_breakdowns_raw = options.pop( - "include_district_breakdowns", "false" - ) - include_district_breakdowns = include_district_breakdowns_raw.lower() == "true" - if include_district_breakdowns and country_id == "us" and region == "us": - dataset = "national-with-breakdowns" + # Deprecated no-op retained for older app-v2 callers. + options.pop("include_district_breakdowns", None) target: Literal["general", "cliff"] = options.pop("target", "general") api_version = options.pop("version", COUNTRY_PACKAGE_VERSIONS.get(country_id)) @@ -125,12 +120,8 @@ def get_budget_window_economic_impact( except (TypeError, ValueError): return _bad_request_response("window_size must be an integer") - include_district_breakdowns_raw = options.pop( - "include_district_breakdowns", "false" - ) - include_district_breakdowns = include_district_breakdowns_raw.lower() == "true" - if include_district_breakdowns and country_id == "us" and region == "us": - dataset = "national-with-breakdowns" + # Deprecated no-op retained for older app-v2 callers. + options.pop("include_district_breakdowns", None) target: Literal["general", "cliff"] = options.pop("target", "general") if target != "general": diff --git a/policyengine_api/services/economy_service.py b/policyengine_api/services/economy_service.py index e79f74404..9142d3c2f 100644 --- a/policyengine_api/services/economy_service.py +++ b/policyengine_api/services/economy_service.py @@ -1168,10 +1168,12 @@ def _validate_us_region(self, region: str) -> None: else: raise ValueError(f"Invalid US region: '{region}'") - # Dataset keywords that are passed directly to the simulation API. - PASSTHROUGH_DATASETS = { + # Deprecated dataset aliases accepted for older app-v2 callers. These no + # longer route to special sim API datasets. + DEPRECATED_BREAKDOWN_DATASETS = { "national-with-breakdowns", "national-with-breakdowns-test", + "national-with-datasets", } DEPRECATED_DATASETS_BY_COUNTRY = { "us": {"cps", "enhanced_cps"}, @@ -1183,6 +1185,8 @@ def _canonical_dataset( ) -> str: if not dataset: return "default" + if dataset in self.DEPRECATED_BREAKDOWN_DATASETS: + return "default" if dataset == get_bundle_default_dataset(country_id): return "default" return dataset @@ -1200,6 +1204,8 @@ def _setup_data( """ if dataset in (None, "", "default"): return None + if dataset in self.DEPRECATED_BREAKDOWN_DATASETS: + return None deprecated_datasets = self.DEPRECATED_DATASETS_BY_COUNTRY.get(country_id, set()) if dataset in deprecated_datasets: @@ -1211,9 +1217,6 @@ def _setup_data( if dataset == get_bundle_default_dataset(country_id): return None - if dataset in self.PASSTHROUGH_DATASETS: - return dataset - if "://" in dataset: return dataset From 93bbb965125002f2f8e134e1a7416243a05d3b65 Mon Sep 17 00:00:00 2001 From: Anthony Volk Date: Mon, 6 Jul 2026 18:27:46 +0200 Subject: [PATCH 2/5] Bump economy impact cache schema --- policyengine_api/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_api/constants.py b/policyengine_api/constants.py index 38fc2c701..745e8e97f 100644 --- a/policyengine_api/constants.py +++ b/policyengine_api/constants.py @@ -173,7 +173,7 @@ def get_bundle_default_dataset_option(country_id: str) -> dict: ) or _resolve_distribution_version(_dist_versions, "policyengine-core", "policyengine") RUNTIME_CACHE_SCHEMA_VERSIONS = { - "economy_impact": 1, + "economy_impact": 2, "report_output": 1, } From 591b189f899bd808e620a712080949bc23a6995f Mon Sep 17 00:00:00 2001 From: Anthony Volk Date: Mon, 6 Jul 2026 18:27:55 +0200 Subject: [PATCH 3/5] Test deprecated district breakdown inputs --- .../test_economy_budget_window_routes.py | 5 ++-- .../routes/test_economy_dataset_validation.py | 29 ++++++++++++++++++- tests/unit/services/test_economy_service.py | 21 ++++++++++---- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/tests/to_refactor/python/test_economy_budget_window_routes.py b/tests/to_refactor/python/test_economy_budget_window_routes.py index 9821502d7..b9fb57d0d 100644 --- a/tests/to_refactor/python/test_economy_budget_window_routes.py +++ b/tests/to_refactor/python/test_economy_budget_window_routes.py @@ -178,7 +178,7 @@ def test_budget_window_route_passes_version_to_service( @patch( "policyengine_api.routes.economy_routes.economy_service.get_budget_window_economic_impact" ) -def test_budget_window_route_uses_breakdown_dataset_for_us_national_request( +def test_budget_window_route_ignores_deprecated_breakdown_flag( mock_get_budget_window_economic_impact, rest_client ): mock_get_budget_window_economic_impact.return_value = _mock_budget_window_result() @@ -192,8 +192,7 @@ def test_budget_window_route_uses_breakdown_dataset_for_us_national_request( assert response.status_code == 200 mock_get_budget_window_economic_impact.assert_called_once() assert ( - mock_get_budget_window_economic_impact.call_args.kwargs["dataset"] - == "national-with-breakdowns" + mock_get_budget_window_economic_impact.call_args.kwargs["dataset"] == "default" ) diff --git a/tests/unit/routes/test_economy_dataset_validation.py b/tests/unit/routes/test_economy_dataset_validation.py index d3b63421b..5b533eef7 100644 --- a/tests/unit/routes/test_economy_dataset_validation.py +++ b/tests/unit/routes/test_economy_dataset_validation.py @@ -1,11 +1,20 @@ import json -from unittest.mock import patch +from unittest.mock import Mock, patch from flask import Flask from policyengine_api.routes.economy_routes import economy_bp +def _mock_economic_result(): + mock_result = Mock() + mock_result.to_dict.return_value = { + "status": "ok", + "data": {"congressional_district_impact": {"districts": []}}, + } + return mock_result + + def _client_with_economy_blueprint(): app = Flask(__name__) app.config["TESTING"] = True @@ -32,6 +41,24 @@ def test_economy_route_returns_bad_request_for_dataset_validation_error( assert "enhanced_cps" in payload["message"] +@patch("policyengine_api.routes.economy_routes.economy_service.get_economic_impact") +def test_economy_route_ignores_deprecated_breakdown_flag(mock_get_economic_impact): + mock_get_economic_impact.return_value = _mock_economic_result() + client = _client_with_economy_blueprint() + + response = client.get( + "/us/economy/123/over/456" + "?region=us&time_period=2026&include_district_breakdowns=true" + ) + payload = json.loads(response.data) + + assert response.status_code == 200 + assert payload["status"] == "ok" + mock_get_economic_impact.assert_called_once() + assert mock_get_economic_impact.call_args.kwargs["dataset"] == "default" + assert mock_get_economic_impact.call_args.kwargs["options"] == {} + + @patch( "policyengine_api.routes.economy_routes.economy_service.get_budget_window_economic_impact" ) diff --git a/tests/unit/services/test_economy_service.py b/tests/unit/services/test_economy_service.py index 389491256..e2fa54032 100644 --- a/tests/unit/services/test_economy_service.py +++ b/tests/unit/services/test_economy_service.py @@ -1980,20 +1980,24 @@ def test__given_invalid_country_default__omits_data(self, mock_logger): result = service._setup_data("invalid", "region") assert result is None - def test__given_passthrough_dataset__returns_dataset_directly(self): + def test__given_deprecated_breakdown_dataset__omits_data(self): service = EconomyService() result = service._setup_data("us", "us", dataset="national-with-breakdowns") - assert result == "national-with-breakdowns" + assert result is None - def test__given_passthrough_test_dataset__returns_dataset_directly( + def test__given_deprecated_breakdown_test_dataset__omits_data( self, ): - # Test with passthrough test dataset service = EconomyService() result = service._setup_data( "us", "us", dataset="national-with-breakdowns-test" ) - assert result == "national-with-breakdowns-test" + assert result is None + + def test__given_deprecated_national_with_datasets__omits_data(self): + service = EconomyService() + result = service._setup_data("us", "us", dataset="national-with-datasets") + assert result is None def test__given_explicit_us_enhanced_cps__raises_value_error(self): service = EconomyService() @@ -2044,10 +2048,17 @@ def test__given_bundle_default_dataset_name__canonicalizes_setup_identity(self): **common_args, dataset="populace_us_2024", ) + deprecated_breakdown_setup = service._build_economic_impact_setup_options( + **common_args, + dataset="national-with-breakdowns", + ) assert bundle_default_setup.dataset == "default" assert bundle_default_setup.data_version is None assert bundle_default_setup.options_hash == default_setup.options_hash + assert deprecated_breakdown_setup.dataset == "default" + assert deprecated_breakdown_setup.data_version is None + assert deprecated_breakdown_setup.options_hash == default_setup.options_hash def test__given_unknown_dataset__passes_through_legacy_designator(self): service = EconomyService() From 47cc3c22cbfb01900c7a6665dba53356e510f59a Mon Sep 17 00:00:00 2001 From: Anthony Volk Date: Mon, 6 Jul 2026 20:15:20 +0200 Subject: [PATCH 4/5] Add changelog for district breakdown deprecation --- changelog.d/deprecate-national-with-breakdowns.changed.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog.d/deprecate-national-with-breakdowns.changed.md diff --git a/changelog.d/deprecate-national-with-breakdowns.changed.md b/changelog.d/deprecate-national-with-breakdowns.changed.md new file mode 100644 index 000000000..78e35a3e4 --- /dev/null +++ b/changelog.d/deprecate-national-with-breakdowns.changed.md @@ -0,0 +1,4 @@ +Deprecated the `national-with-breakdowns`, `national-with-breakdowns-test`, and +`national-with-datasets` economy dataset aliases. API v1 now treats these as +the default certified dataset and ignores the old district-breakdown query flag, +so congressional district output can pass through from the sim API. From 1735f685aee73fc6d6d38450e303e12c76074685 Mon Sep 17 00:00:00 2001 From: Anthony Volk Date: Tue, 7 Jul 2026 16:35:09 +0200 Subject: [PATCH 5/5] Address district breakdown PR review notes --- policyengine_api/openapi_spec.yaml | 8 ++++++++ tests/unit/routes/test_economy_dataset_validation.py | 1 + 2 files changed, 9 insertions(+) diff --git a/policyengine_api/openapi_spec.yaml b/policyengine_api/openapi_spec.yaml index 0799c8f82..805426bfd 100644 --- a/policyengine_api/openapi_spec.yaml +++ b/policyengine_api/openapi_spec.yaml @@ -773,6 +773,14 @@ paths: required: false schema: type: string + - name: include_district_breakdowns + in: query + description: Deprecated no-op. Congressional district results are returned automatically for US national and state-level simulations. + deprecated: true + required: false + schema: + type: boolean + default: false responses: 200: description: Calculating economic impact. diff --git a/tests/unit/routes/test_economy_dataset_validation.py b/tests/unit/routes/test_economy_dataset_validation.py index 5b533eef7..c931fb274 100644 --- a/tests/unit/routes/test_economy_dataset_validation.py +++ b/tests/unit/routes/test_economy_dataset_validation.py @@ -54,6 +54,7 @@ def test_economy_route_ignores_deprecated_breakdown_flag(mock_get_economic_impac assert response.status_code == 200 assert payload["status"] == "ok" + assert payload["result"]["congressional_district_impact"] == {"districts": []} mock_get_economic_impact.assert_called_once() assert mock_get_economic_impact.call_args.kwargs["dataset"] == "default" assert mock_get_economic_impact.call_args.kwargs["options"] == {}