Skip to content

Commit 764af14

Browse files
NRL-769 Add validation for types and type-category mappings
1 parent 25adafa commit 764af14

File tree

2 files changed

+100
-4
lines changed

2 files changed

+100
-4
lines changed

layer/nrlf/core/constants.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,45 @@ def coding_value(self):
113113
},
114114
}
115115

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

layer/nrlf/core/validators.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
CATEGORY_ATTRIBUTES,
1111
ODS_SYSTEM,
1212
REQUIRED_CREATE_FIELDS,
13+
TYPE_ATTRIBUTES,
14+
TYPE_CATEGORIES,
1315
Categories,
1416
)
1517
from nrlf.core.errors import ParseError
@@ -134,8 +136,10 @@ def validate(self, data: Dict[str, Any] | DocumentReference):
134136
self._validate_identifiers(resource)
135137
self._validate_relates_to(resource)
136138
self._validate_ssp_asid(resource)
139+
self._validate_type(resource)
137140
self._validate_category(resource)
138141
self._validate_author(resource)
142+
self._validate_type_category_mapping(resource)
139143
if resource.content[0].extension:
140144
self._validate_content_extension(resource)
141145

@@ -354,6 +358,40 @@ def _validate_ssp_asid(self, model: DocumentReference):
354358
)
355359
return
356360

361+
def _validate_type(self, model: DocumentReference):
362+
"""
363+
Validate the type field contains an appropriate coding system, code and display.
364+
"""
365+
logger.log(LogReference.VALIDATOR001, step="type")
366+
367+
if len(model.type.coding) > 1:
368+
self.result.add_error(
369+
issue_code="invalid",
370+
error_code="INVALID_RESOURCE",
371+
diagnostics=f"Invalid type coding length: {len(model.type[0].coding)} Type Coding must only contain a single value",
372+
field=f"type.coding",
373+
)
374+
return
375+
376+
coding = model.type.coding[0]
377+
if coding.system != "http://snomed.info/sct":
378+
self.result.add_error(
379+
issue_code="value",
380+
error_code="INVALID_RESOURCE",
381+
diagnostics=f"Invalid type system: {coding.system} Type system must be 'http://snomed.info/sct'",
382+
field="type.coding[0].system",
383+
)
384+
return
385+
386+
type_attributes = TYPE_ATTRIBUTES.get(type_id, {})
387+
if coding.display != type_attributes.get("display"):
388+
self.result.add_error(
389+
issue_code="value",
390+
error_code="INVALID_RESOURCE",
391+
diagnostics=f"type code '{coding.code}' must have a display value of '{type_attributes.get('display')}'",
392+
field="type.coding[0].display",
393+
)
394+
357395
def _validate_category(self, model: DocumentReference):
358396
"""
359397
Validate the category field contains an appropriate coding system, code and display.
@@ -386,17 +424,17 @@ def _validate_category(self, model: DocumentReference):
386424
issue_code="value",
387425
error_code="INVALID_RESOURCE",
388426
diagnostics=f"Invalid category system: {coding.system} Category system must be 'http://snomed.info/sct'",
389-
field=f"category[0].coding[{0}].system",
427+
field="category[0].coding[0].system",
390428
)
391429
return
392430

393-
category_id = f"http://snomed.info/sct|{coding.code}"
431+
category_id = f"{coding.system}|{coding.code}"
394432
if category_id not in CATEGORY_ATTRIBUTES.keys():
395433
self.result.add_error(
396434
issue_code="value",
397435
error_code="INVALID_RESOURCE",
398436
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)",
399-
field=f"category[0].coding[{0}].code",
437+
field="category[0].coding[0].code",
400438
)
401439
return
402440

@@ -406,7 +444,26 @@ def _validate_category(self, model: DocumentReference):
406444
issue_code="value",
407445
error_code="INVALID_RESOURCE",
408446
diagnostics=f"category code '{coding.code}' must have a display value of '{category_attributes.get('display')}'",
409-
field=f"category[0].coding[{0}].display",
447+
field="category[0].coding[0].display",
448+
)
449+
450+
def _validate_type_category_mapping(self, model: DocumentReference):
451+
"""
452+
Validate the type field contains an appropriate coding system, code and display.
453+
"""
454+
logger.log(LogReference.VALIDATOR001, step="type_category_mapping")
455+
456+
type_coding = model.type.coding[0]
457+
type_id = f"{type_coding.system}|{type_coding.code}"
458+
category_coding = model.category[0].coding[0]
459+
category_id = f"{category_coding.system}|{category_coding.code}"
460+
461+
if not TYPE_CATEGORIES.get(type_id):
462+
self.result.add_error(
463+
issue_code="value",
464+
error_code="INVALID_RESOURCE",
465+
diagnostics=f"type ({type_id}) does not map to the category: {category_id}",
466+
field=f"type.coding[0].display",
410467
)
411468

412469
def _validate_content_extension(self, model: DocumentReference):

0 commit comments

Comments
 (0)