diff --git a/changelog.d/uk-uc-rebind-808-809.changed.md b/changelog.d/uk-uc-rebind-808-809.changed.md new file mode 100644 index 00000000..e4be9029 --- /dev/null +++ b/changelog.d/uk-uc-rebind-808-809.changed.md @@ -0,0 +1 @@ +Re-bind the nine DWP Universal Credit composition targets to benefit-unit grain, because a DWP household is the UC unit of assessment: the old household-grain binding summed `is_child` over the dwelling, counting children from other benefit units and deleting single claimants who share a dwelling with children. Re-bind the two two-child-limit disability children targets to sum `is_child` for sheet 04B's all-children concept (microcosm#808, microcosm#809). diff --git a/packages/microcosm-build/src/microcosm/build/uk/target_references.json b/packages/microcosm-build/src/microcosm/build/uk/target_references.json index 41a0d09a..8c72eaf8 100644 --- a/packages/microcosm-build/src/microcosm/build/uk/target_references.json +++ b/packages/microcosm-build/src/microcosm/build/uk/target_references.json @@ -4058,7 +4058,7 @@ "geography_level": "country", "geography_id": "K03000001" }, - "entity": "household", + "entity": "benunit", "measure": "dwp/uc/claimants_with_1_children", "family": "dwp_universal_credit", "period": 2025, @@ -4081,7 +4081,7 @@ "geography_level": "country", "geography_id": "K03000001" }, - "entity": "household", + "entity": "benunit", "measure": "dwp/uc/claimants_with_2_children", "family": "dwp_universal_credit", "period": 2025, @@ -4104,7 +4104,7 @@ "geography_level": "country", "geography_id": "K03000001" }, - "entity": "household", + "entity": "benunit", "measure": "dwp/uc/claimants_with_3_children", "family": "dwp_universal_credit", "period": 2025, @@ -4127,7 +4127,7 @@ "geography_level": "country", "geography_id": "K03000001" }, - "entity": "household", + "entity": "benunit", "measure": "dwp/uc/claimants_with_4_children", "family": "dwp_universal_credit", "period": 2025, @@ -4150,7 +4150,7 @@ "geography_level": "country", "geography_id": "K03000001" }, - "entity": "household", + "entity": "benunit", "measure": "dwp/uc/claimants_with_5+_children", "family": "dwp_universal_credit", "period": 2025, @@ -4173,7 +4173,7 @@ "geography_level": "country", "geography_id": "K03000001" }, - "entity": "household", + "entity": "benunit", "measure": "dwp/uc/claimants_single_no_children", "family": "dwp_universal_credit", "period": 2025, @@ -4196,7 +4196,7 @@ "geography_level": "country", "geography_id": "K03000001" }, - "entity": "household", + "entity": "benunit", "measure": "dwp/uc/claimants_single_with_children", "family": "dwp_universal_credit", "period": 2025, @@ -4219,7 +4219,7 @@ "geography_level": "country", "geography_id": "K03000001" }, - "entity": "household", + "entity": "benunit", "measure": "dwp/uc/claimants_couple_no_children", "family": "dwp_universal_credit", "period": 2025, @@ -4242,7 +4242,7 @@ "geography_level": "country", "geography_id": "K03000001" }, - "entity": "household", + "entity": "benunit", "measure": "dwp/uc/claimants_couple_with_children", "family": "dwp_universal_credit", "period": 2025, diff --git a/packages/microcosm-build/src/microcosm/build/uk/uk_national_targets.json b/packages/microcosm-build/src/microcosm/build/uk/uk_national_targets.json index b1380ab9..9aff2983 100644 --- a/packages/microcosm-build/src/microcosm/build/uk/uk_national_targets.json +++ b/packages/microcosm-build/src/microcosm/build/uk/uk_national_targets.json @@ -2801,8 +2801,8 @@ "country" ], "measurement": { - "entity": "household", - "concept": "uk.household.count", + "entity": "benunit", + "concept": "uk.benefit_unit.count", "filters": [ { "concept": "uk.benefits.universal_credit.amount", @@ -2810,7 +2810,7 @@ "value": 0 }, { - "concept": "uk.household.children", + "concept": "uk.benefit_unit.children", "operator": "==", "value": 1 } @@ -2819,30 +2819,26 @@ "bindings": { "policyengine": { "metric_name": "dwp/uc/claimants_with_1_children", - "reduce": "any", - "household_conditions": [ + "value_variable": "benunit_count", + "from_entity": "benunit", + "filters": [ { "variable": "universal_credit", - "entity": "benunit", - "reduce": "any", "operator": ">", "value": 0 }, { - "variable": "is_child", - "entity": "person", - "reduce": "sum", + "variable": "num_children", "operator": "==", "value": 1 } ], - "value_variable": "household_count", - "notes": "Household indicator: any benunit on UC and the household child count matches (compute/benefits.py:33-51)." + "notes": "Prepared benefit-unit count: UC receipt and the claim's child count (model num_children, age<18 per benunit). DWP household means the UC unit of assessment, not an ONS household; DWP counts children on the claim including 16-19 qualifying young persons in non-advanced education, whom the age<18 concept omits - declared residue. Re-bound from household grain per microcosm#809 (the dwelling-wide is_child sum counted other benunits' children)." }, "axiom": { "metric_name": "dwp/uc/claimants_with_1_children", "status": "pending", - "value_rule": "uk.household.count" + "value_rule": "uk.benefit_unit.count" } }, "ledger_selector": { @@ -2861,8 +2857,8 @@ "country" ], "measurement": { - "entity": "household", - "concept": "uk.household.count", + "entity": "benunit", + "concept": "uk.benefit_unit.count", "filters": [ { "concept": "uk.benefits.universal_credit.amount", @@ -2870,7 +2866,7 @@ "value": 0 }, { - "concept": "uk.household.children", + "concept": "uk.benefit_unit.children", "operator": "==", "value": 2 } @@ -2879,30 +2875,26 @@ "bindings": { "policyengine": { "metric_name": "dwp/uc/claimants_with_2_children", - "reduce": "any", - "household_conditions": [ + "value_variable": "benunit_count", + "from_entity": "benunit", + "filters": [ { "variable": "universal_credit", - "entity": "benunit", - "reduce": "any", "operator": ">", "value": 0 }, { - "variable": "is_child", - "entity": "person", - "reduce": "sum", + "variable": "num_children", "operator": "==", "value": 2 } ], - "value_variable": "household_count", - "notes": "Household indicator: any benunit on UC and the household child count matches (compute/benefits.py:33-51)." + "notes": "Prepared benefit-unit count: UC receipt and the claim's child count (model num_children, age<18 per benunit). DWP household means the UC unit of assessment, not an ONS household; DWP counts children on the claim including 16-19 qualifying young persons in non-advanced education, whom the age<18 concept omits - declared residue. Re-bound from household grain per microcosm#809 (the dwelling-wide is_child sum counted other benunits' children)." }, "axiom": { "metric_name": "dwp/uc/claimants_with_2_children", "status": "pending", - "value_rule": "uk.household.count" + "value_rule": "uk.benefit_unit.count" } }, "ledger_selector": { @@ -2921,8 +2913,8 @@ "country" ], "measurement": { - "entity": "household", - "concept": "uk.household.count", + "entity": "benunit", + "concept": "uk.benefit_unit.count", "filters": [ { "concept": "uk.benefits.universal_credit.amount", @@ -2930,7 +2922,7 @@ "value": 0 }, { - "concept": "uk.household.children", + "concept": "uk.benefit_unit.children", "operator": "==", "value": 3 } @@ -2939,30 +2931,26 @@ "bindings": { "policyengine": { "metric_name": "dwp/uc/claimants_with_3_children", - "reduce": "any", - "household_conditions": [ + "value_variable": "benunit_count", + "from_entity": "benunit", + "filters": [ { "variable": "universal_credit", - "entity": "benunit", - "reduce": "any", "operator": ">", "value": 0 }, { - "variable": "is_child", - "entity": "person", - "reduce": "sum", + "variable": "num_children", "operator": "==", "value": 3 } ], - "value_variable": "household_count", - "notes": "Household indicator: any benunit on UC and the household child count matches (compute/benefits.py:33-51)." + "notes": "Prepared benefit-unit count: UC receipt and the claim's child count (model num_children, age<18 per benunit). DWP household means the UC unit of assessment, not an ONS household; DWP counts children on the claim including 16-19 qualifying young persons in non-advanced education, whom the age<18 concept omits - declared residue. Re-bound from household grain per microcosm#809 (the dwelling-wide is_child sum counted other benunits' children)." }, "axiom": { "metric_name": "dwp/uc/claimants_with_3_children", "status": "pending", - "value_rule": "uk.household.count" + "value_rule": "uk.benefit_unit.count" } }, "ledger_selector": { @@ -2981,8 +2969,8 @@ "country" ], "measurement": { - "entity": "household", - "concept": "uk.household.count", + "entity": "benunit", + "concept": "uk.benefit_unit.count", "filters": [ { "concept": "uk.benefits.universal_credit.amount", @@ -2990,7 +2978,7 @@ "value": 0 }, { - "concept": "uk.household.children", + "concept": "uk.benefit_unit.children", "operator": "==", "value": 4 } @@ -2999,30 +2987,26 @@ "bindings": { "policyengine": { "metric_name": "dwp/uc/claimants_with_4_children", - "reduce": "any", - "household_conditions": [ + "value_variable": "benunit_count", + "from_entity": "benunit", + "filters": [ { "variable": "universal_credit", - "entity": "benunit", - "reduce": "any", "operator": ">", "value": 0 }, { - "variable": "is_child", - "entity": "person", - "reduce": "sum", + "variable": "num_children", "operator": "==", "value": 4 } ], - "value_variable": "household_count", - "notes": "Household indicator: any benunit on UC and the household child count matches (compute/benefits.py:33-51)." + "notes": "Prepared benefit-unit count: UC receipt and the claim's child count (model num_children, age<18 per benunit). DWP household means the UC unit of assessment, not an ONS household; DWP counts children on the claim including 16-19 qualifying young persons in non-advanced education, whom the age<18 concept omits - declared residue. Re-bound from household grain per microcosm#809 (the dwelling-wide is_child sum counted other benunits' children)." }, "axiom": { "metric_name": "dwp/uc/claimants_with_4_children", "status": "pending", - "value_rule": "uk.household.count" + "value_rule": "uk.benefit_unit.count" } }, "ledger_selector": { @@ -3041,8 +3025,8 @@ "country" ], "measurement": { - "entity": "household", - "concept": "uk.household.count", + "entity": "benunit", + "concept": "uk.benefit_unit.count", "filters": [ { "concept": "uk.benefits.universal_credit.amount", @@ -3050,7 +3034,7 @@ "value": 0 }, { - "concept": "uk.household.children", + "concept": "uk.benefit_unit.children", "operator": ">=", "value": 5 } @@ -3059,30 +3043,26 @@ "bindings": { "policyengine": { "metric_name": "dwp/uc/claimants_with_5+_children", - "reduce": "any", - "household_conditions": [ + "value_variable": "benunit_count", + "from_entity": "benunit", + "filters": [ { "variable": "universal_credit", - "entity": "benunit", - "reduce": "any", "operator": ">", "value": 0 }, { - "variable": "is_child", - "entity": "person", - "reduce": "sum", + "variable": "num_children", "operator": ">=", "value": 5 } ], - "value_variable": "household_count", - "notes": "Household indicator: any benunit on UC and the household child count matches (compute/benefits.py:33-51)." + "notes": "Prepared benefit-unit count: UC receipt and the claim's child count (model num_children, age<18 per benunit). DWP household means the UC unit of assessment, not an ONS household; DWP counts children on the claim including 16-19 qualifying young persons in non-advanced education, whom the age<18 concept omits - declared residue. Re-bound from household grain per microcosm#809 (the dwelling-wide is_child sum counted other benunits' children)." }, "axiom": { "metric_name": "dwp/uc/claimants_with_5+_children", "status": "pending", - "value_rule": "uk.household.count" + "value_rule": "uk.benefit_unit.count" } }, "ledger_selector": { @@ -3101,8 +3081,8 @@ "country" ], "measurement": { - "entity": "household", - "concept": "uk.household.count", + "entity": "benunit", + "concept": "uk.benefit_unit.count", "filters": [ { "concept": "uk.benefits.universal_credit.amount", @@ -3114,37 +3094,26 @@ "bindings": { "policyengine": { "metric_name": "dwp/uc/claimants_single_no_children", - "reduce": "any", - "household_conditions": [ + "value_variable": "benunit_count", + "from_entity": "benunit", + "filters": [ { "variable": "universal_credit", - "entity": "benunit", - "reduce": "any", "operator": ">", "value": 0 }, { "variable": "family_type", - "entity": "benunit", - "reduce": "any", "operator": "==", "value": "SINGLE" - }, - { - "variable": "is_child", - "entity": "person", - "reduce": "sum", - "operator": "==", - "value": 0 } ], - "value_variable": "household_count", - "notes": "Household indicator over benunit family type (compute/benefits.py:54-81). uk-data's +~0.021% undercount inflation is a populace-side value declaration." + "notes": "Prepared benefit-unit count: UC receipt and family_type == SINGLE (single, no children at benunit grain). DWP household means the UC unit of assessment. Re-bound from household grain per microcosm#809 - the any-benunit family type plus dwelling-wide child sum deleted single claimants sharing a dwelling with another benunit's children. uk-data's +~0.021% undercount inflation is a populace-side value declaration." }, "axiom": { "metric_name": "dwp/uc/claimants_single_no_children", "status": "pending", - "value_rule": "uk.household.count" + "value_rule": "uk.benefit_unit.count" } }, "ledger_selector": { @@ -3163,8 +3132,8 @@ "country" ], "measurement": { - "entity": "household", - "concept": "uk.household.count", + "entity": "benunit", + "concept": "uk.benefit_unit.count", "filters": [ { "concept": "uk.benefits.universal_credit.amount", @@ -3176,40 +3145,26 @@ "bindings": { "policyengine": { "metric_name": "dwp/uc/claimants_single_with_children", - "reduce": "any", - "household_conditions": [ + "value_variable": "benunit_count", + "from_entity": "benunit", + "filters": [ { "variable": "universal_credit", - "entity": "benunit", - "reduce": "any", "operator": ">", "value": 0 }, { "variable": "family_type", - "entity": "benunit", - "reduce": "any", - "operator": "in", - "value": [ - "SINGLE", - "LONE_PARENT" - ] - }, - { - "variable": "is_child", - "entity": "person", - "reduce": "sum", - "operator": ">", - "value": 0 + "operator": "==", + "value": "LONE_PARENT" } ], - "value_variable": "household_count", - "notes": "DWP's single bucket does not distinguish PE-UK's SINGLE and LONE_PARENT (compute/benefits.py:54-81). uk-data's +~0.021% undercount inflation on these counts is a populace-side value declaration - the facts are the raw Stat-Xplore counts." + "notes": "Prepared benefit-unit count: UC receipt and family_type == LONE_PARENT. DWP's 'Single, with children' bucket is LONE_PARENT at benunit grain - the previous SINGLE-or-LONE_PARENT compound plus dwelling-wide child sum collapses to the single enum value. Re-bound per microcosm#809. uk-data's +~0.021% undercount inflation is a populace-side value declaration." }, "axiom": { "metric_name": "dwp/uc/claimants_single_with_children", "status": "pending", - "value_rule": "uk.household.count" + "value_rule": "uk.benefit_unit.count" } }, "ledger_selector": { @@ -3228,8 +3183,8 @@ "country" ], "measurement": { - "entity": "household", - "concept": "uk.household.count", + "entity": "benunit", + "concept": "uk.benefit_unit.count", "filters": [ { "concept": "uk.benefits.universal_credit.amount", @@ -3241,30 +3196,26 @@ "bindings": { "policyengine": { "metric_name": "dwp/uc/claimants_couple_no_children", - "reduce": "any", - "household_conditions": [ + "value_variable": "benunit_count", + "from_entity": "benunit", + "filters": [ { "variable": "universal_credit", - "entity": "benunit", - "reduce": "any", "operator": ">", "value": 0 }, { "variable": "family_type", - "entity": "benunit", - "reduce": "any", "operator": "==", "value": "COUPLE_NO_CHILDREN" } ], - "value_variable": "household_count", - "notes": "Household indicator over benunit family type (compute/benefits.py:54-81). uk-data's +~0.021% undercount inflation is a populace-side value declaration." + "notes": "Prepared benefit-unit count: UC receipt and family_type == COUPLE_NO_CHILDREN. DWP household means the UC unit of assessment. Re-bound from household grain per microcosm#809. uk-data's +~0.021% undercount inflation is a populace-side value declaration." }, "axiom": { "metric_name": "dwp/uc/claimants_couple_no_children", "status": "pending", - "value_rule": "uk.household.count" + "value_rule": "uk.benefit_unit.count" } }, "ledger_selector": { @@ -3283,8 +3234,8 @@ "country" ], "measurement": { - "entity": "household", - "concept": "uk.household.count", + "entity": "benunit", + "concept": "uk.benefit_unit.count", "filters": [ { "concept": "uk.benefits.universal_credit.amount", @@ -3296,30 +3247,26 @@ "bindings": { "policyengine": { "metric_name": "dwp/uc/claimants_couple_with_children", - "reduce": "any", - "household_conditions": [ + "value_variable": "benunit_count", + "from_entity": "benunit", + "filters": [ { "variable": "universal_credit", - "entity": "benunit", - "reduce": "any", "operator": ">", "value": 0 }, { "variable": "family_type", - "entity": "benunit", - "reduce": "any", "operator": "==", "value": "COUPLE_WITH_CHILDREN" } ], - "value_variable": "household_count", - "notes": "Household indicator over benunit family type (compute/benefits.py:54-81). uk-data's +~0.021% undercount inflation is a populace-side value declaration." + "notes": "Prepared benefit-unit count: UC receipt and family_type == COUPLE_WITH_CHILDREN. DWP household means the UC unit of assessment. Re-bound from household grain per microcosm#809. uk-data's +~0.021% undercount inflation is a populace-side value declaration." }, "axiom": { "metric_name": "dwp/uc/claimants_couple_with_children", "status": "pending", - "value_rule": "uk.household.count" + "value_rule": "uk.benefit_unit.count" } }, "ledger_selector": { @@ -4135,14 +4082,14 @@ ], "kind": "baseline_flag_crosstab", "affected_flag_variable": "uc_is_child_limit_affected", - "value_variable": "uc_is_child_limit_affected", + "value_variable": "is_child", "value_reduction": { - "variable": "uc_is_child_limit_affected", + "variable": "is_child", "entity": "person", "reduce": "sum" }, "map_to": "household", - "notes": "Affected children are persons flagged by uc_is_child_limit_affected (household round-trip) who are children in UC-receiving households; an affected household contains at least one such child (compute/benefits.py:130-148). Row counts: affected_children." + "notes": "All children living in affected households, counted with the model is_child sum per household under the baseline affected flag and the disability condition (sheet 04B counts every child: the 3- and 4-children sibling rows divide to exactly 3.00 and 4.00 children per household; these cells run ~3.6-3.7). Re-bound from uc_is_child_limit_affected per microcosm#808 - the affected-children flag averages ~1.27 per household, a structural ~2.8x shortfall against the published all-children concept. Row counts: adult_pip_children." }, "axiom": { "metric_name": "dwp/uc/two_child_limit/adult_pip_children", @@ -4224,14 +4171,14 @@ ], "kind": "baseline_flag_crosstab", "affected_flag_variable": "uc_is_child_limit_affected", - "value_variable": "uc_is_child_limit_affected", + "value_variable": "is_child", "value_reduction": { - "variable": "uc_is_child_limit_affected", + "variable": "is_child", "entity": "person", "reduce": "sum" }, "map_to": "household", - "notes": "Affected children are persons flagged by uc_is_child_limit_affected (household round-trip) who are children in UC-receiving households; an affected household contains at least one such child (compute/benefits.py:130-148). Row counts: affected_children." + "notes": "All children living in affected households, counted with the model is_child sum per household under the baseline affected flag and the disability condition (sheet 04B counts every child: the 3- and 4-children sibling rows divide to exactly 3.00 and 4.00 children per household; these cells run ~3.6-3.7). Re-bound from uc_is_child_limit_affected per microcosm#808 - the affected-children flag averages ~1.27 per household, a structural ~2.8x shortfall against the published all-children concept. Row counts: disabled_child_element_children." }, "axiom": { "metric_name": "dwp/uc/two_child_limit/disabled_child_element_children", diff --git a/packages/microcosm-build/tests/test_uk_ledger_targets.py b/packages/microcosm-build/tests/test_uk_ledger_targets.py index 793222f2..79de9ae1 100644 --- a/packages/microcosm-build/tests/test_uk_ledger_targets.py +++ b/packages/microcosm-build/tests/test_uk_ledger_targets.py @@ -15,12 +15,13 @@ class StubUKAdapter: - """Seven people across three households, flagged child by flagged child. + """Seven people across three households, with distinct child concepts. - The flag and the count are deliberately distinct here: household 0 holds - three flagged children and household 2 holds two, so a household-grain - read of the boolean (1/0/1) can never be mistaken for the child counts - (3/0/2) the contract's count rows declare. + The affected flag and the child counts are deliberately distinct here: + household 0 holds three affected children but four children in all, and + household 2 holds two of each. A household-grain read of the boolean + (1/0/1) can therefore be distinguished from both the affected-child counts + (3/0/2) and the all-child counts (4/0/2). """ def __init__(self): @@ -33,6 +34,10 @@ def __init__(self): ), "person_household_id": person_household, "uc_is_child_limit_affected": child_flags, + "is_child": np.array( + [True, True, True, True, False, True, True] + ), + "pip": np.array([0.0, 0.0, 0.0, 100.0, 100.0, 0.0, 0.0]), }, "household": { "household_id": np.array([0, 1, 2]), @@ -61,6 +66,13 @@ def entity_reduction(self, reduction): dtype=float, ) + def household_condition(self, condition): + assert condition["variable"] == "pip" + assert condition["entity"] == "person" + assert condition["reduce"] == "sum" + assert condition["operator"] == ">" + return self.entity_reduction(condition) > float(condition["value"]) + def parameter(self, name, period): assert name == "gov.hmrc.cgt.annual_exempt_amount" assert period == 2025 @@ -113,6 +125,18 @@ def test_materialize_uk_ledger_targets_with_stub_adapter(): "contract_target_id": "dwp.uc.two_child_limit.children_affected" }, ), + TargetSpec( + name="dwp.uc.two_child_limit.children_claimant_pip", + entity="household", + measure="dwp/uc/two_child_limit/adult_pip_children", + value=1.0, + source="test", + metadata={ + "contract_target_id": ( + "dwp.uc.two_child_limit.children_claimant_pip" + ) + }, + ), ], country="uk", ) @@ -136,6 +160,12 @@ def test_materialize_uk_ledger_targets_with_stub_adapter(): assert adapter.tables["household"][ "dwp/uc/two_child_limit/children_affected" ].tolist() == [3.0, 0.0, 2.0] + # Sheet 04B's claimant-PIP row counts every child in affected households + # satisfying the PIP condition, not only the children carrying the + # affected flag. Household 0 therefore contributes four, not three. + assert adapter.tables["household"][ + "dwp/uc/two_child_limit/adult_pip_children" + ].tolist() == [4.0, 0.0, 0.0] def _composition_frame(): @@ -170,6 +200,88 @@ def _composition_frame(): ) +def _uc_composition_frame(): + """Two multibenunit households that distinguish UC claim composition.""" + + import pandas as pd + + from microcosm.frame import EntitySchema, Frame, WeightKind, Weights + + return Frame( + { + "person": pd.DataFrame( + { + "person_id": np.arange(6), + "person_benunit_id": [0, 0, 1, 2, 2, 3], + "person_household_id": [0, 0, 0, 1, 1, 1], + "is_child": [0.0, 1.0, 0.0, 0.0, 1.0, 0.0], + } + ), + "benunit": pd.DataFrame( + { + "benunit_id": np.arange(4), + "family_type": [ + "LONE_PARENT", + "SINGLE", + "LONE_PARENT", + "SINGLE", + ], + "universal_credit": [100.0, 0.0, 0.0, 100.0], + "num_children": [1, 0, 1, 0], + } + ), + "household": pd.DataFrame({"household_id": np.arange(2)}), + }, + EntitySchema(group_entities=("benunit", "household")), + {"household": Weights(np.array([10.0, 20.0]), WeightKind.DESIGN)}, + metadata={"time_period": "2025"}, + ) + + +def test_uc_composition_materializes_at_benunit_grain(): + from microcosm.build.uk_runtime.ledger_targets import UKFrameTargetAdapter + + registry = TargetRegistry( + [ + TargetSpec( + name="dwp.uc.households_children_1", + entity="benunit", + measure="dwp/uc/claimants_with_1_children", + value=1.0, + source="test", + metadata={ + "contract_target_id": "dwp.uc.households_children_1" + }, + ), + TargetSpec( + name="dwp.uc.households_single_no_children", + entity="benunit", + measure="dwp/uc/claimants_single_no_children", + value=1.0, + source="test", + metadata={ + "contract_target_id": "dwp.uc.households_single_no_children" + }, + ), + ], + country="uk", + ) + adapter = UKFrameTargetAdapter(_uc_composition_frame()) + + result = materialize_uk_ledger_targets(adapter, registry, period=2025) + + assert result.skipped == () + assert adapter.tables["benunit"][ + "dwp/uc/claimants_with_1_children" + ].tolist() == [1.0, 0.0, 0.0, 0.0] + # The non-UC single sharing household 0 with the claimant fails its own + # filters. The childless UC single in household 1 counts even though the + # other benunit in that dwelling contains a child. + assert adapter.tables["benunit"][ + "dwp/uc/claimants_single_no_children" + ].tolist() == [0.0, 0.0, 0.0, 1.0] + + def test_household_condition_reduces_person_level_conditions(): # Person-level conditions collapse through person_household_id. Building # the group-membership column here would ask for "person_person_id", diff --git a/packages/microcosm-build/tests/test_uk_national_targets.py b/packages/microcosm-build/tests/test_uk_national_targets.py index 02ac5e42..15922595 100644 --- a/packages/microcosm-build/tests/test_uk_national_targets.py +++ b/packages/microcosm-build/tests/test_uk_national_targets.py @@ -358,6 +358,47 @@ def test_uk_uc_households_target_counts_benunits(): } +def test_uk_uc_composition_and_disability_children_targets_are_rebound(): + resource = _load() + composition_target_ids = { + "dwp.uc.households_children_1", + "dwp.uc.households_children_2", + "dwp.uc.households_children_3", + "dwp.uc.households_children_4", + "dwp.uc.households_children_5_or_more", + "dwp.uc.households_single_no_children", + "dwp.uc.households_single_with_children", + "dwp.uc.households_couple_no_children", + "dwp.uc.households_couple_with_children", + } + allowed_filter_variables = { + "universal_credit", + "num_children", + "family_type", + } + + for target_id in composition_target_ids: + binding = _target_by_id(resource, target_id)["bindings"]["policyengine"] + assert binding["from_entity"] == "benunit" + assert binding["value_variable"] == "benunit_count" + assert "household_conditions" not in binding + assert "reduce" not in binding + assert all( + predicate["variable"] in allowed_filter_variables + for predicate in binding["filters"] + ) + + for target_id in { + "dwp.uc.two_child_limit.children_claimant_pip", + "dwp.uc.two_child_limit.children_disabled_child_element", + }: + binding = _target_by_id(resource, target_id)["bindings"]["policyengine"] + assert binding["value_variable"] == "is_child" + assert binding["value_reduction"]["variable"] == "is_child" + assert binding["kind"] == "baseline_flag_crosstab" + assert binding["affected_flag_variable"] == "uc_is_child_limit_affected" + + def test_uk_national_cgt_contract_names_match_runtime_specs(): resource = _load()