Skip to content

Commit 05f131c

Browse files
mfjarvisnhsdevws
authored andcommitted
VED-382: Use configurable disease mappings in backend.
1 parent 7735ff2 commit 05f131c

18 files changed

+250
-234
lines changed

backend/src/mappings.py

Lines changed: 0 additions & 58 deletions
This file was deleted.

backend/src/models/fhir_immunization_post_validators.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,8 @@ def validate(self):
126126
# Initialise the mandation validation functions
127127
mandation_functions = MandationFunctions(self.imms, self.vaccine_type)
128128

129-
# Identify whether to use the vaccine_type_agnostic, or else a vaccine type specific, validation rule set
130-
validation_set_to_use = (
131-
"vaccine_type_agnostic"
132-
if self.vaccine_type in ValidationSets.vaccine_types_which_use_agnostic_set
133-
else self.vaccine_type.lower()
134-
)
135-
136129
# Obtain the relevant validation set
137-
validation_set = getattr(ValidationSets, validation_set_to_use)
130+
validation_set = getattr(ValidationSets, self.vaccine_type.lower(), ValidationSets.vaccine_type_agnostic)
138131

139132
# Create an instance of FieldLocations and set dynamic fields
140133
field_locations = FieldLocations()

backend/src/models/utils/validation_utils.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55

66
from typing import Union
7-
from mappings import vaccine_type_mappings
87
from .generic_utils import create_diagnostics_error
98
from base_utils.base_utils import obtain_field_location
109
from models.obtain_field_value import ObtainFieldValue
1110
from models.field_names import FieldNames
1211
from models.errors import MandatoryError
1312
from constants import Urls
13+
from clients import redis_client
1414

1515

1616
def get_target_disease_codes(immunization: dict):
@@ -52,17 +52,14 @@ def convert_disease_codes_to_vaccine_type(disease_codes_input: list) -> Union[st
5252
Takes a list of disease codes and returns the corresponding vaccine type if found,
5353
otherwise raises a value error
5454
"""
55-
try:
56-
return next(
57-
vaccine_type
58-
for disease_codes, vaccine_type in vaccine_type_mappings
59-
if sorted(disease_codes_input) == disease_codes
60-
)
61-
except Exception as e:
55+
key = ":".join(sorted(disease_codes_input))
56+
vaccine_type = redis_client.hget("diseases_to_vacc", key)
57+
if not vaccine_type:
6258
raise ValueError(
6359
f"Validation errors: protocolApplied[0].targetDisease[*].coding[?(@.system=='http://snomed.info/sct')].code - "
6460
f"{disease_codes_input} is not a valid combination of disease codes for this service"
65-
) from e
61+
)
62+
return vaccine_type
6663

6764

6865
def get_vaccine_type(immunization: dict):

backend/src/models/validation_sets.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Validation sets for each vaccine type"""
22

33
from models.mandation_functions import MandationRules
4-
from mappings import VaccineTypes
54

65

76
class ValidationSets:
@@ -11,7 +10,7 @@ class ValidationSets:
1110
1211
TO ADD A NEW VACCINE TYPE:
1312
* If the mandation rules for the new vaccine type are identical to the vaccine_type_agnostic rules, then
14-
add the vaccine type to the vaccine_types_which_use_agnostic_set list.
13+
no action is required.
1514
* If some of the mandation rules for the new vaccine type are different than the agnostic rules, then create a
1615
new validation set, with the same name as the vaccine type. This can be done by copying and pasting the
1716
vaccine_type_agnostic set, and amending any rules as required.
@@ -21,8 +20,6 @@ class ValidationSets:
2120
def __init__(self) -> None:
2221
pass
2322

24-
vaccine_types_which_use_agnostic_set = [VaccineTypes.covid_19, VaccineTypes.flu, VaccineTypes.hpv, VaccineTypes.mmr,VaccineTypes.rsv]
25-
2623
vaccine_type_agnostic = {
2724
"patient_identifier_value": MandationRules.required,
2825
"patient_name_given": MandationRules.mandatory,

backend/src/parameter_parser.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from typing import Optional
77
from urllib.parse import parse_qs, urlencode, quote
88

9-
from mappings import VaccineTypes
9+
from clients import redis_client
1010
from models.errors import ParameterException
1111

1212
ParamValue = list[str]
@@ -107,9 +107,11 @@ def process_search_params(params: ParamContainer) -> SearchParams:
107107
vaccine_type is not None]
108108
if len(vaccine_types) < 1:
109109
raise ParameterException(f"Search parameter {immunization_target_key} must have one or more values.")
110-
if any([x not in VaccineTypes().all for x in vaccine_types]):
110+
111+
valid_vaccine_types = redis_client.hkeys("vacc_to_diseases")
112+
if any([x not in valid_vaccine_types for x in vaccine_types]):
111113
raise ParameterException(
112-
f"immunization-target must be one or more of the following: {','.join(VaccineTypes().all)}")
114+
f"immunization-target must be one or more of the following: {', '.join(valid_vaccine_types)}")
113115

