Skip to content

Commit 2bc003e

Browse files
authored
[PRMP-1360] - Update to nrl pointer request (#488)
* [PRMP-1360] add missing fields in NRL request * [PRMP-1360] fix typo * [PRMP-1360] change to snomedcode
1 parent 8bdeea0 commit 2bc003e

File tree

8 files changed

+88
-53
lines changed

8 files changed

+88
-53
lines changed

lambdas/enums/snomed_codes.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
from enum import StrEnum
1+
from enum import Enum
22

3+
from pydantic import BaseModel
34

4-
class SnomedCodesType(StrEnum):
5-
LLOYD_GEORGE = "16521000000101"
65

6+
class SnomedCode(BaseModel):
7+
code: str
8+
display_name: str
79

8-
class SnomedCodesCategory(StrEnum):
9-
CARE_PLAN = "734163000"
10+
11+
class SnomedCodes(Enum):
12+
LLOYD_GEORGE = SnomedCode(
13+
code="16521000000101", display_name="Lloyd George record folder"
14+
)
15+
CARE_PLAN = SnomedCode(code="734163000", display_name="Care plan")
16+
GENERAL_MEDICAL_PRACTICE = SnomedCode(
17+
code="1060971000000108", display_name="General practice service"
18+
)

lambdas/models/nrl_fhir_document_reference.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Optional
22

3+
from enums.snomed_codes import SnomedCode, SnomedCodes
34
from fhir.resources.R4B.documentreference import DocumentReference
45
from models.nrl_sqs_message import NrlAttachment
56
from pydantic import BaseModel, ConfigDict
@@ -10,9 +11,11 @@ class FhirDocumentReference(BaseModel):
1011
model_config = ConfigDict(alias_generator=to_camel)
1112
nhs_number: str
1213
custodian: str
13-
snomed_code_doc_type: str = "None"
14-
snomed_code_category: str = "None"
15-
snomed_code_category_display: str = "Care plan"
14+
snomed_code_doc_type: SnomedCode = SnomedCodes.LLOYD_GEORGE.value
15+
snomed_code_category: SnomedCode = SnomedCodes.CARE_PLAN.value
16+
snomed_code_practice_setting: SnomedCode = (
17+
SnomedCodes.GENERAL_MEDICAL_PRACTICE.value
18+
)
1619
attachment: Optional[NrlAttachment] = NrlAttachment()
1720

1821
def build_fhir_dict(self):
@@ -36,7 +39,8 @@ def build_fhir_dict(self):
3639
"coding": [
3740
{
3841
"system": snomed_url,
39-
"code": self.snomed_code_doc_type,
42+
"code": self.snomed_code_doc_type.code,
43+
"display": self.snomed_code_doc_type.display_name,
4044
}
4145
]
4246
},
@@ -45,8 +49,8 @@ def build_fhir_dict(self):
4549
"coding": [
4650
{
4751
"system": snomed_url,
48-
"code": self.snomed_code_category,
49-
"display": self.snomed_code_category_display,
52+
"code": self.snomed_code_category.code,
53+
"display": self.snomed_code_category.display_name,
5054
}
5155
]
5256
}
@@ -71,5 +75,16 @@ def build_fhir_dict(self):
7175
},
7276
}
7377
],
78+
"context": {
79+
"practiceSetting": {
80+
"coding": [
81+
{
82+
"system": snomed_url,
83+
"code": self.snomed_code_practice_setting.code,
84+
"display": self.snomed_code_practice_setting.display_name,
85+
}
86+
]
87+
}
88+
},
7489
}
7590
return DocumentReference(**structure_json)

lambdas/models/nrl_sqs_message.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Optional
22

3-
from enums.snomed_codes import SnomedCodesCategory, SnomedCodesType
3+
from enums.snomed_codes import SnomedCode, SnomedCodes
44
from pydantic import AliasGenerator, BaseModel, ConfigDict
55
from pydantic.alias_generators import to_camel
66

@@ -17,12 +17,16 @@ class NrlAttachment(BaseModel):
1717

