@@ -329,16 +329,7 @@ def test_status_on_target_based_on_last_successful_date(
329329 nhs_number = NHSNumber (faker .nhs_number ())
330330
331331 target_rows = person_rows_builder (
332- nhs_number ,
333- cohorts = ["cohort1" ],
334- vaccines = [
335- (
336- vaccine ,
337- datetime .datetime .strptime (last_successful_date , "%Y%m%d" ).replace (tzinfo = datetime .UTC )
338- if last_successful_date
339- else None ,
340- )
341- ],
332+ nhs_number , cohorts = ["cohort1" ], vaccines = {vaccine : {"LAST_SUCCESSFUL_DATE" : last_successful_date }}
342333 )
343334
344335 campaign_configs = [
@@ -728,7 +719,7 @@ def test_cohort_group_descriptions_are_selected_based_on_priority_when_cohorts_h
728719@freeze_time ("2025-04-25" )
729720def test_no_active_iteration_returns_empty_conditions_with_single_active_campaign (faker : Faker ):
730721 # Given
731- person_rows = person_rows_builder (NHSNumber (faker .nhs_number ()))
722+ person_rows = person_rows_builder (NHSNumber (faker .nhs_number ()), cohorts = [] )
732723 campaign_configs = [
733724 rule_builder .CampaignConfigFactory .build (
734725 target = "RSV" ,
@@ -757,7 +748,7 @@ def test_no_active_iteration_returns_empty_conditions_with_single_active_campaig
757748@freeze_time ("2025-04-25" )
758749def test_returns_no_condition_data_for_campaign_without_active_iteration (faker : Faker , caplog ):
759750 # Given
760- person_rows = person_rows_builder (NHSNumber (faker .nhs_number ()))
751+ person_rows = person_rows_builder (NHSNumber (faker .nhs_number ()), cohorts = [] )
761752 campaign_configs = [
762753 rule_builder .CampaignConfigFactory .build (
763754 target = "RSV" ,
@@ -801,7 +792,7 @@ def test_returns_no_condition_data_for_campaign_without_active_iteration(faker:
801792@freeze_time ("2025-04-25" )
802793def test_no_active_campaign (faker : Faker ):
803794 # Given
804- person_rows = person_rows_builder (NHSNumber (faker .nhs_number ()))
795+ person_rows = person_rows_builder (NHSNumber (faker .nhs_number ()), cohorts = [] )
805796 campaign_configs = [rule_builder .CampaignConfigFactory .build ()]
806797 # Need to set the campaign dates to override CampaignConfigFactory.fix_iteration_date_invariants behavior
807798 campaign_configs [0 ].start_date = datetime .date (2025 , 5 , 10 )
@@ -824,7 +815,7 @@ def test_eligibility_status_replaces_tokens_with_attribute_data(faker: Faker):
824815 nhs_number ,
825816 date_of_birth = date_of_birth ,
826817 cohorts = ["cohort_1" , "cohort_2" , "cohort_3" ],
827- vaccines = [( "RSV" , datetime .date (2024 , 1 , 3 ))] ,
818+ vaccines = { "RSV" : { "LAST_SUCCESSFUL_DATE" : datetime .date (2024 , 1 , 3 ). strftime ( "%Y%m%d" )}} ,
828819 icb = "QE1" ,
829820 gp_practice = None ,
830821 )
@@ -1161,6 +1152,109 @@ def test_multiple_virtual_cohorts(faker: Faker):
11611152 )
11621153
11631154
1155+ @freeze_time ("2025-10-02" )
1156+ def test_virtual_cohorts_when_person_has_no_existing_cohorts (faker : Faker ):
1157+ # Given
1158+ nhs_number = NHSNumber (faker .nhs_number ())
1159+ date_of_birth = DateOfBirth (datetime .date (1980 , 10 , 2 ))
1160+ person_rows = person_rows_builder (
1161+ nhs_number ,
1162+ date_of_birth = date_of_birth ,
1163+ cohorts = [],
1164+ vaccines = {
1165+ "RSV" : {
1166+ "LAST_SUCCESSFUL_DATE" : datetime .date (2025 , 9 , 25 ).strftime ("%Y%m%d" ),
1167+ "BOOKED_APPOINTMENT_DATE" : datetime .date (2025 , 10 , 9 ).strftime ("%Y%m%d" ),
1168+ },
1169+ },
1170+ )
1171+ campaign_configs = [
1172+ rule_builder .CampaignConfigFactory .build (
1173+ target = "RSV" ,
1174+ iterations = [
1175+ rule_builder .IterationFactory .build (
1176+ iteration_cohorts = [
1177+ rule_builder .IterationCohortFactory .build (
1178+ cohort_label = "rsv_75to79" ,
1179+ cohort_group = "rsv_age" ,
1180+ positive_description = "In rsv_75to79" ,
1181+ negative_description = "Out rsv_75to79" ,
1182+ priority = 0 ,
1183+ ),
1184+ rule_builder .IterationCohortFactory .build (
1185+ cohort_label = "rsv_80_since_02_Sept_2024" ,
1186+ cohort_group = "rsv_age_catchup" ,
1187+ positive_description = "In rsv_80_since_02_Sept_2024" ,
1188+ negative_description = "Out rsv_80_since_02_Sept_2024" ,
1189+ priority = 10 ,
1190+ ),
1191+ rule_builder .IterationCohortFactory .build (
1192+ cohort_label = "elid_all_people" ,
1193+ cohort_group = "magic_cohort" ,
1194+ positive_description = "In elid_all_people" ,
1195+ negative_description = "Out elid_all_people" ,
1196+ priority = 20 ,
1197+ virtual = "Y" ,
1198+ ),
1199+ ],
1200+ iteration_rules = [
1201+ rule_builder .PersonAgeSuppressionRuleFactory .build (
1202+ attribute_level = RuleAttributeLevel .TARGET ,
1203+ attribute_name = "LAST_SUCCESSFUL_DATE" ,
1204+ attribute_target = "RSV" ,
1205+ cohort_label = "elid_all_people" ,
1206+ comparator = "-25[[NVL:18000101]]" ,
1207+ description = "Remove anyone NOT already vaccinated within the last 25 years" ,
1208+ name = "Remove from magic cohort unless already vaccinated or have future booking" ,
1209+ operator = RuleOperator .year_lte ,
1210+ priority = 100 ,
1211+ type = RuleType .filter ,
1212+ ),
1213+ rule_builder .PersonAgeSuppressionRuleFactory .build (
1214+ attribute_level = RuleAttributeLevel .TARGET ,
1215+ attribute_name = "BOOKED_APPOINTMENT_DATE" ,
1216+ attribute_target = "RSV" ,
1217+ cohort_label = "elid_all_people" ,
1218+ comparator = "0[[NVL:18000101]]" ,
1219+ description = "Remove anyone without a future booking from magic cohort" ,
1220+ name = "Remove from magic cohort unless already vaccinated or have future booking" ,
1221+ operator = RuleOperator .day_lt ,
1222+ priority = 110 ,
1223+ type = RuleType .filter ,
1224+ ),
1225+ rule_builder .PersonAgeSuppressionRuleFactory .build (
1226+ attribute_level = RuleAttributeLevel .TARGET ,
1227+ attribute_name = "LAST_SUCCESSFUL_DATE" ,
1228+ attribute_target = "RSV" ,
1229+ comparator = "-25[[NVL:18000101]]" ,
1230+ description = "## You've had your RSV vaccination\n \n We believe you had your vaccination." ,
1231+ name = "Already Vaccinated" ,
1232+ operator = RuleOperator .year_gte ,
1233+ priority = 200 ,
1234+ rule_stop = True ,
1235+ type = RuleType .suppression ,
1236+ ),
1237+ ],
1238+ )
1239+ ],
1240+ )
1241+ ]
1242+
1243+ calculator = EligibilityCalculator (person_rows , campaign_configs )
1244+
1245+ # When
1246+ actual = calculator .get_eligibility_status ("Y" , ["ALL" ], "ALL" )
1247+
1248+ assert_that (
1249+ actual ,
1250+ is_eligibility_status ().with_conditions (
1251+ has_items (
1252+ is_condition ().with_condition_name (ConditionName ("RSV" )).and_status (Status .not_actionable ),
1253+ )
1254+ ),
1255+ )
1256+
1257+
11641258def test_regardless_of_final_status_audit_all_types_of_cohort_status_rules (faker : Faker ):
11651259 # Given
11661260 nhs_number = NHSNumber (faker .nhs_number ())
@@ -1277,7 +1371,10 @@ def test_eligibility_status_with_invalid_tokens_raises_attribute_error(faker: Fa
12771371 date_of_birth = DateOfBirth (datetime .date (2025 , 5 , 10 ))
12781372
12791373 person_rows = person_rows_builder (
1280- nhs_number , date_of_birth = date_of_birth , cohorts = ["cohort_1" ], vaccines = [("RSV" , datetime .date (2024 , 1 , 3 ))]
1374+ nhs_number ,
1375+ date_of_birth = date_of_birth ,
1376+ cohorts = ["cohort_1" ],
1377+ vaccines = {"RSV" : {"LAST_SUCCESSFUL_DATE" : datetime .date (2024 , 1 , 3 ).strftime ("%Y%m%d" )}},
12811378 )
12821379
12831380 target_attribute_token = "LAST_SUCCESSFUL_DATE: [[TARGET.RSV.LAST_SUCCESSFUL_DATE:INVALID_DATE_FORMAT(%d %B %Y)]]" # noqa: S105
@@ -1309,7 +1406,10 @@ def test_eligibility_status_with_invalid_person_attribute_name_raises_value_erro
13091406 date_of_birth = DateOfBirth (datetime .date (2025 , 5 , 10 ))
13101407
13111408 person_rows = person_rows_builder (
1312- nhs_number , date_of_birth = date_of_birth , cohorts = ["cohort_1" ], vaccines = [("RSV" , datetime .date (2024 , 1 , 3 ))]
1409+ nhs_number ,
1410+ date_of_birth = date_of_birth ,
1411+ cohorts = ["cohort_1" ],
1412+ vaccines = {"RSV" : {"LAST_SUCCESSFUL_DATE" : datetime .date (2024 , 1 , 3 ).strftime ("%Y%m%d" )}},
13131413 )
13141414
13151415 target_attribute_token = "LAST_SUCCESSFUL_DATE: [[TARGET.RSV.ICECREAM]]" # noqa: S105
0 commit comments