Error handling
If something goes wrong during triage of incoming measurements, it is important to inform the clinicians that they need to manually evaluate the received measurement. Depending on the type of failure, a task should be created to fix the problem.
The following types of exceptional situations have been identified during early testing of rules with clinicians.
Situation | Error | Cause | Handling |
---|---|---|---|
Library rule expects Observation or QuestionnaireResponse | Wrong input | Library linked with activity producing Observation where QuestionnaireResponse expected (or vice versa) |
|
Library for rule requires reference range, none defined. | No reference range | Could be caused by wrong configuration (by PlanDefinition editor) or because CareTeam for particular CarePlan has removed the reference range. |
|
Library for rule requires relative reference range type, only absolute reference range currently defined. | No reference range of proper type | Could be caused by wrong configuration (by PlanDefinition editor) or because CareTeam for particular CarePlan has removed the proper reference range. |
|
Library for rule requires reference ranges for components (Observation.component), none or only one defined. | No reference range for particular component (code) | Could be caused by wrong configuration (by PlanDefinition editor) or because CareTeam for particular CarePlan has removed the proper reference range. |
|
Library for rule requires reference range, wrong unit defined | Unit mismatch | Could be caused by wrong configuration (by PlanDefinition editor) or because CareTeam for particular CarePlan has updated reference range with wrong unit. |
|
Library for rule is relative and requires an active reference base to compare to. | No reference base / Reference base code mismatch | Could be caused by wrong configuration (by PlanDefinition editor) or because CareTeam for particular CarePlan has removed the reference base. |
|
Library for rule is relative and requires an active reference base to compare to. | No active reference base | Could be caused by reference base expiry. |
|
Library for rule requires reference base, wrong unit defined | Unit mismatch | Could be caused by wrong configuration (by PlanDefinition editor) or because CareTeam for particular CarePlan has updated reference base with wrong unit. |
|
Library for rule is relative and expects historic (previous) Observation within period of time | No historic Observation found | First Observation submitted or no Observation submitted in period. |
|
Library for rule expects QuestionnaireResponse with specific, required question/answer | Question/answer required by rule cannot be found | Either:
|
|
Coding error in rule/unforeseen and unhandled error situation | NullPointerException | Missing guards | Log entry |
To help handle these situtions a number of RuleExecutionExcptions has been defined and made available to the rules:
MismatchedInput
ReferenceRangeMissing
ReferenceRangeError
ReferenceBaseMissing
ReferenceBaseError
QuestionnaireResponseMissing
If the rule throws one of these exceptions, then two tasks will be created:
A task telling the clinician to perform a manual evaluation of the incoming measurement:
task-category: MeasurementForAssessmentFailureInAutoProcessing
priority: routine
description: Måling til vurdering (fejl forekommet under automatisk behandling)
A task for fixing the problem:
task-category: matching the exception
priority: routine
description: Matching the exception + any addition detailed error message provided by the rule
focus: default is the evaluated measurement, but can be overridden by the rule.
Example of error handling in a rule
function void throwMismatchedInputException(String ruleDescription, String focus) { throw new MismatchedInputException(ruleDescription, focus); } rule "COPDQuestionnaireFUTTriageRule" dialect "java" when $qrs : Collection() $listQrs : ArrayList() from collect (QuestionnaireResponse() from $qrs) $questionnaire : Questionnaire() $procedureRequest : ProcedureRequest() then if ($listQrs.isEmpty()) { throwMismatchedInputException("Manglende input. Forventede én spørgeskemabesvarelse", $procedureRequest.getId()); }
This rule expects a QuestionnaireResponse as primary input. If it is not found - for example because an observation was provided instead - then a MismatchedInputException
is thrown with a detailed error description and the procedureRequest as focus.
DefinedQuestions in rules for QuestionnaireResponses example
This is a map of questions and answers that results in red alerts. The key is the Questionnaire.item.definition url without business version. A similar map exists for yellow alerts. The rule can be expanded with more questions and answers without changing anything else.
function Map<String, List<String>> getRedTriggerMap() { Map<String, List<String>> result = new HashMap<>(); result.put("http://ehealth.sundhed.dk/DefinedQuestion/f2199b4f-f454-414f-9b27-c1ab80f5ae48", List.of("En del", "Meget")); // S5.b result.put("http://ehealth.sundhed.dk/DefinedQuestion/002efb9b-0efc-49c7-81d5-3f4542a5658f", List.of("En del", "Meget")); // S6.b result.put("http://ehealth.sundhed.dk/DefinedQuestion/b9610f7d-df61-4988-8566-b58c1d3b9b14", List.of("En del", "Meget")); // S7.b result.put("http://ehealth.sundhed.dk/DefinedQuestion/cf55d29c-eabd-4909-9824-69d4690f2332", List.of("Mørkt")); // S9.a result.put("http://ehealth.sundhed.dk/DefinedQuestion/843302ef-6623-42f1-b4e3-95fcb74e3108", List.of("En del", "Meget")); // S10.b return result; }
The business version is stripped from the url by discarding the part after |
function String getVersionlessDefinition(String definition) { if (definition.contains("|")) { return definition.substring(0,definition.indexOf("|")); } return definition; }
The url is found by using the link from the answer in the QuestionnaireResponse to lookup the corresponding question in the Questionnaire
function String getQuestionDefinition(Questionnaire questionnaire, String linkId) { for (Questionnaire.QuestionnaireItemComponent item : questionnaire.getItem()) { String definition = getQuestionDefinitionFromItem(item, linkId); if (definition != null) { return definition; } } return null; } function String getQuestionDefinitionFromItem(Questionnaire.QuestionnaireItemComponent item, String linkId) { if (linkId.equals(item.getLinkId())) { return item.getDefinition(); } for (Questionnaire.QuestionnaireItemComponent subItem : item.getItem()) { String definition = getQuestionDefinitionFromItem(subItem, linkId); if (definition != null) { return definition; } } return null; }
The rule results in a yellow ClinicalImpression finding if any yellow answers are found and a red finding if any red answers are found. If no red or yellow answers are found, then the result is a green finding.
function List<CodingDTO> getFindings(Map redAlarmAnswersMap, Map yellowAlarmAnswersMap) { List<CodingDTO> findings = new ArrayList<CodingDTO>(); if (!redAlarmAnswersMap.isEmpty()) { findings.add(new CodingDTO("http://ehealth.sundhed.dk/cs/clinicalimpression-finding-codes", "red-question-answer", "Rød spørgsmål/svar-kombination fundet i spørgeskemabesvarelse")); } if (!yellowAlarmAnswersMap.isEmpty()) { findings.add(new CodingDTO("http://ehealth.sundhed.dk/cs/clinicalimpression-finding-codes", "yellow-question-answer", "Gul spørgsmål/svar-kombination fundet i spørgeskemabesvarelse")); } if (yellowAlarmAnswersMap.isEmpty() && redAlarmAnswersMap.isEmpty()) { findings.add(new CodingDTO("http://ehealth.sundhed.dk/cs/clinicalimpression-finding-codes", "green-question-answer", "Grøn spørgsmål/svar-kombination fundet i spørgeskemabesvarelse. Når anført som opsummering for hel spørgeskemabesvarelse er der ikke fundet røde eller gule spørgsmål/svar-kombinationer.")); } return findings; }
Task priority is determined by the worst finding
function String determinePriority(Map redAlarmAnswers, Map yellowAlarmAnswers) { if (!redAlarmAnswers.isEmpty()) { return "asap"; } if (!yellowAlarmAnswers.isEmpty()) { return "urgent"; } return "routine"; }