Skip to content

Commit 60583a7

Browse files
authored
ELI-405: Update audit rules for filter and suppression regardless of … (#295)
* ELI-405: Update audit rules for filter and suppression regardless of status * ELI-405: Updates tests * ELI-405: Fix formatting
1 parent 2593bed commit 60583a7

File tree

2 files changed

+155
-42
lines changed

2 files changed

+155
-42
lines changed

src/eligibility_signposting_api/audit/audit_context.py

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
IterationResult,
2626
MatchedActionDetail,
2727
Reason,
28-
Status,
28+
RuleType,
2929
SuggestedAction,
3030
)
3131

@@ -86,14 +86,14 @@ def append_audit_condition(
8686

8787
filter_audit_rules, suitability_audit_rules = [], []
8888
for result in cohort_results:
89-
if result.status.name == Status.not_eligible.name:
90-
filter_audit_rules.extend(result.audit_rules)
91-
if result.status.name == Status.not_actionable.name:
92-
suitability_audit_rules.extend(result.audit_rules)
89+
for rule in result.audit_rules:
90+
if rule.rule_type == RuleType.filter:
91+
filter_audit_rules.append(rule)
92+
if rule.rule_type == RuleType.suppression:
93+
suitability_audit_rules.append(rule)
9394

9495
audit_filter_rule = AuditContext.create_audit_filter_rule(filter_audit_rules)
9596
audit_suitability_rule = AuditContext.create_audit_suitability_rule(suitability_audit_rules)
96-
9797
audit_action_rule = AuditContext.add_rule_name_and_priority_to_audit(best_candidate, action_detail)
9898

9999
audit_actions = AuditContext.create_audit_actions(action_detail.actions)
@@ -186,20 +186,8 @@ def create_audit_filter_rule(reasons: list[Reason]) -> list[AuditFilterRule] | N
186186

187187
@staticmethod
188188
def deduplicate_reasons(reasons: list[Reason]) -> list[Reason]:
189-
unique_rule_codes = set()
190-
deduplicated_reasons = []
191-
189+
deduped = {}
192190
for reason in reasons:
193-
if reason.rule_name not in unique_rule_codes and reason.rule_description:
194-
unique_rule_codes.add(reason.rule_name)
195-
deduplicated_reasons.append(
196-
Reason(
197-
reason.rule_type,
198-
reason.rule_name,
199-
reason.rule_priority,
200-
reason.rule_description,
201-
reason.matcher_matched,
202-
)
203-
)
204-
205-
return deduplicated_reasons
191+
key = (reason.rule_type, reason.rule_priority)
192+
deduped.setdefault(key, reason)
193+
return list(deduped.values())

tests/unit/audit/test_audit_context.py

Lines changed: 145 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from eligibility_signposting_api.audit.audit_context import AuditContext
1010
from eligibility_signposting_api.audit.audit_models import AuditAction, AuditEvent
1111
from eligibility_signposting_api.audit.audit_service import AuditService
12-
from eligibility_signposting_api.model.campaign_config import CampaignID, CampaignVersion, RuleType
12+
from eligibility_signposting_api.model import campaign_config
13+
from eligibility_signposting_api.model.campaign_config import CampaignID, CampaignVersion, CohortLabel, RuleType
1314
from eligibility_signposting_api.model.eligibility_status import (
1415
ActionCode,
1516
ActionDescription,
@@ -82,7 +83,7 @@ def test_add_request_details_when_headers_are_empty_sets_audit_log_on_g(app):
8283
assert isinstance(audit_req.request_timestamp, datetime)
8384

8485

85-
def test_append_audit_condition_adds_condition_to_audit_log_on_g(app):
86+
def test_append_audit_condition_adds_condition_to_audit_log_on_g_for_actionable_status(app):
8687
suggested_actions: list[SuggestedAction] | None
8788
condition_name: ConditionName
8889
campaign_details: tuple[CampaignID | None, CampaignVersion | None]
@@ -101,13 +102,27 @@ def test_append_audit_condition_adds_condition_to_audit_log_on_g(app):
101102
condition_name = ConditionName("Condition1")
102103
iteration = IterationFactory.build(version=12345)
103104
audit_rules = [
105+
Reason(
106+
rule_type=RuleType.redirect,
107+
rule_name=RuleName("RedirectRuleName1"),
108+
rule_description=RuleDescription("RedirectRuleDescription1"),
109+
matcher_matched=True,
110+
rule_priority=RulePriority("1"),
111+
),
104112
Reason(
105113
rule_type=RuleType.filter,
106114
rule_name=RuleName("FilterRuleName1"),
107115
rule_description=RuleDescription("FilterRuleDescription1"),
108116
matcher_matched=True,
109117
rule_priority=RulePriority("1"),
110-
)
118+
),
119+
Reason(
120+
rule_type=RuleType.suppression,
121+
rule_name=RuleName("SuppressionRuleName1"),
122+
rule_description=RuleDescription("SuppressionRuleDescription1"),
123+
matcher_matched=True,
124+
rule_priority=RulePriority("1"),
125+
),
111126
]
112127
cohort_group_result = CohortGroupResult(
113128
status=Status.actionable,
@@ -120,10 +135,16 @@ def test_append_audit_condition_adds_condition_to_audit_log_on_g(app):
120135
status=Status.actionable, cohort_results=[cohort_group_result], actions=suggested_actions
121136
)
122137
campaign_details = (CampaignID("CampaignID1"), CampaignVersion(123))
123-
matched_action_detail = MatchedActionDetail(RuleName("RedirectRuleName1"), RulePriority("1"), suggested_actions)
138+
matched_action_detail = MatchedActionDetail(
139+
campaign_config.RuleName("RedirectRuleName1"), campaign_config.RulePriority(1), suggested_actions
140+
)
124141

125142
best_iteration_results = BestIterationResult(
126-
iteration_result, iteration, campaign_details[0], campaign_details[1], {"CohortCode1": cohort_group_result}
143+
iteration_result,
144+
iteration,
145+
campaign_details[0],
146+
campaign_details[1],
147+
{CohortLabel("CohortCode1"): cohort_group_result},
127148
)
128149

129150
with app.app_context():
@@ -156,7 +177,68 @@ def test_append_audit_condition_adds_condition_to_audit_log_on_g(app):
156177
assert cond.actions == expected_audit_action
157178
assert cond.action_rule.rule_priority == "1"
158179
assert cond.action_rule.rule_name == "RedirectRuleName1"
159-
assert cond.suitability_rules is None
180+
assert len(cond.suitability_rules) == 1
181+
assert cond.suitability_rules[0].rule_priority == "1"
182+
assert cond.suitability_rules[0].rule_name == "SuppressionRuleName1"
183+
assert cond.suitability_rules[0].rule_message == "SuppressionRuleDescription1"
184+
assert cond.filter_rules[0].rule_priority == "1"
185+
assert cond.filter_rules[0].rule_name == "FilterRuleName1"
186+
assert cond.eligibility_cohorts[0].cohort_code == "CohortCode1"
187+
assert cond.eligibility_cohorts[0].cohort_status == "actionable"
188+
assert cond.eligibility_cohort_groups[0].cohort_code == "CohortCode1"
189+
assert cond.eligibility_cohort_groups[0].cohort_status == "actionable"
190+
assert cond.eligibility_cohort_groups[0].cohort_text == "CohortDescription1"
191+
192+
193+
def test_should_append_audit_suppression_rules_for_actionable_status(app):
194+
condition_name: ConditionName
195+
campaign_details: tuple[CampaignID | None, CampaignVersion | None]
196+
197+
condition_name = ConditionName("Condition1")
198+
iteration = IterationFactory.build()
199+
audit_rules = [
200+
Reason(
201+
rule_type=RuleType.suppression,
202+
rule_name=RuleName("SuppressionRuleName1"),
203+
rule_description=RuleDescription("SuppressionRuleDescription1"),
204+
matcher_matched=True,
205+
rule_priority=RulePriority("1"),
206+
)
207+
]
208+
cohort_group_result = CohortGroupResult(
209+
status=Status.actionable,
210+
cohort_code="CohortCode1",
211+
description="CohortDescription1",
212+
audit_rules=audit_rules,
213+
reasons=audit_rules,
214+
)
215+
iteration_result = IterationResult(status=Status.actionable, cohort_results=[cohort_group_result], actions=[])
216+
campaign_details = (CampaignID("CampaignID1"), CampaignVersion(123))
217+
218+
best_iteration_results = BestIterationResult(
219+
iteration_result,
220+
iteration,
221+
campaign_details[0],
222+
campaign_details[1],
223+
{CohortLabel("CohortCode1"): cohort_group_result},
224+
)
225+
226+
with app.app_context():
227+
g.audit_log = AuditEvent()
228+
229+
AuditContext.append_audit_condition(
230+
condition_name, best_iteration_results, MatchedActionDetail(), [cohort_group_result]
231+
)
232+
233+
assert g.audit_log.response.condition, condition_name
234+
cond = g.audit_log.response.condition[0]
235+
assert cond.status == "actionable"
236+
assert cond.status_text == "You should have the Condition1 vaccine"
237+
assert cond.actions is None
238+
assert cond.action_rule is None
239+
assert cond.suitability_rules[0].rule_priority == "1"
240+
assert cond.suitability_rules[0].rule_name == "SuppressionRuleName1"
241+
assert cond.suitability_rules[0].rule_message == "SuppressionRuleDescription1"
160242
assert cond.filter_rules is None
161243
assert cond.eligibility_cohorts[0].cohort_code == "CohortCode1"
162244
assert cond.eligibility_cohorts[0].cohort_status == "actionable"
@@ -165,6 +247,62 @@ def test_append_audit_condition_adds_condition_to_audit_log_on_g(app):
165247
assert cond.eligibility_cohort_groups[0].cohort_text == "CohortDescription1"
166248

167249

250+
def test_should_append_audit_filter_rules_for_not_actionable_status(app):
251+
condition_name: ConditionName
252+
campaign_details: tuple[CampaignID | None, CampaignVersion | None]
253+
254+
condition_name = ConditionName("Condition1")
255+
iteration = IterationFactory.build()
256+
audit_rules = [
257+
Reason(
258+
rule_type=RuleType.filter,
259+
rule_name=RuleName("FilterRuleName1"),
260+
rule_description=RuleDescription("FilterRuleDescription1"),
261+
matcher_matched=True,
262+
rule_priority=RulePriority("1"),
263+
)
264+
]
265+
cohort_group_result = CohortGroupResult(
266+
status=Status.not_actionable,
267+
cohort_code="CohortCode1",
268+
description="CohortDescription1",
269+
audit_rules=audit_rules,
270+
reasons=audit_rules,
271+
)
272+
iteration_result = IterationResult(status=Status.not_actionable, cohort_results=[cohort_group_result], actions=[])
273+
campaign_details = (CampaignID("CampaignID1"), CampaignVersion(123))
274+
275+
best_iteration_results = BestIterationResult(
276+
iteration_result,
277+
iteration,
278+
campaign_details[0],
279+
campaign_details[1],
280+
{CohortLabel("CohortCode1"): cohort_group_result},
281+
)
282+
283+
with app.app_context():
284+
g.audit_log = AuditEvent()
285+
286+
AuditContext.append_audit_condition(
287+
condition_name, best_iteration_results, MatchedActionDetail(), [cohort_group_result]
288+
)
289+
290+
assert g.audit_log.response.condition, condition_name
291+
cond = g.audit_log.response.condition[0]
292+
assert cond.status == "not_actionable"
293+
assert cond.status_text == "You should have the Condition1 vaccine"
294+
assert cond.actions is None
295+
assert cond.action_rule is None
296+
assert cond.filter_rules[0].rule_priority == "1"
297+
assert cond.filter_rules[0].rule_name == "FilterRuleName1"
298+
assert cond.suitability_rules is None
299+
assert cond.eligibility_cohorts[0].cohort_code == "CohortCode1"
300+
assert cond.eligibility_cohorts[0].cohort_status == "not_actionable"
301+
assert cond.eligibility_cohort_groups[0].cohort_code == "CohortCode1"
302+
assert cond.eligibility_cohort_groups[0].cohort_status == "not_actionable"
303+
assert cond.eligibility_cohort_groups[0].cohort_text == "CohortDescription1"
304+
305+
168306
def test_add_response_details_adds_to_audit_log_on_g(app):
169307
response_id = uuid.uuid4()
170308
last_updated = datetime(2023, 1, 1, 0, 0, tzinfo=UTC)
@@ -207,7 +345,7 @@ def test_no_duplicates_returns_same_list():
207345
def test_duplicates_are_removed():
208346
reasons = [
209347
Reason(RuleType("F"), RuleName("code1"), RulePriority("1"), RuleDescription("desc1"), matcher_matched=True),
210-
Reason(RuleType("S"), RuleName("code1"), RulePriority("2"), RuleDescription("desc2"), matcher_matched=False),
348+
Reason(RuleType("F"), RuleName("code2"), RulePriority("1"), RuleDescription("desc2"), matcher_matched=False),
211349
Reason(RuleType("R"), RuleName("code3"), RulePriority("3"), RuleDescription("desc3"), matcher_matched=True),
212350
]
213351
expected = [
@@ -221,16 +359,3 @@ def test_empty_list_returns_empty_list():
221359
reasons = []
222360
expected = []
223361
assert AuditContext.deduplicate_reasons(reasons) == expected
224-
225-
226-
def test_reasons_with_no_description_are_filtered_out():
227-
reasons = [
228-
Reason(RuleType("F"), RuleName("code1"), RulePriority("1"), RuleDescription("desc1"), matcher_matched=True),
229-
Reason(RuleType("S"), RuleName("code2"), RulePriority("2"), None, matcher_matched=False),
230-
Reason(RuleType("R"), RuleName("code3"), RulePriority("3"), RuleDescription("desc3"), matcher_matched=True),
231-
]
232-
expected = [
233-
Reason(RuleType("F"), RuleName("code1"), RulePriority("1"), RuleDescription("desc1"), matcher_matched=True),
234-
Reason(RuleType("R"), RuleName("code3"), RulePriority("3"), RuleDescription("desc3"), matcher_matched=True),
235-
]
236-
assert AuditContext.deduplicate_reasons(reasons) == expected

0 commit comments

Comments
 (0)