Skip to content

Commit dbe1fff

Browse files
NRL-1050 Add validation for types and type-category mappings
1 parent 2cbeb7f commit dbe1fff

File tree

2 files changed

+122
-7
lines changed

2 files changed

+122
-7
lines changed

layer/nrlf/core/constants.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,51 @@ def coding_value(self):
105105
},
106106
}
107107

108+
TYPE_ATTRIBUTES = {
109+
PointerTypes.MENTAL_HEALTH_PLAN.value: {
110+
"display": "Mental health crisis plan",
111+
},
112+
PointerTypes.EMERGENCY_HEALTHCARE_PLAN.value: {
113+
"display": "Emergency health care plan",
114+
},
115+
PointerTypes.EOL_COORDINATION_SUMMARY.value: {
116+
"display": "End of Life Care Coordination Summary",
117+
},
118+
PointerTypes.RESPECT_FORM.value: {
119+
"display": "ReSPECT (Recommended Summary Plan for Emergency Care and Treatment) form",
120+
},
121+
PointerTypes.NEWS2_CHART.value: {
122+
"display": "Royal College of Physicians NEWS2 (National Early Warning Score 2) chart",
123+
},
124+
PointerTypes.CONTINGENCY_PLAN.value: {
125+
"display": "Contingency plan",
126+
},
127+
PointerTypes.EOL_CARE_PLAN.value: {
128+
"display": "End of life care plan",
129+
},
130+
PointerTypes.LLOYD_GEORGE_FOLDER.value: {
131+
"display": "Lloyd George record folder",
132+
},
133+
PointerTypes.ADVANCED_CARE_PLAN.value: {
134+
"display": "Advanced care plan",
135+
},
136+
PointerTypes.TREATMENT_ESCALATION_PLAN.value: {
137+
"display": "Treatment escalation plan",
138+
},
139+
PointerTypes.SUMMARY_RECORD.value: {
140+
"display": "Summary record",
141+
},
142+
PointerTypes.PERSONALISED_CARE_AND_SUPPORT_PLAN.value: {
143+
"display": "Personalised Care and Support Plan",
144+
},
145+
PointerTypes.MRA_UPPER_LIMB_ARTERY.value: {
146+
"display": "MRA Upper Limb Artery",
147+
},
148+
PointerTypes.MRI_AXILLA_BOTH.value: {
149+
"display": "MRI Axilla Both",
150+
},
151+
}
152+
108153
TYPE_CATEGORIES = {
109154
#
110155
# Care plans

layer/nrlf/core/validators.py

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

77
from nrlf.core.codes import SpineErrorConcept
8-
from nrlf.core.constants import CATEGORY_ATTRIBUTES, REQUIRED_CREATE_FIELDS
8+
from nrlf.core.constants import (
9+
CATEGORY_ATTRIBUTES,
10+
REQUIRED_CREATE_FIELDS,
11+
TYPE_ATTRIBUTES,
12+
TYPE_CATEGORIES,
13+
)
914
from nrlf.core.errors import ParseError
1015
from nrlf.core.logger import LogReference, logger
1116
from nrlf.core.types import DocumentReference, OperationOutcomeIssue, RequestQueryType
@@ -117,7 +122,9 @@ def validate(self, data: Dict[str, Any] | DocumentReference):
117122
self._validate_identifiers(resource)
118123
self._validate_relates_to(resource)
119124
self._validate_ssp_asid(resource)
125+
self._validate_type(resource)
120126
self._validate_category(resource)
127+
self._validate_type_category_mapping(resource)
121128
if resource.content[0].extension:
122129
self._validate_content_extension(resource)
123130

@@ -336,6 +343,50 @@ def _validate_ssp_asid(self, model: DocumentReference):
336343
)
337344
return
338345

