Skip to content

Commit 966c856

Browse files
NRL-1050 Add validation for types and type-category mappings
1 parent fc29146 commit 966c856

File tree

2 files changed

+117
-7
lines changed

2 files changed

+117
-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: 72 additions & 7 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 CATEGORY_ATTRIBUTES, REQUIRED_CREATE_FIELDS
8+
from nrlf.core.constants import CATEGORY_ATTRIBUTES, REQUIRED_CREATE_FIELDS, TYPE_ATTRIBUTES, TYPE_CATEGORIES
99
from nrlf.core.errors import ParseError
1010
from nrlf.core.logger import LogReference, logger
1111
from nrlf.core.types import DocumentReference, OperationOutcomeIssue, RequestQueryType
@@ -117,7 +117,9 @@ def validate(self, data: Dict[str, Any] | DocumentReference):
117117
self._validate_identifiers(resource)
118118
self._validate_relates_to(resource)
119119
self._validate_ssp_asid(resource)
120+
self._validate_type(resource)
120121
self._validate_category(resource)
122+
self._validate_type_category_mapping(resource)
121123
if resource.content[0].extension:
122124
self._validate_content_extension(resource)
123125

@@ -336,6 +338,50 @@ def _validate_ssp_asid(self, model: DocumentReference):
336338
)
337339
return
338340

341+
def _validate_type(self, model: DocumentReference):
342+
"""
343+
Validate the type field contains an appropriate coding system, code and display.
344+
"""
345+
logger.log(LogReference.VALIDATOR001, step="type")
346+
347+
if len(model.type.coding) > 1:
348+
self.result.add_error(
349+
issue_code="invalid",
350+
error_code="INVALID_RESOURCE",
351+
diagnostics=f"Invalid type coding length: {len(model.type[0].coding)} Type Coding must only contain a single value",
352+
field=f"type.coding",
353+
)
354+
return
355+
356+
coding = model.type.coding[0]
357+
if coding.system not in ["http://snomed.info/sct", "https://nicip.nhs.uk"] :
358+
self.result.add_error(
359+
issue_code="value",
360+
error_code="INVALID_RESOURCE",
361+
diagnostics=f"Invalid type system: {coding.system} Type system must be either 'http://snomed.info/sct' or 'https://nicip.nhs.uk'",
362+
field="type.coding[0].system",
363+
)
364+
return
365+
366+
type_id = f"{coding.system}|{coding.code}"
367+
if type_id not in TYPE_ATTRIBUTES.keys():
368+
self.result.add_error(
369+
issue_code="value",
370+
error_code="INVALID_RESOURCE",
371+
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)",
372+
field="type.coding[0].code",
373+
)
374+
return
375+
376+
type_attributes = TYPE_ATTRIBUTES.get(type_id, {})
377+
if coding.display != type_attributes.get("display"):
378+
self.result.add_error(
379+
issue_code="value",
380+
error_code="INVALID_RESOURCE",
381+
diagnostics=f"type code '{coding.code}' must have a display value of '{type_attributes.get('display')}'",
382+
field="type.coding[0].display",
383+
)
384+
339385
def _validate_category(self, model: DocumentReference):
340386
"""
341387
Validate the category field contains an appropriate coding system, code and display.
@@ -363,22 +409,22 @@ def _validate_category(self, model: DocumentReference):
363409
return
364410

365411
coding = model.category[0].coding[0]
366-
if coding.system != "http://snomed.info/sct":
412+
if coding.system not in ["http://snomed.info/sct", "https://nicip.nhs.uk"] :
367413
self.result.add_error(
368414
issue_code="value",
369415
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",
416+
diagnostics=f"Invalid category system: {coding.system} Category system must be either 'http://snomed.info/sct' or 'https://nicip.nhs.uk'",
417+
field="category[0].coding[0].system",
372418
)
373419
return
374420

375-
category_id = f"http://snomed.info/sct|{coding.code}"
421+
category_id = f"{coding.system}|{coding.code}"
376422
if category_id not in CATEGORY_ATTRIBUTES.keys():
377423
self.result.add_error(
378424
issue_code="value",
379425
error_code="INVALID_RESOURCE",
380426
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",
427+
field="category[0].coding[0].code",
382428
)
383429
return
384430

@@ -388,9 +434,28 @@ def _validate_category(self, model: DocumentReference):
388434
issue_code="value",
389435
error_code="INVALID_RESOURCE",
390436
diagnostics=f"category code '{coding.code}' must have a display value of '{category_attributes.get('display')}'",
391-
field=f"category[0].coding[{0}].display",
437+
field="category[0].coding[0].display",
392438
)
393439

440+
def _validate_type_category_mapping(self, model: DocumentReference):
441+
"""
442+
Validate the type field contains an appropriate coding system, code and display.
443+
"""
444+
logger.log(LogReference.VALIDATOR001, step="type_category_mapping")
445+
446+
type_coding = model.type.coding[0]
447+
type_id = f"{type_coding.system}|{type_coding.code}"
448+
category_coding = model.category[0].coding[0]
449+
category_id = f"{category_coding.system}|{category_coding.code}"
450+
451+
if not TYPE_CATEGORIES.get(type_id):
452+
self.result.add_error(
453+
issue_code="value",
454+
error_code="INVALID_RESOURCE",
455+
diagnostics=f"type ({type_id}) does not map to the category: {category_id}",
456+
field=f"type.coding[0].display",
457+
)
458+
394459
def _validate_content_extension(self, model: DocumentReference):
395460
"""
396461
Validate the content.extension field contains an appropriate coding.

0 commit comments

Comments
 (0)