Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,23 @@ class EligibilityCalculator:
def active_campaigns(self) -> list[rules.CampaignConfig]:
return [cc for cc in self.campaign_configs if cc.campaign_live]

@property
def campaigns_grouped_by_condition_name(
self,
self, conditions: list[str], category: str
) -> Iterator[tuple[eligibility.ConditionName, list[rules.CampaignConfig]]]:
"""Generator function to iterate over campaign groups by condition name."""
"""Generator that yields campaign groups filtered by condition names and campaign category."""

allowed_types = (
{"V", "S"} if category == "ALL" else {category[0]} if category in {"VACCINATIONS", "SCREENING"} else set()
)
filter_all_conditions = "ALL" in conditions

for condition_name, campaign_group in groupby(
sorted(self.active_campaigns, key=attrgetter("target")),
key=attrgetter("target"),
):
yield condition_name, list(campaign_group)
campaigns = list(campaign_group)
if campaigns[0].type in allowed_types and (filter_all_conditions or str(condition_name) in conditions):
yield condition_name, campaigns

@property
def person_cohorts(self) -> set[str]:
Expand Down Expand Up @@ -137,13 +144,15 @@ def get_redirect_rules(
action_mapper = active_iteration.actions_mapper
return redirect_rules, action_mapper, default_comms

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

for condition_name, campaign_group in self.campaigns_grouped_by_condition_name:
for condition_name, campaign_group in self.campaigns_grouped_by_condition_name(conditions, category):
best_active_iteration: Iteration | None
best_candidate: IterationResult
best_campaign_id: CampaignID | None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ def __init__(
self.calculator_factory = calculator_factory

def get_eligibility_status(
self, nhs_number: eligibility.NHSNumber | None = None, *, include_actions_flag: bool = True
self,
nhs_number: eligibility.NHSNumber,
include_actions: str,
conditions: list[str],
category: str,
) -> eligibility.EligibilityStatus:
"""Calculate a person's eligibility for vaccination given an NHS number."""
if nhs_number:
Expand All @@ -51,6 +55,6 @@ def get_eligibility_status(
raise UnknownPersonError from e
else:
calc: calculator.EligibilityCalculator = self.calculator_factory.get(person_data, campaign_configs)
return calc.evaluate_eligibility(include_actions_flag=include_actions_flag)
return calc.evaluate_eligibility(include_actions, conditions, category)

raise UnknownPersonError # pragma: no cover
42 changes: 35 additions & 7 deletions src/eligibility_signposting_api/views/eligibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import uuid
from datetime import UTC, datetime
from http import HTTPStatus
from typing import Any

from flask import Blueprint, make_response, request
from flask.typing import ResponseReturnValue
Expand Down Expand Up @@ -45,8 +46,12 @@ def check_eligibility(
) -> ResponseReturnValue:
logger.info("checking nhs_number %r in %r", nhs_number, eligibility_service, extra={"nhs_number": nhs_number})
try:
query_params = get_or_default_query_params()
eligibility_status = eligibility_service.get_eligibility_status(
nhs_number, include_actions_flag=get_include_actions_flag()
nhs_number,
query_params["includeActions"],
query_params["conditions"],
query_params["category"],
)
except UnknownPersonError:
return handle_unknown_person_error(nhs_number)
Expand All @@ -58,13 +63,36 @@ def check_eligibility(
)


def get_include_actions_flag() -> bool:
if not request.args.get("includeActions"):
logger.info("Defaulting includeActions query param to Y as no value was provided")
include_actions_flag = True
def get_or_default_query_params() -> dict[str, Any]:
default_query_params = {"category": "ALL", "conditions": ["ALL"], "includeActions": "Y"}

if not request.args:
logger.info("Defaulting all query params as no value was provided, using values %s", default_query_params)
return default_query_params

raw_args = request.args.to_dict()
query_params: dict[str, Any] = {}

include_actions = raw_args.get("includeActions")
query_params["includeActions"] = (
include_actions.upper() if include_actions else default_query_params["includeActions"]
)
if include_actions is None:
logger.info("Defaulting includeActions query param to 'Y' as no value was provided")

category = raw_args.get("category")
query_params["category"] = category.upper() if category else default_query_params["category"]
if category is None:
logger.info("Defaulting category query param to 'ALL' as no value was provided")

conditions_str = raw_args.get("conditions")
if conditions_str:
query_params["conditions"] = conditions_str.upper().split(",")
else:
include_actions_flag = request.args.get("includeActions") == "Y"
return include_actions_flag
query_params["conditions"] = default_query_params["conditions"]
logger.info("Defaulting conditions query param to 'ALL' as no value was provided")

return query_params


def handle_unknown_person_error(nhs_number: NHSNumber) -> ResponseReturnValue:
Expand Down
1 change: 1 addition & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ def multiple_campaign_configs(s3_client: BaseClient, rules_bucket: BucketName) -
campaign = rule.CampaignConfigFactory.build(
name=f"campaign_{i}",
target=targets[i],
type="V",
iterations=[
rule.IterationFactory.build(
iteration_rules=target_rules_map.get(targets[i]),
Expand Down
10 changes: 7 additions & 3 deletions tests/integration/lambda/test_app_running_as_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,11 @@ def test_given_nhs_number_in_path_matches_with_nhs_number_in_headers_and_check_i
messages = get_log_messages(flask_function, logs_client)
assert_that(
messages,
has_item(contains_string("Defaulting includeActions query param to Y as no value was provided")),
has_item(contains_string("Defaulting category query param to 'ALL' as no value was provided")),
)
assert_that(
messages,
has_item(contains_string("Defaulting conditions query param to 'ALL' as no value was provided")),
)


Expand Down Expand Up @@ -418,7 +422,7 @@ def test_given_person_has_unique_status_for_different_conditions_with_audit( #
"nhsd_end_user_organisation_ods": "nhsd_end_user_organisation_ods",
"nhsd_application_id": "nhsd_application_id",
},
params={"includeActions": "Y"},
params={"includeActions": "Y", "category": "VACCINATIONS", "conditions": "COVID,FLU,RSV"},
timeout=10,
)

Expand All @@ -438,7 +442,7 @@ def test_given_person_has_unique_status_for_different_conditions_with_audit( #
"nhsdEndUserOrganisationOds": "nhsd_end_user_organisation_ods",
"nhsdApplicationId": "nhsd_application_id",
}
expected_query_params = {"category": None, "conditions": None, "includeActions": "Y"}
expected_query_params = {"category": "VACCINATIONS", "conditions": "COVID,FLU,RSV", "includeActions": "Y"}

rsv_campaign = multiple_campaign_configs[0]
covid_campaign = multiple_campaign_configs[1]
Expand Down
Loading