346+
def _validate_type(self, model: DocumentReference):
347+
"""
348+
Validate the type field contains an appropriate coding system, code and display.
349+
"""
350+
logger.log(LogReference.VALIDATOR001, step="type")
351+
352+
if len(model.type.coding) > 1:
353+
self.result.add_error(
354+
issue_code="invalid",
355+
error_code="INVALID_RESOURCE",
356+
diagnostics=f"Invalid type coding length: {len(model.type[0].coding)} Type Coding must only contain a single value",
357+
field=f"type.coding",
358+
)
359+
return
360+
361+
coding = model.type.coding[0]
362+
if coding.system not in ["http://snomed.info/sct", "https://nicip.nhs.uk"]:
363+
self.result.add_error(
364+
issue_code="value",
365+
error_code="INVALID_RESOURCE",
366+
diagnostics=f"Invalid type system: {coding.system} Type system must be either 'http://snomed.info/sct' or 'https://nicip.nhs.uk'",
367+
field="type.coding[0].system",
368+
)
369+
return
370+
371+
type_id = f"{coding.system}|{coding.code}"
372+
if type_id not in TYPE_ATTRIBUTES.keys():
373+
self.result.add_error(
374+
issue_code="value",
375+
error_code="INVALID_RESOURCE",
376+
diagnostics=f"Invalid type code: {coding.code} Type must be a member of the England-NRLRecordCategory value set (https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType)",
377+
field="type.coding[0].code",
378+
)
379+
return
380+
381+
type_attributes = TYPE_ATTRIBUTES.get(type_id, {})
382+
if coding.display != type_attributes.get("display"):
383+
self.result.add_error(
384+
issue_code="value",
385+
error_code="INVALID_RESOURCE",
386+
diagnostics=f"type code '{coding.code}' must have a display value of '{type_attributes.get('display')}'",
387+
field="type.coding[0].display",
388+
)
389+
339390
def _validate_category(self, model: DocumentReference):
340391
"""
341392
Validate the category field contains an appropriate coding system, code and display.
@@ -363,22 +414,22 @@ def _validate_category(self, model: DocumentReference):
363414
return
364415

365416
coding = model.category[0].coding[0]
366-
if coding.system != "http://snomed.info/sct":
417+
if coding.system not in ["http://snomed.info/sct", "https://nicip.nhs.uk"]:
367418
self.result.add_error(
368419
issue_code="value",
369420
error_code="INVALID_RESOURCE",
370-
diagnostics=f"Invalid category system: {coding.system} Category system must be 'http://snomed.info/sct'",
371-
field=f"category[0].coding[{0}].system",
421+
diagnostics=f"Invalid category system: {coding.system} Category system must be either 'http://snomed.info/sct' or 'https://nicip.nhs.uk'",
422+
field="category[0].coding[0].system",
372423
)
373424
return
374425

375-
category_id = f"http://snomed.info/sct|{coding.code}"
426+
category_id = f"{coding.system}|{coding.code}"
376427
if category_id not in CATEGORY_ATTRIBUTES.keys():
377428
self.result.add_error(
378429
issue_code="value",
379430
error_code="INVALID_RESOURCE",
380431
diagnostics=f"Invalid category code: {coding.code} Category must be a member of the England-NRLRecordCategory value set (https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordCategory)",
381-
field=f"category[0].coding[{0}].code",
432+
field="category[0].coding[0].code",
382433
)
383434
return
384435

@@ -388,7 +439,26 @@ def _validate_category(self, model: DocumentReference):
388439
issue_code="value",
389440
error_code="INVALID_RESOURCE",
390441
diagnostics=f"category code '{coding.code}' must have a display value of '{category_attributes.get('display')}'",
391-
field=f"category[0].coding[{0}].display",
442+
field="category[0].coding[0].display",
443+
)
444+
445+
def _validate_type_category_mapping(self, model: DocumentReference):
446+
"""
447+
Validate the type field contains an appropriate coding system, code and display.
448+
"""
449+
logger.log(LogReference.VALIDATOR001, step="type_category_mapping")
450+
451+
type_coding = model.type.coding[0]
452+
type_id = f"{type_coding.system}|{type_coding.code}"
453+
category_coding = model.category[0].coding[0]
454+
category_id = f"{category_coding.system}|{category_coding.code}"
455+
456+
if not TYPE_CATEGORIES.get(type_id):
457+
self.result.add_error(
458+
issue_code="value",
459+
error_code="INVALID_RESOURCE",
460+
diagnostics=f"type ({type_id}) does not map to the category: {category_id}",
461+
field=f"type.coding[0].display",
392462
)
393463

394464
def _validate_content_extension(self, model: DocumentReference):

0 commit comments

Comments
 (0)