114116
# date.from
115117
date_froms = params.get(date_from_key, [])

backend/tests/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
import os
22
import sys
3+
from unittest.mock import patch
4+
5+
from tests.utils.mock_redis import MockRedisClient
36

47
sys.path.append(f"{os.path.dirname(os.path.abspath(__file__))}/../src")
8+
9+
# TODO - probably shouldn't do this here. Mock in individual tests instead for clarity.
10+
# I tried setUpModule() but that wasn't called from __init__.py
11+
redis_patcher = patch("clients.redis_client", MockRedisClient())
12+
redis_patcher.start()

backend/tests/test_fhir_controller.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
IdentifierDuplicationError,
2929
)
3030
from tests.utils.immunization_utils import create_covid_19_immunization
31-
from mappings import VaccineTypes
3231
from parameter_parser import patient_identifier_system, process_search_params
3332
from tests.utils.generic_utils import load_json_data
3433
from tests.utils.values_for_tests import ValidValues
@@ -133,7 +132,7 @@ def test_get_imms_by_identifer_header_missing(self):
133132
response = self.controller.get_immunization_by_identifier(lambda_event)
134133

135134
self.assertEqual(response["statusCode"], 403)
136-
135+
137136
@patch("fhir_controller.get_supplier_permissions")
138137
def test_not_found_for_identifier(self, mock_get_permissions):
139138
"""it should return not-found OperationOutcome if it doesn't exist"""
@@ -165,7 +164,7 @@ def test_not_found_for_identifier(self, mock_get_permissions):
165164

166165
imms = identifier.replace("|", "#")
167166
# When
168-
167+
169168
response = self.controller.get_immunization_by_identifier(lambda_event)
170169

