Skip to content

Commit 86e0e9c

Browse files
eli-439 Missing token substitution when attribute null in other target record for same patient (#321)
* eli-439 WIP test for defect * Added test and extra check for vacc att type. * eli-439 formatting * eli-439 revised tests * ELI-439: Fixes sonar warnings * ELI-439: Fixes lint --------- Co-authored-by: Shweta <[email protected]>
1 parent af0fc64 commit 86e0e9c

File tree

3 files changed

+42
-8
lines changed

3 files changed

+42
-8
lines changed

src/eligibility_signposting_api/services/processors/token_parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def parse(token: str) -> ParsedToken:
6060

6161
format_str = format_match.group(1) if format_match else None
6262

63-
last_part = re.sub(r":DATE\(.*?\)", "", token_name, flags=re.IGNORECASE)
63+
last_part = re.sub(r":DATE\([^)]*\)", "", token_name, flags=re.IGNORECASE)
6464

6565
if len(token_parts) == TokenParser.MIN_TOKEN_PARTS:
6666
name = last_part.upper()

src/eligibility_signposting_api/services/processors/token_processor.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
import re
22
from dataclasses import Field, fields, is_dataclass
33
from datetime import UTC, datetime
4-
from typing import Any, Never, TypeVar
4+
from typing import Any, Never
55

66
from wireup import service
77

88
from eligibility_signposting_api.config.contants import ALLOWED_CONDITIONS
99
from eligibility_signposting_api.model.person import Person
1010
from eligibility_signposting_api.services.processors.token_parser import ParsedToken, TokenParser
1111

12-
T = TypeVar("T")
13-
14-
1512
TARGET_ATTRIBUTE_LEVEL = "TARGET"
1613
PERSON_ATTRIBUTE_LEVEL = "PERSON"
1714
ALLOWED_TARGET_ATTRIBUTES = {
@@ -30,7 +27,7 @@
3027
@service
3128
class TokenProcessor:
3229
@staticmethod
33-
def find_and_replace_tokens(person: Person, data_class: T) -> T:
30+
def find_and_replace_tokens[T](person: Person, data_class: T) -> T:
3431
if not is_dataclass(data_class):
3532
return data_class
3633
for class_field in fields(data_class):
@@ -95,8 +92,9 @@ def replace_token(text: str, person: Person) -> str:
9592
for attribute in person.data:
9693
is_person_attribute = attribute.get("ATTRIBUTE_TYPE") == PERSON_ATTRIBUTE_LEVEL
9794
is_allowed_target = parsed_token.attribute_name.upper() in ALLOWED_CONDITIONS.__args__
95+
is_correct_target = parsed_token.attribute_name.upper() == attribute.get("ATTRIBUTE_TYPE")
9896

99-
if (is_allowed_target or is_person_attribute) and key_to_find in attribute:
97+
if ((is_allowed_target and is_correct_target) or is_person_attribute) and key_to_find in attribute:
10098
found_attribute = attribute
10199
key_to_replace = key_to_find
102100
break
@@ -120,7 +118,7 @@ def handle_token_not_found(parsed_token: ParsedToken, token: str) -> Never:
120118
raise ValueError(message)
121119

122120
@staticmethod
123-
def apply_formatting(attribute: dict[str, T], attribute_value: str, date_format: str | None) -> str:
121+
def apply_formatting[T](attribute: dict[str, T], attribute_value: str, date_format: str | None) -> str:
124122
try:
125123
attribute_data = attribute.get(attribute_value)
126124
if (date_format or date_format == "") and attribute_data:

tests/unit/services/processors/test_token_processor.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,42 @@ def test_valid_token_but_missing_attribute_data_to_replace(self):
223223
assert actual.status_text == expected.status_text
224224
assert actual.condition_name == expected.condition_name
225225

226+
def test_valid_token_but_missing_attribute_in_multiple_vacc_data_to_replace(self):
227+
person = Person(
228+
[
229+
{"ATTRIBUTE_TYPE": "PERSON", "AGE": "30", "POSTCODE": None},
230+
{"ATTRIBUTE_TYPE": "RSV", "CONDITION_NAME": "RSV", "LAST_SUCCESSFUL_DATE": None},
231+
{"ATTRIBUTE_TYPE": "FAKEVACCS", "CONDITION_NAME": "FAKEVACCS", "LAST_SUCCESSFUL_DATE": None},
232+
{"ATTRIBUTE_TYPE": "COVID", "CONDITION_NAME": "COVID", "LAST_SUCCESSFUL_DATE": "20250101"},
233+
{"ATTRIBUTE_TYPE": "FLU", "CONDITION_NAME": "FLU", "LAST_SUCCESSFUL_DATE": "20260101"},
234+
]
235+
)
236+
237+
condition = Condition(
238+
condition_name=ConditionName(
239+
"You had your COVID vaccine on [[TARGET.COVID.LAST_SUCCESSFUL_DATE:DATE(%d %B %Y)]]"
240+
),
241+
status=Status.actionable,
242+
status_text=StatusText("status"),
243+
cohort_results=[],
244+
suitability_rules=[],
245+
actions=[],
246+
)
247+
248+
expected = Condition(
249+
condition_name=ConditionName("You had your COVID vaccine on 01 January 2025"),
250+
status=Status.actionable,
251+
status_text=StatusText("status"),
252+
cohort_results=[],
253+
suitability_rules=[],
254+
actions=[],
255+
)
256+
257+
actual = TokenProcessor.find_and_replace_tokens(person, condition)
258+
259+
assert actual.status_text == expected.status_text
260+
assert actual.condition_name == expected.condition_name
261+
226262
def test_simple_string_with_multiple_tokens(self):
227263
person = Person(
228264
[

0 commit comments

Comments
 (0)