Skip to content

Commit cdd169a

Browse files
Already vaccinated scenarios (#95)
* Already vaccinated scenarios covered * resove params for vaccine tests/unit/services/calculators/test_eligibility_calculator.py Co-authored-by: Simon B <[email protected]> * Using IterationRuleFactory since it is used only one time tests/unit/services/calculators/test_eligibility_calculator.py Co-authored-by: Simon B <[email protected]> * incorporated review comments - removed redundant code --------- Co-authored-by: Simon B <[email protected]>
1 parent d2140fe commit cdd169a

File tree

4 files changed

+80
-2
lines changed

4 files changed

+80
-2
lines changed

src/eligibility_signposting_api/model/rules.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
RuleDescription = NewType("RuleDescription", str)
2424
RulePriority = NewType("RulePriority", int)
2525
RuleAttributeName = NewType("RuleAttributeName", str)
26+
RuleAttributeTarget = NewType("RuleAttributeTarget", str)
2627
RuleComparator = NewType("RuleComparator", str)
2728
StartDate = NewType("StartDate", date)
2829
EndDate = NewType("EndDate", date)
@@ -96,7 +97,7 @@ class IterationRule(BaseModel):
9697
cohort_label: CohortLabel | None = Field(None, alias="CohortLabel")
9798
operator: RuleOperator = Field(..., alias="Operator")
9899
comparator: RuleComparator = Field(..., alias="Comparator")
99-
attribute_target: str | None = Field(None, alias="AttributeTarget")
100+
attribute_target: RuleAttributeTarget | None = Field(None, alias="AttributeTarget")
100101

101102
model_config = {"populate_by_name": True, "extra": "ignore"}
102103

src/eligibility_signposting_api/services/calculators/rule_calculator.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ def get_attribute_value(self) -> str | None:
4040
(r for r in self.person_data if r.get("ATTRIBUTE_TYPE", "") == "PERSON"), None
4141
)
4242
attribute_value = person.get(self.rule.attribute_name) if person else None
43+
case rules.RuleAttributeLevel.TARGET:
44+
target: Mapping[str, str | None] | None = next(
45+
(r for r in self.person_data if r.get("ATTRIBUTE_TYPE", "") == self.rule.attribute_target), None
46+
)
47+
attribute_value = target.get(self.rule.attribute_name) if target else None
4348
case _: # pragma: no cover
4449
msg = f"{self.rule.attribute_level} not implemented"
4550
raise NotImplementedError(msg)

tests/fixtures/builders/repos/person.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ def person_rows_builder( # noqa:PLR0913
7979
{
8080
"NHS_NUMBER": key,
8181
"ATTRIBUTE_TYPE": vaccine,
82-
"LAST_SUCCESSFUL_DATE": last_successful_date.strftime("%Y%m%d"),
82+
"LAST_SUCCESSFUL_DATE": (
83+
last_successful_date.strftime("%Y%m%d") if last_successful_date else last_successful_date
84+
),
8385
"OPTOUT": choice(["Y", "N"]),
8486
"LAST_INVITE_DATE": faker.past_date("-5y").strftime("%Y%m%d"),
8587
}

tests/unit/services/calculators/test_eligibility_calculator.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from freezegun import freeze_time
66
from hamcrest import assert_that, contains_exactly, empty, has_item, has_items
77

8+
from eligibility_signposting_api.model import rules
89
from eligibility_signposting_api.model import rules as rules_model
910
from eligibility_signposting_api.model.eligibility import ConditionName, DateOfBirth, NHSNumber, Postcode, Status
1011
from eligibility_signposting_api.services.calculators.eligibility_calculator import EligibilityCalculator
@@ -616,3 +617,72 @@ def test_base_eligible_and_icb_example(
616617
has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(expected_status))
617618
),
618619
)
620+
621+
622+
@pytest.mark.parametrize(
623+
("last_successful_date", "expected_status", "test_comment"),
624+
[
625+
("20240101", Status.not_actionable, "last_successful_date is a past date"),
626+
("20250101", Status.not_actionable, "last_successful_date is today"),
627+
# Below is a non-ideal situation (might be due to a data entry error), so considered as actionable.
628+
("20260101", Status.actionable, "last_successful_date is a future date"),
629+
("", Status.actionable, "last_successful_date is empty"),
630+
(None, Status.actionable, "last_successful_date is none"),
631+
],
632+
)
633+
@freeze_time("2025-01-01")
634+
def test_not_actionable_status_on_target_when_last_successful_date_lte_today(
635+
last_successful_date, expected_status, test_comment, faker: Faker
636+
):
637+
# Given
638+
nhs_number = NHSNumber(faker.nhs_number())
639+
640+
target_rows = person_rows_builder(
641+
nhs_number,
642+
cohorts=["cohort1"],
643+
vaccines=[
644+
(
645+
"RSV",
646+
datetime.datetime.strptime(last_successful_date, "%Y%m%d").replace(tzinfo=datetime.UTC)
647+
if last_successful_date
648+
else None,
649+
)
650+
],
651+
)
652+
653+
campaign_configs = [
654+
rule_builder.CampaignConfigFactory.build(
655+
target="RSV",
656+
iterations=[
657+
rule_builder.IterationFactory.build(
658+
iteration_rules=[
659+
rule_builder.IterationRuleFactory.build(
660+
type=rules.RuleType.suppression,
661+
name=rules.RuleName("You have already been vaccinated against RSV"),
662+
description=rules.RuleDescription("Exclude anyone Completed RSV Vaccination"),
663+
operator=rules.RuleOperator.day_lte,
664+
attribute_level=rules.RuleAttributeLevel.TARGET,
665+
attribute_name=rules.RuleAttributeName("LAST_SUCCESSFUL_DATE"),
666+
comparator=rules.RuleComparator("0"),
667+
attribute_target=rules.RuleAttributeTarget("RSV"),
668+
)
669+
],
670+
iteration_cohorts=[rule_builder.IterationCohortFactory.build(cohort_label="cohort1")],
671+
)
672+
],
673+
)
674+
]
675+
676+
calculator = EligibilityCalculator(target_rows, campaign_configs)
677+
678+
# When
679+
actual = calculator.evaluate_eligibility()
680+
681+
# Then
682+
assert_that(
683+
actual,
684+
is_eligibility_status().with_conditions(
685+
has_item(is_condition().with_condition_name(ConditionName("RSV")).and_status(expected_status))
686+
),
687+
test_comment,
688+
)

0 commit comments

Comments
 (0)