Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
@@ -1,6 +1,5 @@
from __future__ import annotations

from collections import defaultdict
from dataclasses import dataclass, field
from typing import TYPE_CHECKING

Expand All @@ -11,14 +10,14 @@
from eligibility_signposting_api.model.eligibility_status import (
BestIterationResult,
CohortGroupResult,
Condition,
ConditionName,
EligibilityStatus,
IterationResult,
Status,
)
from eligibility_signposting_api.services.processors.action_rule_handler import ActionRuleHandler
from eligibility_signposting_api.services.processors.campaign_evaluator import CampaignEvaluator
from eligibility_signposting_api.services.processors.eligibility_result_builder import EligibilityResultBuilder
from eligibility_signposting_api.services.processors.rule_processor import RuleProcessor

if TYPE_CHECKING:
Expand Down Expand Up @@ -47,6 +46,7 @@ class EligibilityCalculator:
campaign_evaluator: CampaignEvaluator = field(default_factory=CampaignEvaluator)
rule_processor: RuleProcessor = field(default_factory=RuleProcessor)
action_rule_handler: ActionRuleHandler = field(default_factory=ActionRuleHandler)
eligibility_result_builder: EligibilityResultBuilder = field(default_factory=EligibilityResultBuilder)

results: list[eligibility_status.Condition] = field(default_factory=list)