1818
class NrlSqsMessage(BaseModel):
1919
model_config = ConfigDict(
20-
alias_generator=AliasGenerator(serialization_alias=to_camel)
20+
alias_generator=AliasGenerator(serialization_alias=to_camel),
21+
use_enum_values=True,
2122
)
2223

2324
nhs_number: str
24-
snomed_code_doc_type: str = SnomedCodesType.LLOYD_GEORGE
25-
snomed_code_category: str = SnomedCodesCategory.CARE_PLAN
25+
snomed_code_doc_type: SnomedCode = SnomedCodes.LLOYD_GEORGE.value
26+
snomed_code_category: SnomedCode = SnomedCodes.CARE_PLAN.value
27+
snomed_code_practice_setting: SnomedCode = (
28+
SnomedCodes.GENERAL_MEDICAL_PRACTICE.value
29+
)
2630
description: str = ""
2731
attachment: Optional[NrlAttachment] = None
2832
action: str

lambdas/services/document_deletion_service.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from enums.lambda_error import LambdaError
77
from enums.nrl_sqs_upload import NrlActionTypes
88
from enums.s3_lifecycle_tags import S3LifecycleTags
9-
from enums.snomed_codes import SnomedCodesCategory, SnomedCodesType
9+
from enums.snomed_codes import SnomedCodes
1010
from enums.supported_document_types import SupportedDocumentTypes
1111
from models.document_reference import DocumentReference
1212
from models.nrl_sqs_message import NrlSqsMessage
@@ -91,13 +91,13 @@ def send_sqs_message_to_remove_pointer(self, nhs_number: str):
9191
delete_nrl_message = NrlSqsMessage(
9292
nhs_number=nhs_number,
9393
action=NrlActionTypes.DELETE,
94-
snomed_code_doc_type=SnomedCodesType.LLOYD_GEORGE,
95-
snomed_code_category=SnomedCodesCategory.CARE_PLAN,
94+
snomed_code_doc_type=SnomedCodes.LLOYD_GEORGE.value,
95+
snomed_code_category=SnomedCodes.CARE_PLAN.value,
9696
)
9797
sqs_group_id = f"NRL_delete_{uuid.uuid4()}"
9898
nrl_queue_url = os.environ["NRL_SQS_QUEUE_URL"]
9999
self.sqs_service.send_message_fifo(
100100
queue_url=nrl_queue_url,
101-
message_body=delete_nrl_message.model_dump_json(),
101+
message_body=delete_nrl_message.model_dump_json(exclude_unset=True),
102102
group_id=sqs_group_id,
103103
)

lambdas/services/nrl_api_service.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
import uuid
33

44
import requests
5-
from requests import HTTPError
5+
from enums.snomed_codes import SnomedCode
66
from requests.adapters import HTTPAdapter
7+
from requests.exceptions import ConnectionError, HTTPError, Timeout
78
from urllib3 import Retry
89
from utils.audit_logging_setup import LoggingService
910
from utils.exceptions import NrlApiException
@@ -46,7 +47,7 @@ def create_new_pointer(self, body, retry_on_expired: bool = True):
4647
)
4748
response.raise_for_status()
4849
logger.info("Successfully created new pointer")
49-
except HTTPError as e:
50+
except (ConnectionError, Timeout, HTTPError) as e:
5051
logger.error(e.response.content)
5152
if e.response.status_code == 401 and retry_on_expired:
5253
self.headers["Authorization"] = (
@@ -56,14 +57,16 @@ def create_new_pointer(self, body, retry_on_expired: bool = True):
5657
else:
5758
raise NrlApiException("Error while creating new NRL Pointer")
5859

59-
def get_pointer(self, nhs_number, record_type=None, retry_on_expired: bool = True):
60+
def get_pointer(
61+
self, nhs_number, record_type: SnomedCode = None, retry_on_expired: bool = True
62+
):
6063
try:
6164
self.set_x_request_id()
6265
params = {
6366
"subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{nhs_number}"
6467
}
6568
if record_type:
66-
params["type"] = f"http://snomed.info/sct|{record_type}"
69+
params["type"] = f"http://snomed.info/sct|{record_type.code}"
6770
response = self.session.get(
6871
url=self.endpoint, params=params, headers=self.headers
6972
)
@@ -79,7 +82,7 @@ def get_pointer(self, nhs_number, record_type=None, retry_on_expired: bool = Tru
7982
else:
8083
raise NrlApiException("Error while getting NRL Pointer")
8184

82-
def delete_pointer(self, nhs_number, record_type):
85+
def delete_pointer(self, nhs_number, record_type: SnomedCode = None):
8386
search_results = self.get_pointer(nhs_number, record_type).get("entry", [])
8487
for entry in search_results:
8588
self.set_x_request_id()

lambdas/tests/unit/handlers/test_manage_nrl_pointer_handler.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import json
2-
31
import pytest
2+
from enums.snomed_codes import SnomedCodes
43
from handlers.manage_nrl_pointer_handler import lambda_handler
4+
from models.nrl_sqs_message import NrlAttachment, NrlSqsMessage
55
from utils.exceptions import NrlApiException
66

77

@@ -14,18 +14,18 @@ def mock_service(mocker):
1414

1515

1616
def build_test_sqs_message(action="create"):
17-
SQS_Message = {
18-
"nhs_number": "123456789",
19-
"snomed_code_doc_type": "16521000000101",
20-
"snomed_code_category": "734163000",
21-
"action": action,
22-
"attachment": {
23-
"contentType": "application/pdf",
24-
"url": "https://example.org/my-doc.pdf",
25-
},
26-
}
17+
doc_details = NrlAttachment(
18+
url="https://example.org/my-doc.pdf",
19+
)
20+
sqs_message = NrlSqsMessage(
21+
nhs_number="123456789",
22+
action=action,
23+
snomed_code_doc_type=SnomedCodes.LLOYD_GEORGE.value,
24+
snomed_code_category=SnomedCodes.CARE_PLAN.value,
25+
attachment=doc_details,
26+
).model_dump_json()
2727
return {
28-
"body": json.dumps(SQS_Message),
28+
"body": sqs_message,
2929
"eventSource": "aws:sqs",
3030
}
3131

lambdas/tests/unit/services/test_document_deletion_service.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import pytest
44
from enums.s3_lifecycle_tags import S3LifecycleTags
5+
from enums.snomed_codes import SnomedCodes
56
from enums.supported_document_types import SupportedDocumentTypes
67
from services.document_deletion_service import DocumentDeletionService
78
from tests.unit.conftest import (
@@ -227,12 +228,14 @@ def test_send_sqs_message_to_remove_pointer(mocker, mock_deletion_service):
227228

228229
expected_message_body = (
229230
'{{"nhs_number":"{}",'
230-
'"snomed_code_doc_type":"16521000000101",'
231-
'"snomed_code_category":"734163000",'
232-
'"description":"",'
233-
'"attachment":null,'
231+
'"snomed_code_doc_type":{},'
232+
'"snomed_code_category":{},'
234233
'"action":"delete"}}'
235-
).format(TEST_NHS_NUMBER)
234+
).format(
235+
TEST_NHS_NUMBER,
236+
SnomedCodes.LLOYD_GEORGE.value.model_dump_json(),
237+
SnomedCodes.CARE_PLAN.value.model_dump_json(),
238+
)
236239

237240
mock_deletion_service.send_sqs_message_to_remove_pointer(TEST_NHS_NUMBER)
238241

lambdas/tests/unit/services/test_nrl_api_service.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pytest
2+
from enums.snomed_codes import SnomedCodes
23
from requests import Response
34
from services.nrl_api_service import NrlApiService
45
from tests.unit.conftest import FAKE_URL, TEST_NHS_NUMBER
@@ -43,12 +44,12 @@ def test_get_end_user_ods_code(nrl_service):
4344

4445

4546
def test_get_pointer_with_record_type(mocker, nrl_service):
46-
mock_type = 11111111
47+
mock_type = SnomedCodes.LLOYD_GEORGE.value
4748
mocker.patch("uuid.uuid4", return_value="test_uuid")
4849

4950
mock_params = {
5051
"subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{TEST_NHS_NUMBER}",
51-
"type": f"http://snomed.info/sct|{mock_type}",
52+
"type": f"http://snomed.info/sct|{mock_type.code}",
5253
}
5354
mock_headers = {
5455
"Authorization": f"Bearer {ACCESS_TOKEN}",
@@ -64,11 +65,11 @@ def test_get_pointer_with_record_type(mocker, nrl_service):
6465

6566

6667
def test_get_pointer_with_record_type_no_retry(mocker, nrl_service):
67-
mock_type = 11111111
68+
mock_type = SnomedCodes.LLOYD_GEORGE.value
6869
mocker.patch("uuid.uuid4", return_value="test_uuid")
6970
mock_params = {
7071
"subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{TEST_NHS_NUMBER}",
71-
"type": f"http://snomed.info/sct|{mock_type}",
72+
"type": f"http://snomed.info/sct|{mock_type.code}",
7273
}
7374
mock_headers = {
7475
"Authorization": f"Bearer {ACCESS_TOKEN}",
@@ -90,11 +91,11 @@ def test_get_pointer_with_record_type_no_retry(mocker, nrl_service):
9091

9192

9293
def test_get_pointer_with_record_type_with_retry(mocker, nrl_service):
93-
mock_type = 11111111
94+
mock_type = SnomedCodes.LLOYD_GEORGE.value
9495
mocker.patch("uuid.uuid4", return_value="test_uuid")
9596
mock_params = {
9697
"subject:identifier": f"https://fhir.nhs.uk/Id/nhs-number|{TEST_NHS_NUMBER}",
97-
"type": f"http://snomed.info/sct|{mock_type}",
98+
"type": f"http://snomed.info/sct|{mock_type.code}",
9899
}
99100
mock_headers = {
100101
"Authorization": f"Bearer {ACCESS_TOKEN}",
@@ -120,7 +121,7 @@ def test_get_pointer_raise_error(nrl_service):
120121
response.status_code = 400
121122
response._content = b"{}"
122123

123-
mock_type = 11111111
124+
mock_type = SnomedCodes.LLOYD_GEORGE.value
124125

125126
nrl_service.session.get.return_value = response
126127
pytest.raises(NrlApiException, nrl_service.get_pointer, TEST_NHS_NUMBER, mock_type)
@@ -129,7 +130,7 @@ def test_get_pointer_raise_error(nrl_service):
129130

130131

131132
def test_delete_pointer_with_record_type_no_record(mocker, nrl_service):
132-
mock_type = 11111111
133+
mock_type = SnomedCodes.LLOYD_GEORGE.value
133134
mocker.patch("uuid.uuid4", return_value="test_uuid")
134135

135136
nrl_response = {
@@ -145,7 +146,7 @@ def test_delete_pointer_with_record_type_no_record(mocker, nrl_service):
145146

146147

147148
def test_delete_pointer_with_record_type_one_record(mocker, nrl_service):
148-
mock_type = 11111111
149+
mock_type = SnomedCodes.LLOYD_GEORGE.value
149150
mocker.patch("uuid.uuid4", return_value="test_uuid")
150151
mock_pointer_id = "ODSCODE-1111bfb1-1111-2222-3333-4444555c666f"
151152
mock_headers = {
@@ -176,7 +177,7 @@ def test_delete_pointer_with_record_type_one_record(mocker, nrl_service):
176177

177178

178179
def test_delete_pointer_with_record_type_more_than_one_record(mocker, nrl_service):
179-
mock_type = 11111111
180+
mock_type = SnomedCodes.LLOYD_GEORGE.value
180181
mocker.patch("uuid.uuid4", return_value="test_uuid")
181182
mock_pointer_id = "ODSCODE-1111bfb1-1111-2222-3333-4444555c666"
182183

@@ -209,7 +210,7 @@ def test_delete_pointer_not_raise_error(mocker, nrl_service):
209210
response = Response()
210211
response.status_code = 400
211212
response._content = b"{}"
212-
mock_type = 11111111
213+
mock_type = SnomedCodes.LLOYD_GEORGE.value
213214
nrl_response = {
214215
"resourceType": "Bundle",
215216
"type": "searchset",

0 commit comments

Comments
 (0)