Skip to content

Commit 63f3303

Browse files
authored
ELI-320: Implements category and condition attribute filters (#231)
1 parent a67adf2 commit 63f3303

8 files changed

Lines changed: 200 additions & 74 deletions

File tree

src/eligibility_signposting_api/services/calculators/eligibility_calculator.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,23 @@ class EligibilityCalculator:
6262
def active_campaigns(self) -> list[rules.CampaignConfig]:
6363
return [cc for cc in self.campaign_configs if cc.campaign_live]
6464

65-
@property
6665
def campaigns_grouped_by_condition_name(
67-
self,
66+
self, conditions: list[str], category: str
6867
) -> Iterator[tuple[eligibility.ConditionName, list[rules.CampaignConfig]]]:
69-
"""Generator function to iterate over campaign groups by condition name."""
68+
"""Generator that yields campaign groups filtered by condition names and campaign category."""
69+
70+
allowed_types = (
71+
{"V", "S"} if category == "ALL" else {category[0]} if category in {"VACCINATIONS", "SCREENING"} else set()
72+
)
73+
filter_all_conditions = "ALL" in conditions
74+
7075
for condition_name, campaign_group in groupby(
7176
sorted(self.active_campaigns, key=attrgetter("target")),
7277
key=attrgetter("target"),
7378
):
74-
yield condition_name, list(campaign_group)
79+
campaigns = list(campaign_group)
80+
if campaigns[0].type in allowed_types and (filter_all_conditions or str(condition_name) in conditions):
81+
yield condition_name, campaigns
7582

7683
@property
7784
def person_cohorts(self) -> set[str]:
@@ -137,13 +144,15 @@ def get_redirect_rules(
137144
action_mapper = active_iteration.actions_mapper
138145
return redirect_rules, action_mapper, default_comms
139146

140-
def evaluate_eligibility(self, *, include_actions_flag: bool = True) -> eligibility.EligibilityStatus:
141-
"""Iterates over campaign groups, evaluates eligibility, and returns a consolidated status."""
147+
def evaluate_eligibility(
148+
self, include_actions: str, conditions: list[str], category: str
149+
) -> eligibility.EligibilityStatus:
150+
include_actions_flag = include_actions.upper() == "Y"
142151
condition_results: dict[ConditionName, IterationResult] = {}
143152
actions: list[SuggestedAction] | None = []
144153
redirect_rule_priority, redirect_rule_name = None, None
145154

146-
for condition_name, campaign_group in self.campaigns_grouped_by_condition_name:
155+
for condition_name, campaign_group in self.campaigns_grouped_by_condition_name(conditions, category):
147156
best_active_iteration: Iteration | None
148157
best_candidate: IterationResult
149158
best_campaign_id: CampaignID | None

src/eligibility_signposting_api/services/eligibility_services.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ def __init__(
3131
self.calculator_factory = calculator_factory
3232

3333
def get_eligibility_status(
34-
self, nhs_number: eligibility.NHSNumber | None = None, *, include_actions_flag: bool = True
34+
self,
35+
nhs_number: eligibility.NHSNumber,
36+
include_actions: str,
37+
conditions: list[str],
38+
category: str,
3539
) -> eligibility.EligibilityStatus:
3640
"""Calculate a person's eligibility for vaccination given an NHS number."""
3741
if nhs_number:
@@ -51,6 +55,6 @@ def get_eligibility_status(
5155
raise UnknownPersonError from e
5256
else:
5357
calc: calculator.EligibilityCalculator = self.calculator_factory.get(person_data, campaign_configs)
54-
return calc.evaluate_eligibility(include_actions_flag=include_actions_flag)
58+
return calc.evaluate_eligibility(include_actions, conditions, category)
5559

5660
raise UnknownPersonError # pragma: no cover

src/eligibility_signposting_api/views/eligibility.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import uuid
33
from datetime import UTC, datetime
44
from http import HTTPStatus
5+
from typing import Any
56

67
from flask import Blueprint, make_response, request
78
from flask.typing import ResponseReturnValue
@@ -45,8 +46,12 @@ def check_eligibility(
4546
) -> ResponseReturnValue:
4647
logger.info("checking nhs_number %r in %r", nhs_number, eligibility_service, extra={"nhs_number": nhs_number})
4748
try:
49+
query_params = get_or_default_query_params()
4850
eligibility_status = eligibility_service.get_eligibility_status(
49-
nhs_number, include_actions_flag=get_include_actions_flag()
51+
nhs_number,
52+
query_params["includeActions"],
53+
query_params["conditions"],
54+
query_params["category"],
5055
)
5156
except UnknownPersonError:
5257
return handle_unknown_person_error(nhs_number)
@@ -58,13 +63,36 @@ def check_eligibility(
5863
)
5964

6065

61-
def get_include_actions_flag() -> bool:
62-
if not request.args.get("includeActions"):
63-
logger.info("Defaulting includeActions query param to Y as no value was provided")
64-
include_actions_flag = True
66+
def get_or_default_query_params() -> dict[str, Any]:
67+
default_query_params = {"category": "ALL", "conditions": ["ALL"], "includeActions": "Y"}
68+
69+
if not request.args:
70+
logger.info("Defaulting all query params as no value was provided, using values %s", default_query_params)
71+
return default_query_params
72+
73+
raw_args = request.args.to_dict()
74+
query_params: dict[str, Any] = {}
75+
76+
include_actions = raw_args.get("includeActions")
77+
query_params["includeActions"] = (
78+
include_actions.upper() if include_actions else default_query_params["includeActions"]
79+
)
80+
if include_actions is None:
81+
logger.info("Defaulting includeActions query param to 'Y' as no value was provided")
82+
83+
category = raw_args.get("category")
84+
query_params["category"] = category.upper() if category else default_query_params["category"]
85+
if category is None:
86+
logger.info("Defaulting category query param to 'ALL' as no value was provided")
87+
88+
conditions_str = raw_args.get("conditions")
89+
if conditions_str:
90+
query_params["conditions"] = conditions_str.upper().split(",")
6591
else:
66-
include_actions_flag = request.args.get("includeActions") == "Y"
67-
return include_actions_flag
92+
query_params["conditions"] = default_query_params["conditions"]
93+
logger.info("Defaulting conditions query param to 'ALL' as no value was provided")
94+
95+
return query_params
6896

6997

7098
def handle_unknown_person_error(nhs_number: NHSNumber) -> ResponseReturnValue:

tests/integration/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ def multiple_campaign_configs(s3_client: BaseClient, rules_bucket: BucketName) -
496496
campaign = rule.CampaignConfigFactory.build(
497497
name=f"campaign_{i}",
498498
target=targets[i],
499+
type="V",
499500
iterations=[
500501
rule.IterationFactory.build(
501502
iteration_rules=target_rules_map.get(targets[i]),

tests/integration/lambda/test_app_running_as_lambda.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,11 @@ def test_given_nhs_number_in_path_matches_with_nhs_number_in_headers_and_check_i
264264
messages = get_log_messages(flask_function, logs_client)
265265
assert_that(
266266
messages,
267-
has_item(contains_string("Defaulting includeActions query param to Y as no value was provided")),
267+
has_item(contains_string("Defaulting category query param to 'ALL' as no value was provided")),
268+
)
269+
assert_that(
270+
messages,
271+
has_item(contains_string("Defaulting conditions query param to 'ALL' as no value was provided")),
268272
)
269273

270274

@@ -418,7 +422,7 @@ def test_given_person_has_unique_status_for_different_conditions_with_audit( #
418422
"nhsd_end_user_organisation_ods": "nhsd_end_user_organisation_ods",
419423
"nhsd_application_id": "nhsd_application_id",
420424
},
421-
params={"includeActions": "Y"},
425+
params={"includeActions": "Y", "category": "VACCINATIONS", "conditions": "COVID,FLU,RSV"},
422426
timeout=10,
423427
)
424428

@@ -438,7 +442,7 @@ def test_given_person_has_unique_status_for_different_conditions_with_audit( #
438442
"nhsdEndUserOrganisationOds": "nhsd_end_user_organisation_ods",
439443
"nhsdApplicationId": "nhsd_application_id",
440444
}
441-
expected_query_params = {"category": None, "conditions": None, "includeActions": "Y"}
445+
expected_query_params = {"category": "VACCINATIONS", "conditions": "COVID,FLU,RSV", "includeActions": "Y"}
442446

443447
rsv_campaign = multiple_campaign_configs[0]
444448
covid_campaign = multiple_campaign_configs[1]

0 commit comments

Comments
 (0)