Expand Down Expand Up @@ -96,7 +96,7 @@ def get_eligibility_status(self, include_actions: str, conditions: list[str], ca
AuditContext.append_audit_condition(condition_name, best_iteration_result, matched_action_detail)

# Consolidate all the results and return
final_result = self.build_condition_results(condition_results)
final_result = self.eligibility_result_builder.build_condition_results(condition_results)
return eligibility_status.EligibilityStatus(conditions=final_result)

def get_best_iteration_result(self, campaign_group: list[CampaignConfig]) -> BestIterationResult:
Expand Down Expand Up @@ -131,41 +131,3 @@ def get_iteration_results(self, campaign_group: list[CampaignConfig]) -> dict[It
IterationResult(status, best_cohorts, []), active_iteration, cc.id, cc.version, cohort_results
)
return iteration_results

@staticmethod
def build_condition_results(condition_results: dict[ConditionName, IterationResult]) -> list[Condition]:
conditions: list[Condition] = []
# iterate over conditions
for condition_name, active_iteration_result in condition_results.items():
grouped_cohort_results = defaultdict(list)
# iterate over cohorts and group them by status and cohort_group
for cohort_result in active_iteration_result.cohort_results:
if active_iteration_result.status == cohort_result.status:
grouped_cohort_results[cohort_result.cohort_code].append(cohort_result)

# deduplicate grouped cohort results by cohort_code
deduplicated_cohort_results = [
CohortGroupResult(
cohort_code=group_cohort_code,
status=group[0].status,
# Flatten all reasons from the group
reasons=[reason for cohort in group for reason in cohort.reasons],
# get the first nonempty description
description=next((c.description for c in group if c.description), group[0].description),
audit_rules=[],
)
for group_cohort_code, group in grouped_cohort_results.items()
if group
]

# return condition with cohort results
conditions.append(
Condition(
condition_name=condition_name,
status=active_iteration_result.status,
cohort_results=list(deduplicated_cohort_results),
actions=condition_results[condition_name].actions,
status_text=active_iteration_result.status.get_status_text(condition_name),
)
)
return conditions
Comment thread
shweta-nhs marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from collections import defaultdict

from eligibility_signposting_api.model.eligibility_status import (
CohortGroupResult,
Condition,
ConditionName,
IterationResult,
)


class EligibilityResultBuilder:
@staticmethod
def build_condition_results(condition_results: dict[ConditionName, IterationResult]) -> list[Condition]:
conditions: list[Condition] = []
# iterate over conditions
for condition_name, active_iteration_result in condition_results.items():
grouped_cohort_results = defaultdict(list)
# iterate over cohorts and group them by status and cohort_group
for cohort_result in active_iteration_result.cohort_results:
if active_iteration_result.status == cohort_result.status:
grouped_cohort_results[cohort_result.cohort_code].append(cohort_result)

# deduplicate grouped cohort results by cohort_code
deduplicated_cohort_results = [
CohortGroupResult(
cohort_code=group_cohort_code,
status=group[0].status,
# Flatten all reasons from the group
reasons=[reason for cohort in group for reason in cohort.reasons],
# get the first nonempty description
description=next((c.description for c in group if c.description), group[0].description),
audit_rules=[],
)
for group_cohort_code, group in grouped_cohort_results.items()
if group
]

# return condition with cohort results
conditions.append(
Condition(
condition_name=condition_name,
status=active_iteration_result.status,
cohort_results=list(deduplicated_cohort_results),
actions=condition_results[condition_name].actions,
status_text=active_iteration_result.status.get_status_text(condition_name),
)
)
return conditions
262 changes: 262 additions & 0 deletions tests/unit/services/processors/test_eligibility_result_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
import pytest
from hamcrest import assert_that, contains_inanyorder, is_

from eligibility_signposting_api.model.eligibility_status import (
ActionCode,
ActionDescription,
ActionType,
CohortGroupResult,
ConditionName,
InternalActionCode,
IterationResult,
Reason,
RuleDescription,
RuleName,
RulePriority,
RuleType,
Status,
SuggestedAction,
)
from eligibility_signposting_api.services.processors.eligibility_result_builder import EligibilityResultBuilder


@pytest.fixture
def result_builder():
return EligibilityResultBuilder()


def test_build_condition_results_empty_input(result_builder):
condition_results = {}
result = result_builder.build_condition_results(condition_results)
assert_that(result, is_([]))


def test_build_condition_results_single_condition_single_cohort_actionable(result_builder):
cohort_group_results = [CohortGroupResult("COHORT_A", Status.actionable, [], "Cohort A Description", [])]
suggested_actions = [
SuggestedAction(
internal_action_code=InternalActionCode("default_action_code"),
action_type=ActionType("CareCardWithText"),
action_code=ActionCode("BookLocal"),
action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"),
url_link=None,
url_label=None,
)
]
iteration_result = IterationResult(Status.actionable, cohort_group_results, suggested_actions)

condition_results = {ConditionName("RSV"): iteration_result}

result = result_builder.build_condition_results(condition_results)

assert_that(len(result), is_(1))
assert_that(result[0].condition_name, is_(ConditionName("RSV")))
assert_that(result[0].status, is_(Status.actionable))
assert_that(result[0].actions, is_(suggested_actions))
assert_that(result[0].status_text, is_(Status.actionable.get_status_text(ConditionName("RSV"))))

assert_that(len(result[0].cohort_results), is_(1))
deduplicated_cohort = result[0].cohort_results[0]
assert_that(deduplicated_cohort.cohort_code, is_("COHORT_A"))
assert_that(deduplicated_cohort.status, is_(Status.actionable))
assert_that(deduplicated_cohort.reasons, is_([]))
assert_that(deduplicated_cohort.description, is_("Cohort A Description"))
assert_that(deduplicated_cohort.audit_rules, is_([]))


def test_build_condition_results_single_condition_single_cohort_not_eligible_with_reasons(result_builder):
cohort_group_results = [CohortGroupResult("COHORT_A", Status.not_eligible, [], "Cohort A Description", [])]
suggested_actions = [
SuggestedAction(
internal_action_code=InternalActionCode("default_action_code"),
action_type=ActionType("CareCardWithText"),
action_code=ActionCode("BookLocal"),
action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"),
url_link=None,
url_label=None,
)
]
iteration_result = IterationResult(Status.not_eligible, cohort_group_results, suggested_actions)

condition_results = {ConditionName("RSV"): iteration_result}

result = result_builder.build_condition_results(condition_results)

assert_that(len(result), is_(1))
assert_that(result[0].condition_name, is_(ConditionName("RSV")))
assert_that(result[0].status, is_(Status.not_eligible))
assert_that(result[0].actions, is_(suggested_actions))
assert_that(result[0].status_text, is_(Status.not_eligible.get_status_text(ConditionName("RSV"))))

assert_that(len(result[0].cohort_results), is_(1))
deduplicated_cohort = result[0].cohort_results[0]
assert_that(deduplicated_cohort.cohort_code, is_("COHORT_A"))
assert_that(deduplicated_cohort.status, is_(Status.not_eligible))
assert_that(deduplicated_cohort.reasons, is_([]))
assert_that(deduplicated_cohort.description, is_("Cohort A Description"))
assert_that(deduplicated_cohort.audit_rules, is_([]))


def test_build_condition_results_single_condition_multiple_cohorts_same_cohort_code_same_status(result_builder):
reason_1 = Reason(
RuleType.filter,
RuleName("Filter Rule 1"),
RulePriority("1"),
RuleDescription("Filter Rule Description 2"),
matcher_matched=True,
)
reason_2 = Reason(
RuleType.filter,
RuleName("Filter Rule 2"),
RulePriority("2"),
RuleDescription("Filter Rule Description 2"),
matcher_matched=True,
)
cohort_group_results = [
CohortGroupResult("COHORT_A", Status.not_eligible, [reason_1], "", []),
CohortGroupResult("COHORT_A", Status.not_eligible, [reason_2], "Cohort A Description 2", []),
CohortGroupResult("COHORT_A", Status.not_eligible, [], "Cohort A Description 3", []),
]
suggested_actions = [
SuggestedAction(
internal_action_code=InternalActionCode("default_action_code"),
action_type=ActionType("CareCardWithText"),
action_code=ActionCode("BookLocal"),
action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"),
url_link=None,
url_label=None,
)
]
iteration_result = IterationResult(Status.not_eligible, cohort_group_results, suggested_actions)

condition_results = {ConditionName("RSV"): iteration_result}

result = result_builder.build_condition_results(condition_results)

assert_that(len(result), is_(1))
condition = result[0]
assert_that(len(condition.cohort_results), is_(1))

deduplicated_cohort = condition.cohort_results[0]
assert_that(deduplicated_cohort.cohort_code, is_("COHORT_A"))
assert_that(deduplicated_cohort.status, is_(Status.not_eligible))
assert_that(deduplicated_cohort.reasons, contains_inanyorder(reason_1, reason_2))
assert_that(deduplicated_cohort.description, is_("Cohort A Description 2"))
assert_that(deduplicated_cohort.audit_rules, is_([]))


def test_build_condition_results_multiple_cohorts_different_cohort_code_same_status(result_builder):
reason_1 = Reason(
RuleType.filter,
RuleName("Filter Rule 1"),
RulePriority("1"),
RuleDescription("Filter Rule Description 2"),
matcher_matched=True,
)
reason_2 = Reason(
RuleType.filter,
RuleName("Filter Rule 2"),
RulePriority("2"),
RuleDescription("Filter Rule Description 2"),
matcher_matched=True,
)
cohort_group_results = [
CohortGroupResult("COHORT_X", Status.not_eligible, [reason_1], "Cohort X Description", []),
CohortGroupResult("COHORT_Y", Status.not_eligible, [reason_2], "Cohort Y Description", []),
]
suggested_actions = [
SuggestedAction(
internal_action_code=InternalActionCode("default_action_code"),
action_type=ActionType("CareCardWithText"),
action_code=ActionCode("BookLocal"),
action_description=ActionDescription("You can get an RSV vaccination at your GP surgery"),
url_link=None,
url_label=None,
)
]
iteration_result = IterationResult(Status.not_eligible, cohort_group_results, suggested_actions)

condition_results = {ConditionName("RSV"): iteration_result}

result = result_builder.build_condition_results(condition_results)

assert_that(len(result), is_(1))
condition = result[0]
assert_that(len(condition.cohort_results), is_(2))

expected_deduplicated_cohorts = [
CohortGroupResult("COHORT_X", Status.not_eligible, [reason_1], "Cohort X Description", []),
CohortGroupResult("COHORT_Y", Status.not_eligible, [reason_2], "Cohort Y Description", []),
]
assert_that(condition.cohort_results, contains_inanyorder(*expected_deduplicated_cohorts))


def test_build_condition_results_cohorts_status_not_matching_iteration_status(result_builder):
reason_1 = Reason(
RuleType.filter, RuleName("Filter Rule 1"), RulePriority("1"), RuleDescription("Matching"), matcher_matched=True
)
reason_2 = Reason(
RuleType.filter,
RuleName("Filter Rule 2"),
RulePriority("2"),
RuleDescription("Not matching"),
matcher_matched=True,
)
cohort_group_results = [
CohortGroupResult("COHORT_X", Status.not_eligible, [reason_1], "Cohort X Description", []),
CohortGroupResult("COHORT_Y", Status.not_actionable, [reason_2], "Cohort Y Description", []),
]

iteration_result = IterationResult(Status.not_eligible, cohort_group_results, [])

condition_results = {ConditionName("RSV"): iteration_result}

result = result_builder.build_condition_results(condition_results)

assert_that(len(result), is_(1))
condition = result[0]
assert_that(len(condition.cohort_results), is_(1))
assert_that(condition.cohort_results[0].cohort_code, is_("COHORT_X"))
assert_that(condition.cohort_results[0].status, is_(Status.not_eligible))


def test_build_condition_results_multiple_conditions(result_builder):
reason_1 = Reason(
RuleType.filter,
RuleName("Filter Rule 1"),
RulePriority("1"),
RuleDescription("Filter Rule Description 2"),
matcher_matched=True,
)
reason_2 = Reason(
RuleType.filter,
RuleName("Filter Rule 2"),
RulePriority("2"),
RuleDescription("Filter Rule Description 2"),
matcher_matched=True,
)
cohort_group_result1 = [CohortGroupResult("RSV_COHORT", Status.not_eligible, [reason_1], "RSV Desc", [])]
cohort_group_result2 = [CohortGroupResult("COVID_COHORT", Status.not_actionable, [reason_2], "Covid Desc", [])]

iteration_result1 = IterationResult(Status.not_eligible, cohort_group_result1, [])

iteration_result2 = IterationResult(Status.not_actionable, cohort_group_result2, [])

condition_results = {
ConditionName("RSV"): iteration_result1,
ConditionName("COVID"): iteration_result2,
}

result = result_builder.build_condition_results(condition_results)

rsv = next((c for c in result if c.condition_name == ConditionName("RSV")), None)
assert_that(rsv.status, is_(Status.not_eligible))
assert_that(len(rsv.cohort_results), is_(1))
assert_that(rsv.cohort_results[0].cohort_code, is_("RSV_COHORT"))
assert_that(rsv.cohort_results[0].reasons, is_([reason_1]))

covid = next((c for c in result if c.condition_name == ConditionName("COVID")), None)
assert_that(covid.status, is_(Status.not_actionable))
assert_that(len(covid.cohort_results), is_(1))
assert_that(covid.cohort_results[0].cohort_code, is_("COVID_COHORT"))
assert_that(covid.cohort_results[0].reasons, is_([reason_2]))
Loading