diff --git a/changelog.d/pure-leaf-inputs.md b/changelog.d/pure-leaf-inputs.md new file mode 100644 index 00000000000..da130bc9f72 --- /dev/null +++ b/changelog.d/pure-leaf-inputs.md @@ -0,0 +1 @@ +- Removed non-geographic guards from input variables so they remain pure data inputs, and added checks preventing input variables from using formulas, adds, subtracts, or non-geographic `defined_for` gates. diff --git a/policyengine_us/tests/core/test_input_variable_definitions.py b/policyengine_us/tests/core/test_input_variable_definitions.py new file mode 100644 index 00000000000..02883e15b7c --- /dev/null +++ b/policyengine_us/tests/core/test_input_variable_definitions.py @@ -0,0 +1,63 @@ +from policyengine_us import CountryTaxBenefitSystem +from policyengine_us.model_api import STATES + + +def _is_geographic_defined_for(defined_for: str) -> bool: + return defined_for in STATES or defined_for.startswith("in_") + + +def _state_prefix(name): + for state in STATES: + if name.startswith(f"{state.lower()}_"): + return state + return None + + +def test_input_variables_do_not_use_non_geographic_defined_for(): + system = CountryTaxBenefitSystem() + + invalid = { + name: variable.defined_for + for name, variable in system.variables.items() + if variable.is_input_variable() + and variable.defined_for is not None + and not _is_geographic_defined_for(variable.defined_for) + } + + assert invalid == {} + + +def test_input_variables_do_not_use_formulas_adds_or_subtracts(): + system = CountryTaxBenefitSystem() + + invalid = { + name: { + "formulas": bool(getattr(variable, "formulas", None)), + "adds": bool(getattr(variable, "adds", None)), + "subtracts": bool(getattr(variable, "subtracts", None)), + } + for name, variable in system.variables.items() + if variable.is_input_variable() + and ( + getattr(variable, "formulas", None) + or getattr(variable, "adds", None) + or getattr(variable, "subtracts", None) + ) + } + + assert invalid == {} + + +def test_state_input_variables_match_state_defined_for(): + system = CountryTaxBenefitSystem() + + invalid = { + name: variable.defined_for + for name, variable in system.variables.items() + if variable.is_input_variable() + and _state_prefix(name) is not None + and variable.defined_for in STATES + and variable.defined_for != _state_prefix(name) + } + + assert invalid == {} diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/nyserda/drive_clean_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/nyserda/drive_clean_rebate.yaml index f9f0d07fd13..828be8b2af2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/nyserda/drive_clean_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/nyserda/drive_clean_rebate.yaml @@ -2,6 +2,7 @@ period: 2022 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 30_000 ny_drive_clean_vehicle_electric_range: 5 output: @@ -11,6 +12,7 @@ period: 2022 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 30_000 ny_drive_clean_vehicle_electric_range: 40 output: @@ -20,6 +22,7 @@ period: 2022 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 30_000 ny_drive_clean_vehicle_electric_range: 200 output: @@ -29,6 +32,7 @@ period: 2022 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 50_000 ny_drive_clean_vehicle_electric_range: 40 output: @@ -38,6 +42,7 @@ period: 2022 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 50_000 ny_drive_clean_vehicle_electric_range: 200 output: @@ -47,6 +52,7 @@ period: 2019 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 42_000 ny_drive_clean_vehicle_electric_range: 10 output: @@ -56,6 +62,7 @@ period: 2019 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 42_000 ny_drive_clean_vehicle_electric_range: 20 output: @@ -65,6 +72,7 @@ period: 2019 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 42_000 ny_drive_clean_vehicle_electric_range: 40 output: @@ -74,6 +82,7 @@ period: 2019 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 42_000 ny_drive_clean_vehicle_electric_range: 120 output: @@ -83,6 +92,7 @@ period: 2019 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 70_000 ny_drive_clean_vehicle_electric_range: 20 output: @@ -92,6 +102,7 @@ period: 2019 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 70_000 ny_drive_clean_vehicle_electric_range: 40 output: @@ -101,7 +112,18 @@ period: 2019 input: state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: true ny_drive_clean_vehicle_cost: 70_000 ny_drive_clean_vehicle_electric_range: 200 output: ny_drive_clean_rebate: 500 + +- name: No qualifying purchase + period: 2022 + input: + state_code: NY + ny_drive_clean_purchased_qualifying_vehicle: false + ny_drive_clean_vehicle_cost: 30_000 + ny_drive_clean_vehicle_electric_range: 200 + output: + ny_drive_clean_rebate: 0 diff --git a/policyengine_us/variables/gov/hhs/medicare/costs/medicare_enrolled.py b/policyengine_us/variables/gov/hhs/medicare/costs/medicare_enrolled.py index 9792cce961d..3b715488595 100644 --- a/policyengine_us/variables/gov/hhs/medicare/costs/medicare_enrolled.py +++ b/policyengine_us/variables/gov/hhs/medicare/costs/medicare_enrolled.py @@ -8,5 +8,4 @@ class medicare_enrolled(Variable): documentation = "Whether the person is enrolled in Medicare (Part A and/or Part B)" definition_period = YEAR reference = "https://www.cms.gov/medicare" - defined_for = "is_medicare_eligible" - default_value = True # Most Medicare-eligible people are enrolled + default_value = False diff --git a/policyengine_us/variables/gov/local/ca/la/general_relief/eligibility/la_general_relief_disability_eligible.py b/policyengine_us/variables/gov/local/ca/la/general_relief/eligibility/la_general_relief_disability_eligible.py index 5f7fbbfcf94..8f530d26482 100644 --- a/policyengine_us/variables/gov/local/ca/la/general_relief/eligibility/la_general_relief_disability_eligible.py +++ b/policyengine_us/variables/gov/local/ca/la/general_relief/eligibility/la_general_relief_disability_eligible.py @@ -8,5 +8,4 @@ class la_general_relief_disability_eligible(Variable): label = "Eligible for the Los Angeles County General Relief based on the disability requirements" # Person has to be disabled AND unable to work # which is curently not implemented - defined_for = "is_disabled" reference = "https://drive.google.com/file/d/1Oc7UuRFxJj-eDwTeox92PtmRVGnG9RjW/view?usp=sharing" diff --git a/policyengine_us/variables/gov/local/ca/la/general_relief/housing_subsidy/eligibility/la_general_relief_housing_subsidy_program_eligible.py b/policyengine_us/variables/gov/local/ca/la/general_relief/housing_subsidy/eligibility/la_general_relief_housing_subsidy_program_eligible.py index 24857e01085..37ab0abe7de 100644 --- a/policyengine_us/variables/gov/local/ca/la/general_relief/housing_subsidy/eligibility/la_general_relief_housing_subsidy_program_eligible.py +++ b/policyengine_us/variables/gov/local/ca/la/general_relief/housing_subsidy/eligibility/la_general_relief_housing_subsidy_program_eligible.py @@ -7,5 +7,4 @@ class la_general_relief_housing_subsidy_program_eligible(Variable): label = "Eligible for the Los Angeles County General Relief Housing Subsidy based on the program eligibility" definition_period = YEAR # Person has to be a resident of LA County - defined_for = "la_general_relief_eligible" reference = "https://dpss.lacounty.gov/en/cash/gr/housing.html" diff --git a/policyengine_us/variables/gov/states/il/dhs/aabd/il_aabd_institutional_status.py b/policyengine_us/variables/gov/states/il/dhs/aabd/il_aabd_institutional_status.py index 3e28d6ec490..1ee62d9ac2d 100644 --- a/policyengine_us/variables/gov/states/il/dhs/aabd/il_aabd_institutional_status.py +++ b/policyengine_us/variables/gov/states/il/dhs/aabd/il_aabd_institutional_status.py @@ -21,5 +21,5 @@ class il_aabd_institutional_status(Variable): default_value = IllinoisAABDInstitutionalStatus.NONE definition_period = MONTH label = "Illinois AABD Institutional Status" - defined_for = StateCode.MA + defined_for = StateCode.IL reference = "https://www.law.cornell.edu/regulations/illinois/Ill-Admin-Code-tit-89-SS-113.70" diff --git a/policyengine_us/variables/gov/states/ny/nyserda/drive_clean/ny_drive_clean_rebate.py b/policyengine_us/variables/gov/states/ny/nyserda/drive_clean/ny_drive_clean_rebate.py index 6ecda1ca3a8..f70d9241133 100644 --- a/policyengine_us/variables/gov/states/ny/nyserda/drive_clean/ny_drive_clean_rebate.py +++ b/policyengine_us/variables/gov/states/ny/nyserda/drive_clean/ny_drive_clean_rebate.py @@ -12,6 +12,9 @@ class ny_drive_clean_rebate(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ny.nyserda.drive_clean + purchased_qualifying_vehicle = tax_unit( + "ny_drive_clean_purchased_qualifying_vehicle", period + ) # Calculate vehicle all-electric range vehicle_range = tax_unit("ny_drive_clean_vehicle_electric_range", period) # Calculate vehicle price @@ -23,7 +26,7 @@ def formula(tax_unit, period, parameters): vehicle_over_msrp_threshold = vehicle_cost >= p.flat_rebate.msrp_threshold # If the vehicle is more expensive than the MSRP treshold, the filer # receives the flat rebate, otherwise they receive the range-based rebate - return where( + return purchased_qualifying_vehicle * where( vehicle_over_msrp_threshold, p.flat_rebate.amount, rebate_amount_based_on_range, diff --git a/policyengine_us/variables/gov/states/ny/nyserda/drive_clean/ny_drive_clean_vehicle_cost.py b/policyengine_us/variables/gov/states/ny/nyserda/drive_clean/ny_drive_clean_vehicle_cost.py index 0c5e63820fc..312bcec3031 100644 --- a/policyengine_us/variables/gov/states/ny/nyserda/drive_clean/ny_drive_clean_vehicle_cost.py +++ b/policyengine_us/variables/gov/states/ny/nyserda/drive_clean/ny_drive_clean_vehicle_cost.py @@ -8,4 +8,3 @@ class ny_drive_clean_vehicle_cost(Variable): unit = USD definition_period = YEAR reference = "https://www.nyserda.ny.gov/-/media/Project/Nyserda/Files/Programs/Drive-Clean-NY/implementation-manual.pdf#page=8" - defined_for = "ny_drive_clean_purchased_qualifying_vehicle" diff --git a/policyengine_us/variables/gov/states/sc/tax/income/credits/college_tuition/sc_tuition_credit.py b/policyengine_us/variables/gov/states/sc/tax/income/credits/college_tuition/sc_tuition_credit.py index d43d33a7f12..e1beb343dcc 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/credits/college_tuition/sc_tuition_credit.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/credits/college_tuition/sc_tuition_credit.py @@ -5,7 +5,6 @@ class sc_tuition_credit(Variable): value_type = float entity = Person label = "South Carolina Tuition Credit" - defined_for = "sc_tuition_credit_eligible" unit = USD definition_period = YEAR reference = ( diff --git a/policyengine_us/variables/household/demographic/person/current_pregnancy_month.py b/policyengine_us/variables/household/demographic/person/current_pregnancy_month.py index b3d40104fd3..71328147b41 100644 --- a/policyengine_us/variables/household/demographic/person/current_pregnancy_month.py +++ b/policyengine_us/variables/household/demographic/person/current_pregnancy_month.py @@ -6,4 +6,3 @@ class current_pregnancy_month(Variable): entity = Person label = "Current pregnancy month" definition_period = MONTH - defined_for = "is_pregnant" diff --git a/policyengine_us/variables/household/demographic/person/is_in_foster_care_group_home.py b/policyengine_us/variables/household/demographic/person/is_in_foster_care_group_home.py index d1391be5ee2..50f36e3c406 100644 --- a/policyengine_us/variables/household/demographic/person/is_in_foster_care_group_home.py +++ b/policyengine_us/variables/household/demographic/person/is_in_foster_care_group_home.py @@ -7,4 +7,3 @@ class is_in_foster_care_group_home(Variable): label = "Person is currently in a qualifying foster care group home" definition_period = MONTH default_value = False - defined_for = "is_in_foster_care" diff --git a/policyengine_us/variables/household/demographic/person/retired_from_federal_government.py b/policyengine_us/variables/household/demographic/person/retired_from_federal_government.py index 9b7c8618b0c..8f189c47536 100644 --- a/policyengine_us/variables/household/demographic/person/retired_from_federal_government.py +++ b/policyengine_us/variables/household/demographic/person/retired_from_federal_government.py @@ -6,5 +6,4 @@ class retired_from_federal_government(Variable): entity = Person label = "Retired from Federal Government" definition_period = YEAR - defined_for = "is_retired" default_value = False