Skip to content

Commit 113b949

Browse files
committed
Injects person data reader and campaign processor into eligibility calculator
1 parent 0405260 commit 113b949

13 files changed

Lines changed: 31 additions & 87 deletions

File tree

src/eligibility_signposting_api/services/calculators/eligibility_calculator.py

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from typing import TYPE_CHECKING, Any
99

1010
from eligibility_signposting_api.audit.audit_context import AuditContext
11+
from eligibility_signposting_api.services.processors.campaign_evaluator import CampaignEvaluator
12+
from eligibility_signposting_api.services.processors.person_data_reader import PersonDataReader
1113

1214
if TYPE_CHECKING:
1315
from eligibility_signposting_api.model.rules import (
@@ -58,42 +60,12 @@ class EligibilityCalculator:
5860
person_data: Row
5961
campaign_configs: Collection[rules.CampaignConfig]
6062

61-
results: list[eligibility_status.Condition] = field(default_factory=list)
62-
63-
@property
64-
def active_campaigns(self) -> list[rules.CampaignConfig]:
65-
return [cc for cc in self.campaign_configs if cc.campaign_live]
66-
67-
def campaigns_grouped_by_condition_name(
68-
self, conditions: list[str], category: str
69-
) -> Iterator[tuple[eligibility_status.ConditionName, list[rules.CampaignConfig]]]:
70-
"""Generator that yields campaign groups filtered by condition names and campaign category."""
71-
72-
mapping = {
73-
"ALL": {"V", "S"},
74-
"VACCINATIONS": {"V"},
75-
"SCREENING": {"S"},
76-
}
77-
78-
allowed_types = mapping.get(category, set())
63+
campaign_evaluator: CampaignEvaluator = field(default_factory=CampaignEvaluator)
64+
person_data_reader: PersonDataReader = field(default_factory=PersonDataReader)
7965

80-
filter_all_conditions = "ALL" in conditions
66+
results: list[eligibility_status.Condition] = field(default_factory=list)
8167

82-
for condition_name, campaign_group in groupby(
83-
sorted(self.active_campaigns, key=attrgetter("target")),
84-
key=attrgetter("target"),
85-
):
86-
campaigns = list(campaign_group)
87-
if campaigns[0].type in allowed_types and (filter_all_conditions or str(condition_name) in conditions):
88-
yield condition_name, campaigns
8968

90-
@property
91-
def person_cohorts(self) -> set[str]:
92-
cohorts_row: Mapping[str, dict[str, dict[str, dict[str, Any]]]] = next(
93-
(row for row in self.person_data if row.get("ATTRIBUTE_TYPE") == "COHORTS"),
94-
{},
95-
)
96-
return set(cohorts_row.get("COHORT_MAP", {}).get("cohorts", {}).get("M", {}).keys())
9769

9870
@staticmethod
9971
def get_the_best_cohort_memberships(
@@ -164,7 +136,10 @@ def evaluate_eligibility(
164136
actions: list[SuggestedAction] | None = []
165137
action_rule_priority, action_rule_name = None, None
166138

167-
for condition_name, campaign_group in self.campaigns_grouped_by_condition_name(conditions, category):
139+
requested_grouped_campaigns = self.campaign_evaluator.get_requested_grouped_campaigns(self.campaign_configs,
140+
conditions,
141+
category)
142+
for condition_name, campaign_group in requested_grouped_campaigns:
168143
best_active_iteration: Iteration | None
169144
best_candidate: IterationResult
170145
best_campaign_id: CampaignID | None
@@ -289,7 +264,8 @@ def get_cohort_results(self, active_iteration: rules.Iteration) -> dict[str, Coh
289264
filter_rules, suppression_rules = self.get_rules_by_type(active_iteration)
290265
for cohort in sorted(active_iteration.iteration_cohorts, key=attrgetter("priority")):
291266
# Base Eligibility - check
292-
if cohort.cohort_label in self.person_cohorts or cohort.is_magic_cohort:
267+
person_cohorts = self.person_data_reader.get_person_cohorts(self.person_data)
268+
if cohort.cohort_label in person_cohorts or cohort.is_magic_cohort:
293269
# Eligibility - check
294270
if self.is_eligible_by_filter_rules(cohort, cohort_results, filter_rules):
295271
# Actionability - evaluation

src/eligibility_signposting_api/services/calculators/rule_calculator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from hamcrest.core.string_description import StringDescription
88

99
from eligibility_signposting_api.model import eligibility_status, rules
10-
from eligibility_signposting_api.services.rules.operators import OperatorRegistry
10+
from eligibility_signposting_api.services.operators.operators import OperatorRegistry
1111

1212
Row = Collection[Mapping[str, Any]]
1313

src/eligibility_signposting_api/services/rules/__init__.py renamed to src/eligibility_signposting_api/services/operators/__init__.py

File renamed without changes.

src/eligibility_signposting_api/services/rules/operators.py renamed to src/eligibility_signposting_api/services/operators/operators.py

File renamed without changes.

src/eligibility_signposting_api/services/processors/__init__.py

Whitespace-only changes.

src/eligibility_signposting_api/services/campaign_evaluator.py renamed to src/eligibility_signposting_api/services/processors/campaign_evaluator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class CampaignEvaluator:
1414
def get_active_campaigns(self, campaign_configs: Collection[rules.CampaignConfig]) -> list[rules.CampaignConfig]:
1515
return [cc for cc in campaign_configs if cc.campaign_live]
1616

17-
def campaigns_grouped_by_condition_name(
17+
def get_requested_grouped_campaigns(
1818
self, campaign_configs: Collection[rules.CampaignConfig], conditions: list[str], category: str
1919
) -> Iterator[tuple[eligibility_status.ConditionName, list[rules.CampaignConfig]]]:
2020
mapping = {

src/eligibility_signposting_api/services/person_data_reader.py renamed to src/eligibility_signposting_api/services/processors/person_data_reader.py

File renamed without changes.

tests/fixtures/builders/model/rule.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
from polyfactory import Use
66
from polyfactory.factories.pydantic_factory import ModelFactory
77

8-
from eligibility_signposting_api.model import rules
8+
from eligibility_signposting_api.model import rules, eligibility_status
9+
from eligibility_signposting_api.model.eligibility_status import Reason, RuleName, RuleType, RulePriority, \
10+
RuleDescription
911

1012

1113
def past_date(days_behind: int = 365) -> date:

tests/unit/services/calculators/test_eligibility_calculator.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2383,38 +2383,6 @@ def test_should_not_include_actions_when_include_actions_flag_is_false_when_stat
23832383
)
23842384

23852385

2386-
@pytest.mark.parametrize(
2387-
("campaign_target", "campaign_type", "conditions_filter", "category_filter", "expected_result"),
2388-
[
2389-
# Multiple matching campaigns under the same condition
2390-
("RSV", "V", ["RSV"], "VACCINATIONS", [("RSV", "V")]),
2391-
("RSV", "V", ["COVID"], "VACCINATIONS", []),
2392-
("RSV", "S", ["RSV"], "ALL", [("RSV", "S")]),
2393-
("RSV", "S", ["ALL"], "ALL", [("RSV", "S")]),
2394-
("RSV", "S", ["RSV"], "VACCINATIONS", []),
2395-
# Multiple campaigns with different types under the same condition name
2396-
("RSV", "V", ["RSV"], "ALL", [("RSV", "V")]),
2397-
# Campaign is live but condition not in filter (no yield)
2398-
("FLU", "V", ["COVID", "RSV"], "ALL", []),
2399-
# Category is ALL and condition filter includes ALL (everything matches)
2400-
("FLU", "S", ["ALL"], "ALL", [("FLU", "S")]),
2401-
# Condition filter is unknown (should not match anything)
2402-
("COVID", "V", ["UNKNOWN"], "VACCINATIONS", []),
2403-
# Campaign with the target matching one of several condition filters
2404-
("FLU", "V", ["COVID", "FLU"], "VACCINATIONS", [("FLU", "V")]),
2405-
],
2406-
)
2407-
def test_campaigns_grouped_by_condition_name_filters_correctly(
2408-
campaign_target, campaign_type, conditions_filter, category_filter, expected_result
2409-
):
2410-
campaign = rule_builder.CampaignConfigFactory.build(target=campaign_target, type=campaign_type, campaign_live=True)
2411-
2412-
calculator = EligibilityCalculator(person_data=[], campaign_configs=[campaign])
2413-
result = list(calculator.campaigns_grouped_by_condition_name(conditions_filter, category_filter))
2414-
2415-
assert_that([(str(name), group[0].type) for name, group in result], is_(expected_result))
2416-
2417-
24182386
@pytest.mark.parametrize(
24192387
(
24202388
"test_comment",

tests/unit/services/operators/test_operators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from hamcrest import assert_that, equal_to
44

55
from eligibility_signposting_api.model.rules import RuleOperator
6-
from eligibility_signposting_api.services.rules.operators import Operator, OperatorRegistry
6+
from eligibility_signposting_api.services.operators.operators import Operator, OperatorRegistry
77

88
# Test cases: person_data, rule_operator, rule_value, expected, test_comment
99
cases: list[tuple[str | None, RuleOperator, str | None, bool, str]] = []

0 commit comments

Comments
 (0)