171170
# Then
@@ -403,7 +402,7 @@ def test_validate_immunization_identifier_having_whitespace(self):
403402
self.assertEqual(response["statusCode"], 400)
404403
outcome = json.loads(response["body"])
405404
self.assertEqual(outcome["resourceType"], "OperationOutcome")
406-
405+
407406
@patch("fhir_controller.get_supplier_permissions")
408407
def test_validate_imms_id_invalid_vaccinetype(self, mock_get_permissions):
409408
"""it should validate lambda's Immunization id"""
@@ -704,7 +703,7 @@ def test_validate_immunization_identifier_having_whitespace(self):
704703
self.assertEqual(response["statusCode"], 400)
705704
outcome = json.loads(response["body"])
706705
self.assertEqual(outcome["resourceType"], "OperationOutcome")
707-
706+
708707
@patch("fhir_controller.get_supplier_permissions")
709708
def test_validate_imms_id_invalid_vaccinetype(self, mock_get_permissions):
710709
"""it should validate lambda's Immunization id"""
@@ -786,7 +785,7 @@ def test_get_imms_by_id_unauthorised_vax_error(self,mock_permissions):
786785
# Then
787786
mock_permissions.assert_called_once_with("test")
788787
self.assertEqual(response["statusCode"], 403)
789-
788+
790789
@patch("fhir_controller.get_supplier_permissions")
791790
def test_get_imms_by_id_no_vax_permission(self, mock_permissions):
792791
"""it should return Immunization Id if it exists"""
@@ -1108,7 +1107,7 @@ def test_update_immunization_UnauthorizedVaxError(self, mock_get_supplier_permis
11081107
response = self.controller.update_immunization(aws_event)
11091108
mock_get_supplier_permissions.assert_called_once_with("Test")
11101109
self.assertEqual(response["statusCode"], 403)
1111-
1110+
11121111
@patch("fhir_controller.get_supplier_permissions")
11131112
def test_update_immunization_UnauthorizedVaxError_check_for_non_batch(self, mock_get_supplier_permissions):
11141113
"""it should not update the Immunization record"""
@@ -1539,7 +1538,7 @@ def test_immunization_exception_not_found(self, mock_get_permissions):
15391538
body = json.loads(response["body"])
15401539
self.assertEqual(body["resourceType"], "OperationOutcome")
15411540
self.assertEqual(body["issue"][0]["code"], "not-found")
1542-
1541+
15431542
@patch("fhir_controller.get_supplier_permissions")
15441543
def test_immunization_unhandled_error(self, mock_get_supplier_permissions):
15451544
"""it should return server-error OperationOutcome if service throws UnhandledResponseError"""
@@ -1577,12 +1576,12 @@ def setUp(self):
15771576
@patch("fhir_controller.get_supplier_permissions")
15781577
def test_get_search_immunizations(self, mock_get_supplier_permissions):
15791578
"""it should search based on patient_identifier and immunization_target"""
1580-
1579+
15811580
mock_get_supplier_permissions.return_value = ["covid19:search"]
15821581
search_result = Bundle.construct()
15831582
self.service.search_immunizations.return_value = search_result
15841583

1585-
vaccine_type = VaccineTypes().all[0]
1584+
vaccine_type = "COVID19"
15861585
params = f"{self.immunization_target_key}={vaccine_type}&" + urllib.parse.urlencode(
15871586
[(f"{self.patient_identifier_key}", f"{self.patient_identifier_valid_value}")]
15881587
)
@@ -1616,7 +1615,7 @@ def test_get_search_immunizations_vax_permission_check(self, mock_get_supplier_p
16161615
search_result = Bundle.construct()
16171616
self.service.search_immunizations.return_value = search_result
16181617

1619-
vaccine_type = VaccineTypes().all[0]
1618+
vaccine_type = "COVID19"
16201619
lambda_event = {
16211620
"SupplierSystem": "test",
16221621
"multiValueQueryStringParameters": {
@@ -1639,7 +1638,7 @@ def test_get_search_immunizations_for_unauthorized_vaccine_type_search(self, moc
16391638
bundle = Bundle.parse_obj(search_result)
16401639
self.service.search_immunizations.return_value = bundle
16411640

1642-
vaccine_type = VaccineTypes().all[0], VaccineTypes().all[1]
1641+
vaccine_type = "COVID19", "FLU"
16431642
vaccine_type = ",".join(vaccine_type)
16441643

16451644
lambda_event = {
@@ -1693,7 +1692,7 @@ def test_get_search_immunizations_for_unauthorized_vaccine_type_search_403(self,
16931692
mock_get_supplier_permissions.return_value = []
16941693
self.service.search_immunizations.return_value = bundle
16951694

1696-
vaccine_type = VaccineTypes().all[0], VaccineTypes().all[1]
1695+
vaccine_type = "COVID19", "FLU"
16971696
vaccine_type = ",".join(vaccine_type)
16981697

16991698
lambda_event = {
@@ -1718,7 +1717,7 @@ def test_get_search_immunizations_unauthorized(self, mock_get_supplier_permissio
17181717
mock_get_supplier_permissions.return_value = []
17191718
self.service.search_immunizations.return_value = search_result
17201719

1721-
vaccine_type = VaccineTypes().all[0]
1720+
vaccine_type = "COVID19"
17221721
params = f"{self.immunization_target_key}={vaccine_type}&" + urllib.parse.urlencode(
17231722
[(f"{self.patient_identifier_key}", f"{self.patient_identifier_valid_value}")]
17241723
)
@@ -1742,7 +1741,7 @@ def test_post_search_immunizations(self,mock_get_supplier_permissions):
17421741
search_result = Bundle.construct()
17431742
self.service.search_immunizations.return_value = search_result
17441743

1745-
vaccine_type = VaccineTypes().all[0]
1744+
vaccine_type = "COVID19"
17461745
params = f"{self.immunization_target_key}={vaccine_type}&" + urllib.parse.urlencode(
17471746
[(f"{self.patient_identifier_key}", f"{self.patient_identifier_valid_value}")]
17481747
)
@@ -1761,7 +1760,7 @@ def test_post_search_immunizations(self,mock_get_supplier_permissions):
17611760
"headers": {"Content-Type": "application/x-www-form-urlencoded", "SupplierSystem": "Test"},
17621761
"body": base64_encoded_body,
17631762
}
1764-
1763+
17651764
# When
17661765
response = self.controller.search_immunizations(lambda_event)
17671766
# Then
@@ -1781,7 +1780,7 @@ def test_post_search_immunizations_for_unauthorized_vaccine_type_search(self,moc
17811780
self.service.search_immunizations.return_value = bundle
17821781
mock_get_supplier_permissions.return_value = ["covid19:search"]
17831782

1784-
vaccine_type = VaccineTypes().all[0], VaccineTypes().all[1]
1783+
vaccine_type = "COVID19", "FLU"
17851784
vaccine_type = ",".join(vaccine_type)
17861785
# Construct the application/x-www-form-urlencoded body
17871786
body = {
@@ -1846,7 +1845,7 @@ def test_post_search_immunizations_for_unauthorized_vaccine_type_search_403(self
18461845
mock_get_supplier_permissions.return_value = []
18471846
self.service.search_immunizations.return_value = bundle
18481847

1849-
vaccine_type = VaccineTypes().all[0], VaccineTypes().all[1]
1848+
vaccine_type = "COVID19", "FLU"
18501849
vaccine_type = ",".join(vaccine_type)
18511850

18521851
# Construct the application/x-www-form-urlencoded body
@@ -1916,7 +1915,7 @@ def test_search_immunizations_returns_400_on_passing_superseded_nhs_number(self,
19161915
self.service.search_immunizations.return_value = search_result
19171916
mock_get_supplier_permissions.return_value = ["covid19:search"]
19181917

1919-
vaccine_type = VaccineTypes().all[0]
1918+
vaccine_type = "COVID19"
19201919
lambda_event = {
19211920
"headers": {
19221921
"Content-Type": "application/x-www-form-urlencoded",
@@ -1943,7 +1942,7 @@ def test_search_immunizations_returns_200_remove_vaccine_not_done(self, mock_get
19431942
bundle = Bundle.parse_obj(search_result)
19441943
mock_get_supplier_permissions.return_value = ["covid19:search"]
19451944
self.service.search_immunizations.return_value = bundle
1946-
vaccine_type = VaccineTypes().all[0]
1945+
vaccine_type = "COVID19"
19471946
lambda_event = {
19481947
"headers": {
19491948
"Content-Type": "application/x-www-form-urlencoded",
@@ -1967,7 +1966,7 @@ def test_search_immunizations_returns_200_remove_vaccine_not_done(self, mock_get
19671966
def test_self_link_excludes_extraneous_params(self, mock_get_supplier_permissions):
19681967
search_result = Bundle.construct()
19691968
self.service.search_immunizations.return_value = search_result
1970-
vaccine_type = VaccineTypes().all[0]
1969+
vaccine_type = "COVID19"
19711970
mock_get_supplier_permissions.return_value = ["covid19:search"]
19721971
params = f"{self.immunization_target_key}={vaccine_type}&" + urllib.parse.urlencode(
19731972
[(f"{self.patient_identifier_key}", f"{self.patient_identifier_valid_value}")]

0 commit comments

Comments
 (0)