Added operator evaluation for R4 enableWhen
This commit is contained in:
parent
6a83a3fb59
commit
cddb6b993c
|
@ -36,8 +36,6 @@ import org.hl7.fhir.dstu3.model.Type;
|
|||
import org.hl7.fhir.dstu3.model.UriType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||
import org.hl7.fhir.instance.validation.DefaultEnableWhenEvaluator;
|
||||
import org.hl7.fhir.instance.validation.IEnableWhenEvaluator;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
|
||||
|
@ -57,7 +55,6 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
*/
|
||||
|
||||
private IWorkerContext myWorkerCtx;
|
||||
private IEnableWhenEvaluator myEnableWhenEvaluator = new DefaultEnableWhenEvaluator();
|
||||
|
||||
// this is here not to introduce enabledWhen validation unless wanted
|
||||
private boolean skipEnabledCheck = true;
|
||||
|
@ -229,13 +226,15 @@ public class QuestionnaireResponseValidator extends BaseValidator {
|
|||
|
||||
List<QuestionnaireResponseItemComponent> responseItems = findResponsesByLinkId(theResponseItems, linkId);
|
||||
if (responseItems.isEmpty()) {
|
||||
if ((skipEnabledCheck || myEnableWhenEvaluator.isQuestionEnabled(nextQuestionnaireItem, theResponseItems)) && nextQuestionnaireItem.getRequired() ) {
|
||||
if (nextQuestionnaireItem.getRequired()) {
|
||||
if ((skipEnabledCheck /*|| myEnableWhenEvaluator.isQuestionEnabled(nextQuestionnaireItem, theResponseItems)*/) && nextQuestionnaireItem.getRequired() ) {
|
||||
if (theValidateRequired) {
|
||||
rule(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required {0} with linkId[{1}]", itemType, linkId);
|
||||
} else {
|
||||
hint(theErrors, IssueType.BUSINESSRULE, thePathStack, false, "Missing required {0} with linkId[{1}]", itemType, linkId);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (responseItems.size() > 1) {
|
||||
|
|
|
@ -19,7 +19,7 @@ public class EnableWhenResult {
|
|||
* LinkId of the questionnaire item
|
||||
* @param enableWhenCondition
|
||||
* Evaluated enableWhen condition
|
||||
* @param responseItem
|
||||
* @param answerItem
|
||||
* item in QuestionnaireResponse
|
||||
*/
|
||||
public EnableWhenResult(boolean enabled, String linkId, QuestionnaireItemEnableWhenComponent enableWhenCondition,
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package org.hl7.fhir.instance.validation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent;
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
|
||||
import org.hl7.fhir.r4.elementmodel.Element;
|
||||
|
||||
public interface IEnableWhenEvaluator {
|
||||
public boolean isQuestionEnabled(org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent questionnaireItem,
|
||||
Element questionnaireResponse);
|
||||
public boolean isQuestionEnabled(QuestionnaireItemComponent item, List<QuestionnaireResponseItemComponent> theResponseItems);
|
||||
|
||||
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
package org.hl7.fhir.instance.validation;
|
||||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.*;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r4.elementmodel.Element;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.Questionnaire.*;
|
||||
|
||||
/**
|
||||
* Evaluates Questionnaire.item.enableWhen against a QuestionnaireResponse.
|
||||
* Ignores possible modifierExtensions and extensions.
|
||||
|
@ -18,42 +18,6 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
|
|||
public static final String ITEM_ELEMENT = "item";
|
||||
public static final String ANSWER_ELEMENT = "answer";
|
||||
|
||||
@Override
|
||||
public boolean isQuestionEnabled(org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent item,
|
||||
List<QuestionnaireResponseItemComponent> resp) {
|
||||
boolean enabled = true;
|
||||
if(item.hasEnableWhen()) {
|
||||
enabled = false;
|
||||
for(org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent enable : item.getEnableWhen()) {
|
||||
if(enable.getHasAnswer()) {
|
||||
// check if referenced question has answer
|
||||
String itemId = enable.getQuestion();
|
||||
for(QuestionnaireResponseItemComponent respItem : resp) {
|
||||
if(respItem.getLinkId().equalsIgnoreCase(itemId) && respItem.hasAnswer()) {
|
||||
//TODO check answer value
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// and if not
|
||||
String itemId = enable.getQuestion();
|
||||
for(QuestionnaireResponseItemComponent respItem : resp) {
|
||||
if(respItem.getLinkId().equalsIgnoreCase(itemId) && !respItem.hasAnswer()) {
|
||||
|
||||
//TODO check answer value
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isQuestionEnabled(QuestionnaireItemComponent questionnaireItem, Element questionnaireResponse) {
|
||||
if (!questionnaireItem.hasEnableWhen()) {
|
||||
|
@ -80,38 +44,69 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
|
|||
}
|
||||
|
||||
|
||||
public EnableWhenResult evaluateCondition(QuestionnaireItemEnableWhenComponent enableCondition,
|
||||
protected EnableWhenResult evaluateCondition(QuestionnaireItemEnableWhenComponent enableCondition,
|
||||
Element questionnaireResponse, String linkId) {
|
||||
//TODO: Fix EnableWhenResult stuff
|
||||
List<Element> answerItems = findQuestionAnswers(questionnaireResponse,
|
||||
enableCondition.getQuestion());
|
||||
if (enableCondition.hasAnswer()) {
|
||||
boolean result = answerItems.stream().anyMatch(answer -> evaluateAnswer(answer, enableCondition.getAnswer()));
|
||||
|
||||
return new EnableWhenResult(result, linkId, enableCondition, questionnaireResponse);
|
||||
|
||||
QuestionnaireItemOperator operator = enableCondition.getOperator();
|
||||
if (operator == QuestionnaireItemOperator.EXISTS){
|
||||
Type answer = enableCondition.getAnswer();
|
||||
if (!(answer instanceof BooleanType)){
|
||||
throw new RuntimeException("Exists-operator requires answerBoolean");
|
||||
}
|
||||
return new EnableWhenResult(((BooleanType)answer).booleanValue() != answerItems.isEmpty(),
|
||||
linkId, enableCondition, questionnaireResponse);
|
||||
}
|
||||
return new EnableWhenResult(false, linkId, enableCondition, questionnaireResponse);
|
||||
boolean result = answerItems
|
||||
.stream()
|
||||
.anyMatch(answer -> evaluateAnswer(answer, enableCondition.getAnswer(), enableCondition.getOperator()));
|
||||
return new EnableWhenResult(result, linkId, enableCondition, questionnaireResponse);
|
||||
}
|
||||
|
||||
private boolean evaluateAnswer(Element answer, Type expectedAnswer) {
|
||||
org.hl7.fhir.r4.model.Type actualAnswer;
|
||||
protected boolean evaluateAnswer(Element answer, Type expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
|
||||
Type actualAnswer;
|
||||
try {
|
||||
actualAnswer = answer.asType();
|
||||
} catch (FHIRException e) {
|
||||
throw new RuntimeException("Unexpected answer type", e);
|
||||
}
|
||||
if (!actualAnswer.getClass().isAssignableFrom(expectedAnswer.getClass())) {
|
||||
if (!actualAnswer.getClass().equals(expectedAnswer.getClass())) {
|
||||
throw new RuntimeException("Expected answer and actual answer have incompatible types");
|
||||
}
|
||||
if (expectedAnswer instanceof Coding) {
|
||||
return validateCodingAnswer((Coding)expectedAnswer, (Coding)actualAnswer);
|
||||
} else if (expectedAnswer instanceof PrimitiveType) {
|
||||
return actualAnswer.equalsShallow(expectedAnswer);
|
||||
return compareCodingAnswer((Coding)expectedAnswer, (Coding)actualAnswer, questionnaireItemOperator);
|
||||
} else if ((expectedAnswer instanceof PrimitiveType)) {
|
||||
return comparePrimitiveAnswer((PrimitiveType<?>)actualAnswer, (PrimitiveType<?>)expectedAnswer, questionnaireItemOperator);
|
||||
}
|
||||
// TODO: Quantity, Attachment, reference?
|
||||
throw new RuntimeException("Unimplemented answer type: " + expectedAnswer.getClass());
|
||||
}
|
||||
private boolean comparePrimitiveAnswer(PrimitiveType<?> actualAnswer, PrimitiveType<?> expectedAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
|
||||
if (actualAnswer.getValue() instanceof Comparable){
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
int result = ((Comparable)actualAnswer.getValue()).compareTo(expectedAnswer.getValue());
|
||||
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
|
||||
return result == 0;
|
||||
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
|
||||
return result != 0;
|
||||
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_OR_EQUAL){
|
||||
return result >= 0;
|
||||
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_OR_EQUAL){
|
||||
return result <= 0;
|
||||
} else if (questionnaireItemOperator == QuestionnaireItemOperator.LESS_THAN){
|
||||
return result < 0;
|
||||
} else if (questionnaireItemOperator == QuestionnaireItemOperator.GREATER_THAN){
|
||||
return result > 0;
|
||||
}
|
||||
throw new RuntimeException("Bad operator for PrimitiveType comparison");
|
||||
} else if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
|
||||
return actualAnswer.equalsShallow(expectedAnswer);
|
||||
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
|
||||
return !actualAnswer.equalsShallow(expectedAnswer);
|
||||
}
|
||||
throw new RuntimeException("Bad operator for PrimitiveType comparison");
|
||||
}
|
||||
|
||||
private List<Element> findQuestionAnswers(Element questionnaireResponse, String question) {
|
||||
List<Element> matchingItems = questionnaireResponse.getChildren(ITEM_ELEMENT)
|
||||
|
@ -132,8 +127,14 @@ public class DefaultEnableWhenEvaluator implements IEnableWhenEvaluator {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private boolean validateCodingAnswer(Coding expectedAnswer, Coding actualAnswer) {
|
||||
return compareSystems(expectedAnswer, actualAnswer) && compareCodes(expectedAnswer, actualAnswer);
|
||||
private boolean compareCodingAnswer(Coding expectedAnswer, Coding actualAnswer, QuestionnaireItemOperator questionnaireItemOperator) {
|
||||
boolean result = compareSystems(expectedAnswer, actualAnswer) && compareCodes(expectedAnswer, actualAnswer);
|
||||
if (questionnaireItemOperator == QuestionnaireItemOperator.EQUAL){
|
||||
return result == true;
|
||||
} else if (questionnaireItemOperator == QuestionnaireItemOperator.NOT_EQUAL){
|
||||
return result == false;
|
||||
}
|
||||
throw new RuntimeException("Bad operator for Coding comparison");
|
||||
}
|
||||
|
||||
private boolean compareCodes(Coding expectedCoding, Coding value) {
|
|
@ -0,0 +1,10 @@
|
|||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import org.hl7.fhir.r4.elementmodel.Element;
|
||||
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
|
||||
|
||||
public interface IEnableWhenEvaluator {
|
||||
public boolean isQuestionEnabled(QuestionnaireItemComponent questionnaireItem,
|
||||
Element questionnaireResponse);
|
||||
|
||||
}
|
|
@ -10,8 +10,6 @@ import org.hl7.fhir.exceptions.DefinitionException;
|
|||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.PathEngineException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.instance.validation.DefaultEnableWhenEvaluator;
|
||||
import org.hl7.fhir.instance.validation.IEnableWhenEvaluator;
|
||||
import org.hl7.fhir.r4.conformance.ProfileUtilities;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext.ValidationResult;
|
||||
|
@ -2779,16 +2777,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
// ok, now we have a list of known items, grouped by linkId. We"ve made an error for anything out of order
|
||||
for (QuestionnaireItemComponent qItem : qItems) {
|
||||
List<Element> mapItem = map.get(qItem.getLinkId());
|
||||
if (mapItem != null)
|
||||
if (mapItem != null){
|
||||
validateQuestionannaireResponseItem(qsrc, qItem, errors, mapItem, stack, inProgress);
|
||||
else {
|
||||
//item is missing, is the question enabled?
|
||||
if(! myEnableWhenEvaluator.isQuestionEnabled(qItem, element)) {
|
||||
|
||||
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId());
|
||||
} else {
|
||||
//item is missing, is the question enabled?
|
||||
if (!myEnableWhenEvaluator.isQuestionEnabled(qItem, element)) {
|
||||
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), !qItem.getRequired(), "No response found for required item "+qItem.getLinkId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateQuestionnaireResponseItemQuantity( List<ValidationMessage> errors, Element answer, NodeStack stack) {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package org.hl7.fhir.dstu3.hapi.validation;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemEnableWhenComponent;
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
|
||||
|
||||
public class EnableWhenResult {
|
||||
private final boolean enabled;
|
||||
private final QuestionnaireItemEnableWhenComponent enableWhenCondition;
|
||||
private final QuestionnaireResponseItemComponent responseItem;
|
||||
private final String linkId;
|
||||
|
||||
/**
|
||||
* Evaluation result of enableWhen condition
|
||||
*
|
||||
* @param enabled
|
||||
* Evaluation result
|
||||
* @param linkId
|
||||
* LinkId of the questionnaire item
|
||||
* @param enableWhenCondition
|
||||
* Evaluated enableWhen condition
|
||||
* @param responseItem
|
||||
* item in QuestionnaireResponse
|
||||
*/
|
||||
public EnableWhenResult(boolean enabled, String linkId, QuestionnaireItemEnableWhenComponent enableWhenCondition,
|
||||
QuestionnaireResponseItemComponent responseItem) {
|
||||
this.enabled = enabled;
|
||||
this.linkId = linkId;
|
||||
this.responseItem = responseItem;
|
||||
this.enableWhenCondition = enableWhenCondition;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public String getLinkId() {
|
||||
return linkId;
|
||||
}
|
||||
|
||||
public QuestionnaireResponseItemComponent getResponseItem() {
|
||||
return responseItem;
|
||||
}
|
||||
|
||||
public QuestionnaireItemEnableWhenComponent getEnableWhenCondition() {
|
||||
return enableWhenCondition;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue