Skip to content

Commit 41505dc

Browse files
Merge pull request #706 from NHSDigital/feature/made14-NRL-959-new-pointer-types
[NRL-959] Add summary-record and pcp pointer types
2 parents 3a5a266 + 49be31e commit 41505dc

File tree

13 files changed

+242
-3898
lines changed

13 files changed

+242
-3898
lines changed

api/consumer/record-locator/consumer.yaml

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

api/consumer/swagger.yaml

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,18 @@ info:
2828
There is a growing list of health and social care organisations authorised to share records using NRL, and presently
2929
the pointers are classified into the following types:
3030
31-
* [Mental Health Crisis Plan](http://snomed.info/sct/736253002)
31+
* [Mental health crisis plan](http://snomed.info/sct/736253002)
3232
* [Royal College of Physicians NEWS2 (National Early Warning Score 2) chart](http://snomed.info/sct/1363501000000100)
3333
* [ReSPECT (Recommended Summary Plan for Emergency Care and Treatment) form](http://snomed.info/sct/1382601000000107)
3434
* [Contingency plan](http://snomed.info/sct/325691000000100)
3535
* [End of life care plan](http://snomed.info/sct/736373009)
3636
* [End of Life Care Coordination Summary](http://snomed.info/sct/861421000000109)
37-
* [Emergency Health Care Plans](http://snomed.info/sct/887701000000100)
37+
* [Emergency health care plan](http://snomed.info/sct/887701000000100)
38+
* [Lloyd George record folder](http://snomed.info/sct/16521000000101)
39+
* [Advanced care plan](http://snomed.info/sct/736366004)
40+
* [Treatment escalation plan](http://snomed.info/sct/735324008)
41+
* [Summary record]("http://snomed.info/sct|824321000000109")
42+
* [Personalised Care and Support Plan]("http://snomed.info/sct|2181441000000107")
3843
3944
You can also retrieve booking and referal pointers however you can not currently do this by directly integrating with
4045
the National Record Locator, you must instead onboard to the [Booking and Referral - FHIR API](https://digital.nhs.uk/developer/api-catalogue/booking-and-referral-fhir)
@@ -1468,10 +1473,10 @@ components:
14681473
summary: None
14691474
value: ""
14701475
SNOMED_CODES_MENTAL_HEALTH_CRISIS_PLAN:
1471-
summary: Mental Health Crisis Plan
1476+
summary: Mental health crisis plan
14721477
value: http://snomed.info/sct|736253002
14731478
SNOMED_CODES_EMERGENCY_HEALTH_CARE_PLAN:
1474-
summary: Emergency Healthcare Plan
1479+
summary: Emergency health care plan
14751480
value: http://snomed.info/sct|887701000000100
14761481
SNOMED_CODES_END_OF_LIFE_CARE_COORDINATION_SUMMARY:
14771482
summary: End of Life Care Coordination Summary
@@ -1480,14 +1485,29 @@ components:
14801485
summary: ReSPECT form
14811486
value: http://snomed.info/sct|1382601000000107
14821487
SNOMED_CODES_NEWS2_CHART:
1483-
summary: Royal College of Physicians NEWS2
1488+
summary: Royal College of Physicians NEWS2 chart
14841489
value: http://snomed.info/sct|1363501000000100
14851490
SNOMED_CODES_CONTINGENCY_PLAN:
14861491
summary: Contingency plan
14871492
value: http://snomed.info/sct|325691000000100
14881493
SNOMED_CODES_END_OF_LIFE_CARE_PLAN:
14891494
summary: End of life care plan
14901495
value: http://snomed.info/sct|736373009
1496+
SNOMED_CODES_LLOYD_GEORGE_RECORD_FOLDER:
1497+
summary: Lloyd George record folder
1498+
value: http://snomed.info/sct|16521000000101
1499+
SNOMED_CODES_ADVANCED_CARE_PLAN:
1500+
summary: Advanced care plan
1501+
value: http://snomed.info/sct|736366004
1502+
SNOMED_CODES_TREATMENT_ESCALATION_PLAN:
1503+
summary: Treatment escalation plan
1504+
value: http://snomed.info/sct|735324008
1505+
SNOMED_CODES_SUMMARY_RECORD:
1506+
summary: Summary record
1507+
value: http://snomed.info/sct|824321000000109
1508+
SNOME_CODES_PERSONALISED_CARE_AND_SUPPORT_PLAN:
1509+
summary: Personalised Care and Support Plan
1510+
value: http://snomed.info/sct|2181441000000107
14911511
invalid:
14921512
summary: Unknown
14931513
value: http://snomed.info/sct|410970009

api/producer/record-locator/producer.yaml

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

api/producer/swagger.yaml

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,18 @@ info:
2828
There is a growing list of health and social care organisations authorised to share records using NRL, and presently
2929
the pointers are classified into the following types:
3030
31-
* [Mental Health Crisis Plan](http://snomed.info/sct/736253002)
31+
* [Mental health crisis plan](http://snomed.info/sct/736253002)
3232
* [Royal College of Physicians NEWS2 (National Early Warning Score 2) chart](http://snomed.info/sct/1363501000000100)
3333
* [ReSPECT (Recommended Summary Plan for Emergency Care and Treatment) form](http://snomed.info/sct/1382601000000107)
3434
* [Contingency plan](http://snomed.info/sct/325691000000100)
3535
* [End of life care plan](http://snomed.info/sct/736373009)
3636
* [End of Life Care Coordination Summary](http://snomed.info/sct/861421000000109)
37-
* [Emergency Health Care Plans](http://snomed.info/sct/887701000000100)
37+
* [Emergency health care plan](http://snomed.info/sct/887701000000100)
38+
* [Lloyd George record folder](http://snomed.info/sct/16521000000101)
39+
* [Advanced care plan](http://snomed.info/sct/736366004)
40+
* [Treatment escalation plan](http://snomed.info/sct/735324008)
41+
* [Summary record]("http://snomed.info/sct|824321000000109")
42+
* [Personalised Care and Support Plan]("http://snomed.info/sct|2181441000000107")
3843
3944
You can also retrieve booking and referral pointers however you can not currently do this by directly integrating with
4045
the National Record Locator, you must instead onboard to the [Booking and Referral - FHIR API](https://digital.nhs.uk/developer/api-catalogue/booking-and-referral-fhir)
@@ -1973,10 +1978,10 @@ components:
19731978
summary: None
19741979
value: ""
19751980
SNOMED_CODES_MENTAL_HEALTH_CRISIS_PLAN:
1976-
summary: Mental Health Crisis Plan
1981+
summary: Mental health crisis plan
19771982
value: http://snomed.info/sct|736253002
19781983
SNOMED_CODES_EMERGENCY_HEALTH_CARE_PLAN:
1979-
summary: Emergency Healthcare Plan
1984+
summary: Emergency health care plan
19801985
value: http://snomed.info/sct|887701000000100
19811986
SNOMED_CODES_END_OF_LIFE_CARE_COORDINATION_SUMMARY:
19821987
summary: End of Life Care Coordination Summary
@@ -1985,14 +1990,29 @@ components:
19851990
summary: ReSPECT form
19861991
value: http://snomed.info/sct|1382601000000107
19871992
SNOMED_CODES_NEWS2_CHART:
1988-
summary: Royal College of Physicians NEWS2
1993+
summary: Royal College of Physicians NEWS2 chart
19891994
value: http://snomed.info/sct|1363501000000100
19901995
SNOMED_CODES_CONTINGENCY_PLAN:
19911996
summary: Contingency plan
19921997
value: http://snomed.info/sct|325691000000100
19931998
SNOMED_CODES_END_OF_LIFE_CARE_PLAN:
19941999
summary: End of life care plan
19952000
value: http://snomed.info/sct|736373009
2001+
SNOMED_CODES_LLOYD_GEORGE_RECORD_FOLDER:
2002+
summary: Lloyd George record folder
2003+
value: http://snomed.info/sct|16521000000101
2004+
SNOMED_CODES_ADVANCED_CARE_PLAN:
2005+
summary: Advanced care plan
2006+
value: http://snomed.info/sct|736366004
2007+
SNOMED_CODES_TREATMENT_ESCALATION_PLAN:
2008+
summary: Treatment escalation plan
2009+
value: http://snomed.info/sct|735324008
2010+
SNOMED_CODES_SUMMARY_RECORD:
2011+
summary: Summary record
2012+
value: http://snomed.info/sct|824321000000109
2013+
SNOME_CODES_PERSONALISED_CARE_AND_SUPPORT_PLAN:
2014+
summary: Personalised Care and Support Plan
2015+
value: http://snomed.info/sct|2181441000000107
19962016
invalid:
19972017
summary: Unknown
19982018
value: http://snomed.info/sct|410970009

layer/nrlf/core/constants.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ class Source(Enum):
99
VALID_SOURCES = frozenset(item.value for item in Source.__members__.values())
1010
EMPTY_VALUES = ("", None, [], {})
1111
REQUIRED_CREATE_FIELDS = ["custodian", "id", "type", "status", "subject", "category"]
12-
CATEGORIES = {"734163000": "Care plan", "1102421000000108": "Observations"}
1312
JSON_TYPES = {dict, list}
1413
NHS_NUMBER_INDEX = "idx_nhs_number_by_id"
1514
ID_SEPARATOR = "-"
@@ -54,34 +53,67 @@ class PointerTypes(Enum):
5453
LLOYD_GEORGE_FOLDER = "http://snomed.info/sct|16521000000101"
5554
ADVANCED_CARE_PLAN = "http://snomed.info/sct|736366004"
5655
TREATMENT_ESCALATION_PLAN = "http://snomed.info/sct|735324008"
56+
SUMMARY_RECORD = "http://snomed.info/sct|824321000000109"
57+
PERSONALISED_CARE_AND_SUPPORT_PLAN = "http://snomed.info/sct|2181441000000107"
5758

5859
@staticmethod
5960
def list():
6061
return list(map(lambda type: type.value, PointerTypes))
6162

63+
def coding_system(self):
64+
return self.value.split(TYPE_SEPARATOR)[0]
65+
6266
def coding_value(self):
6367
return self.value.split(TYPE_SEPARATOR)[1]
6468

6569

6670
class Categories(Enum):
6771
CARE_PLAN = "http://snomed.info/sct|734163000"
6872
OBSERVATIONS = "http://snomed.info/sct|1102421000000108"
73+
CLINICAL_NOTE = "http://snomed.info/sct|823651000000106"
74+
75+
@staticmethod
76+
def list():
77+
return list(map(lambda category: category.value, Categories))
78+
79+
def coding_system(self):
80+
return self.value.split(TYPE_SEPARATOR)[0]
6981

7082
def coding_value(self):
7183
return self.value.split(TYPE_SEPARATOR)[1]
7284

7385

86+
CATEGORY_ATTRIBUTES = {
87+
Categories.CARE_PLAN.value: {
88+
"display": "Care plan",
89+
},
90+
Categories.OBSERVATIONS.value: {
91+
"display": "Observations",
92+
},
93+
Categories.CLINICAL_NOTE.value: {
94+
"display": "Clinical note",
95+
},
96+
}
97+
7498
TYPE_CATEGORIES = {
99+
#
100+
# Care plans
75101
PointerTypes.MENTAL_HEALTH_PLAN.value: Categories.CARE_PLAN.value,
76102
PointerTypes.EMERGENCY_HEALTHCARE_PLAN.value: Categories.CARE_PLAN.value,
77103
PointerTypes.EOL_COORDINATION_SUMMARY.value: Categories.CARE_PLAN.value,
78104
PointerTypes.RESPECT_FORM.value: Categories.CARE_PLAN.value,
79105
PointerTypes.CONTINGENCY_PLAN.value: Categories.CARE_PLAN.value,
80106
PointerTypes.EOL_CARE_PLAN.value: Categories.CARE_PLAN.value,
81107
PointerTypes.LLOYD_GEORGE_FOLDER.value: Categories.CARE_PLAN.value,
82-
PointerTypes.NEWS2_CHART.value: Categories.OBSERVATIONS.value,
83108
PointerTypes.ADVANCED_CARE_PLAN.value: Categories.CARE_PLAN.value,
84109
PointerTypes.TREATMENT_ESCALATION_PLAN.value: Categories.CARE_PLAN.value,
110+
PointerTypes.PERSONALISED_CARE_AND_SUPPORT_PLAN.value: Categories.CARE_PLAN.value,
111+
#
112+
# Observations
113+
PointerTypes.NEWS2_CHART.value: Categories.OBSERVATIONS.value,
114+
#
115+
# Clinical notes
116+
PointerTypes.SUMMARY_RECORD.value: Categories.CLINICAL_NOTE.value,
85117
}
86118

87119

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import pytest
2+
3+
from layer.nrlf.core.constants import (
4+
CATEGORY_ATTRIBUTES,
5+
SYSTEM_SHORT_IDS,
6+
TYPE_CATEGORIES,
7+
Categories,
8+
PointerTypes,
9+
)
10+
11+
12+
@pytest.mark.parametrize("pointer_type", PointerTypes)
13+
def test_pointer_type_has_category(pointer_type):
14+
assert (
15+
pointer_type.value in TYPE_CATEGORIES
16+
), f"Pointer type {pointer_type.value} has no category"
17+
18+
19+
@pytest.mark.parametrize("pointer_type", PointerTypes)
20+
def test_pointer_types(pointer_type):
21+
assert (
22+
pointer_type.coding_system() in SYSTEM_SHORT_IDS
23+
), f"Point type system {pointer_type.coding_system()} has no short id"
24+
25+
26+
@pytest.mark.parametrize("category", Categories)
27+
def test_pointer_category_has_types(category):
28+
assert (
29+
category.value in TYPE_CATEGORIES.values()
30+
), f"Pointer category {category.value} is not used by any type"
31+
32+
33+
@pytest.mark.parametrize("category", Categories)
34+
def test_pointer_category_has_short_code(category):
35+
assert (
36+
category.coding_system() in SYSTEM_SHORT_IDS
37+
), f"Pointer category system {category.coding_system()} has no short id"
38+
39+
40+
@pytest.mark.parametrize("type", TYPE_CATEGORIES.keys())
41+
def test_type_category_type_is_known(type):
42+
assert type in PointerTypes.list(), f"Unknown type {type} used in TYPE_CATEGORIES"
43+
44+
45+
@pytest.mark.parametrize("category", TYPE_CATEGORIES.values())
46+
def test_type_category_category_is_known(category):
47+
assert (
48+
category in Categories.list()
49+
), f"Unknown category {category} used in TYPE_CATEGORIES"
50+
51+
52+
@pytest.mark.parametrize("category", Categories)
53+
def test_category_has_attributes(category):
54+
assert (
55+
category.value in CATEGORY_ATTRIBUTES
56+
), f"Category {category.value} has no attributes"
57+
58+
59+
@pytest.mark.parametrize("category", Categories)
60+
def test_category_has_required_attributes(category):
61+
assert (
62+
category.value in CATEGORY_ATTRIBUTES
63+
), f"Category {category.value} has no attributes"
64+
assert (
65+
"display" in CATEGORY_ATTRIBUTES[category.value]
66+
), f"Category {category.value} has no display attribute"

layer/nrlf/core/validators.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pydantic import ValidationError
66

77
from nrlf.core.codes import SpineErrorConcept
8-
from nrlf.core.constants import CATEGORIES, REQUIRED_CREATE_FIELDS
8+
from nrlf.core.constants import CATEGORY_ATTRIBUTES, REQUIRED_CREATE_FIELDS
99
from nrlf.core.errors import ParseError
1010
from nrlf.core.logger import LogReference, logger
1111
from nrlf.core.types import DocumentReference, OperationOutcomeIssue, RequestQueryType
@@ -370,7 +370,8 @@ def _validate_category(self, model: DocumentReference):
370370
)
371371
return
372372

373-
if coding.code not in CATEGORIES.keys():
373+
category_id = f"http://snomed.info/sct|{coding.code}"
374+
if category_id not in CATEGORY_ATTRIBUTES.keys():
374375
self.result.add_error(
375376
issue_code="value",
376377
error_code="INVALID_RESOURCE",
@@ -379,11 +380,12 @@ def _validate_category(self, model: DocumentReference):
379380
)
380381
return
381382

382-
if coding.display != CATEGORIES.get(coding.code):
383+
category_attributes = CATEGORY_ATTRIBUTES.get(category_id, {})
384+
if coding.display != category_attributes.get("display"):
383385
self.result.add_error(
384386
issue_code="value",
385387
error_code="INVALID_RESOURCE",
386-
diagnostics=f"category code '{coding.code}' must have a display value of '{CATEGORIES.get(coding.code)}'",
388+
diagnostics=f"category code '{coding.code}' must have a display value of '{category_attributes.get('display')}'",
387389
field=f"category[0].coding[{0}].display",
388390
)
389391

scripts/get_s3_permissions.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,7 @@
66
import fire
77
from aws_session_assume import get_boto_session
88

9-
POINTER_TYPES = [
10-
"http://snomed.info/sct|736253002",
11-
"http://snomed.info/sct|1363501000000100",
12-
"http://snomed.info/sct|1382601000000107",
13-
"http://snomed.info/sct|325691000000100",
14-
"http://snomed.info/sct|736373009",
15-
"http://snomed.info/sct|861421000000109",
16-
"http://snomed.info/sct|887701000000100",
17-
"http://snomed.info/sct|736366004",
18-
"http://snomed.info/sct|735324008",
19-
]
9+
from nrlf.core.constants import PointerTypes
2010

2111

2212
def get_file_folders(s3_client, bucket_name, prefix=""):
@@ -56,7 +46,7 @@ def add_test_files(folder, file_name, local_path):
5646
file_path = Path.joinpath(folder_path, file_name)
5747
file_path.parent.mkdir(parents=True, exist_ok=True)
5848
with open(file_path, "w") as f:
59-
json.dump(POINTER_TYPES, f)
49+
json.dump(PointerTypes.list(), f)
6050

6151

6252
def download_files(s3_client, bucket_name, local_path, file_names, folders):

0 commit comments

Comments
 (0)